retraining-scheduler¶
Schedules and triggers model retraining based on drift detection, scheduled intervals, or manual requests.
Overview¶
| Property | Value |
|---|---|
| Trigger | EventBridge / Manual |
| Runtime | Python 3.11 |
| Timeout | 60 seconds |
| Memory | 256 MB |
| Settings class | RetrainingSchedulerSettings |
Input Schema¶
{
"models": [ # Optional, uses DEFAULT_MODELS if not provided
{
"name": "E2ETestStrategy",
"strategy": "E2ETestStrategy",
"freqai_model": "LightGBMRegressor",
"train_period_days": 30,
"pairs": ["BTC/USDT:USDT", "ETH/USDT:USDT"],
"timeframe": "1h"
}
],
"force": false, # Force retraining regardless of state
"trigger": "manual" # Optional: override trigger type
}
Output Schema¶
{
"success": true,
"data": {
"summary": {
"models_evaluated": 2,
"retraining_triggered": 1,
"skipped": 1,
"errors": 0
},
"results": [
{
"model_name": "E2ETestStrategy",
"status": "triggered",
"trigger": "drift_detected",
"task_arn": "arn:aws:ecs:...:task/abc123",
"retraining_triggered": true,
"timestamp": "2024-01-01T12:00:00+00:00"
},
{
"model_name": "RadStrategy",
"status": "skipped",
"reason": "recently_retrained",
"hours_since_last": 12.5,
"retraining_triggered": false,
"timestamp": "2024-01-01T12:00:00+00:00"
}
]
},
"environment": "dev"
}
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
ECS_CLUSTER | Yes | - | ECS cluster name/ARN |
ECS_SUBNETS | Yes | - | Comma-separated subnet IDs |
ECS_SECURITY_GROUPS | Yes | - | Comma-separated SG IDs |
DYNAMODB_DEPLOYMENTS_TABLE | Yes | — | Release/ACTIVE-pointer table. Required for the universe fallback: a model with no inline pairs resolves its training universe from the ACTIVE Release. Unset surfaces as ConfigurationError, not a request rejection. |
ECS_CONTAINER_NAME | No | strategy | Container name for overrides |
USE_SPOT | No | false | Use Fargate Spot instances |
RETRAINING_STATE_TABLE | Yes | - | DynamoDB state table for retraining records |
DRIFT_STATE_TABLE | Yes | - | Drift detection state table |
MIN_HOURS_BETWEEN_RETRAINING | No | 24 | Minimum cooldown between retraining |
RETRAINING_INTERVAL_DAYS | No | 7 | Scheduled retraining interval in days |
MLFLOW_TRACKING_URI | No | - | MLflow server URL (passed to ECS task as env var) |
SNS_ALERTS_TOPIC_ARN | No | - | SNS topic ARN for notifications |
Trigger Types¶
Uses RetrainingTrigger enum from tradai.common.entities.retraining:
| Trigger | Description | Priority |
|---|---|---|
drift_detected | Significant PSI drift detected in drift state table | High |
scheduled | Periodic retraining interval reached (RETRAINING_INTERVAL_DAYS) | Medium |
manual | Explicit user request (trigger: "manual" in event or force: true) | Highest |
Retraining Decision Flow¶
flowchart TD
A[Evaluate Model] --> B{Force flag?}
B -->|Yes| C[Trigger: manual]
B -->|No| D{Recently retrained?}
D -->|Yes, within MIN_HOURS| E[Skip: cooldown]
D -->|No| F{Drift detected in DynamoDB?}
F -->|Yes, is_drifted=true| G[Trigger: drift_detected]
F -->|No| H{Scheduled interval due?}
H -->|Yes, days >= RETRAINING_INTERVAL_DAYS| I[Trigger: scheduled]
H -->|No| J[Skip: no trigger]
C --> K[Launch ECS Task]
G --> K
I --> K
K --> L[Update Retraining State in DynamoDB]
L --> M{Drift triggered?}
M -->|Yes| N[Reset drift state: is_drifted=false]
M -->|No| O[Send Notification]
N --> O ECS Task Configuration¶
The Lambda launches Fargate tasks with these container environment overrides:
| Env Var | Value | Description |
|---|---|---|
TRADING_MODE | train | Matches EntrypointSettings |
RUN_CONFIG | Serialized TrainingRunConfig | The authoritative contract (#754 P2) and the sole carrier of training config. A container that cannot parse it fails at launch. |
JOB_ID | The retraining lock token | Opens the #786 job-log sink gate; stable across a retried launch |
LOG_FORMAT | json | Required for the job-log sink |
ENVIRONMENT | From settings | Deployment environment |
MLFLOW_TRACKING_URI | From settings | Only added if MLFLOW_TRACKING_URI is configured |
There is no per-field env any more. The dual-write was removed at cutover step 6 (#754 P2), once the canary proved reader-capable images were deployed. STRATEGY, MODEL_NAME, FREQAI_MODEL, TRAIN_PERIOD_DAYS, BACKTEST_PERIOD_DAYS, TRAINING_TIMEOUT_HOURS, PAIRS and TIMEFRAME are no longer emitted — the blob is the sole carrier, and a container that cannot parse it fails at launch rather than booting on defaults.
Two consequences worth knowing:
- The FreqAI model range is no longer pinned to three. While the dual-write stood, any model outside the old reader's 3-item allowlist was refused pre-lock, because emitting it would brick an image at settings construction. Every model
FreqAIModelRegistryknows —get_all_known_models(), i.e. the Freqtrade built-ins plus the TradAI ones — is launchable now._assert_known_freqai_modelstill rejects anything outside that set before the retraining lock is taken: nothing downstream validates the name (is_valid_modelreturns True on every path, by design), so an unverifiable value would otherwise fail inside the container with the lock already held. A genuinely custom model class is therefore not launchable via the scheduler — the Lambda cannot import it to check. - A stale task-def baseline is now refused. The cutover's fail-loud property was real but accidental: a pre-reader image ignores
RUN_CONFIGentirely and fails only because its_build_configraises on a missingPAIRS. Since ECS mergescontainerOverridesadditively onto the task-def baseline, a revision that still bakesPAIRSin would let that image boot on stale values and silently train the wrong universe._assert_no_legacy_ training_baselinerefuses such a revision pre-lock.STRATEGYis deliberately not in that set —build_strategy_env_varswrites it on every live/dry-run deploy. Verified 2026-08-22: none of dev's three strategy families carries any of the gated keys, so the guard is a no-op today; it exists so the property stays true when a rollback or repin brings a stale revision back. -
The combined-override guard has slack it cannot use.
_assert_override_budgetstill measures the real wire payload against ECS's 8192-char cap, butrun_config's own 5000-char blob cap now binds first — roughly 245 pairs and ~5.8k chars combined for a typical strategy name, well under 8192. (Treat those as a measurement, not an invariant: both shift with the strategy/model name lengths and the container name.) The guard is kept as defence-in-depth for env vars added later. -
Task definition: resolved via
get_strategy_task_definition(deployment_identity, environment)→tradai-strategy-{slug}-{env}. An explicittask_definitionkey on the model config overrides it. The family is pre-flighted withDescribeTaskDefinitionbefore the retraining lock is taken, so a missing family fails with an actionable message rather than an opaque ECS error — the check fails open if the IAM grant is absent. - Capacity provider:
FARGATE_SPOT(weight=1, base=0) +FARGATE(weight=0) ifUSE_SPOT=true, otherwiselaunchType=FARGATE - No command override: uses container ENTRYPOINT from image
DynamoDB State Management¶
Retraining State Table¶
Records retraining job state with 30-day TTL:
{
"model_name": "E2ETestStrategy", # Partition key
"last_retraining": "2024-01-01T12:00:00+00:00",
"task_arn": "arn:aws:ecs:...",
"trigger": "drift_detected",
"status": "running",
"expires_at": 1706745600 # Unix timestamp, 30 days TTL
}
Drift State Table¶
Checked for is_drifted flag. Reset after drift-triggered retraining:
# Reset operation
table.update_item(
Key={"model_name": model_name},
UpdateExpression="SET is_drifted = :val, reset_at = :ts",
...
)
CloudWatch Metrics¶
Namespace suffix: ModelRetraining
| Metric | Dimensions | Description |
|---|---|---|
RetrainingTriggered | Model, Environment | 1.0 if triggered, 0.0 if skipped |
RetrainingTrigger_{type} | Model, Environment | Per-trigger-type count |
EventBridge Schedule¶
{
"ScheduleExpression": "rate(6 hours)",
"Targets": [{
"Arn": "arn:aws:lambda:...:retraining-scheduler",
"Input": "{\"models\": [{\"name\": \"E2ETestStrategy\", \"strategy\": \"E2ETestStrategy\"}]}"
}]
}
SNS Notification Format¶
TradAI Model Retraining Notification
Environment: prod
Model: E2ETestStrategy
Trigger: Drift Detected
Task ARN: arn:aws:ecs:...:task/abc123
Timestamp: 2024-01-01T12:00:00+00:00
A model retraining job has been triggered. You will receive another
notification when the training completes.
Reason: Significant drift was detected in model predictions.
See Also¶
Related Lambdas:
- drift-monitor - Detects model drift (triggers retraining)
- compare-models - Champion vs challenger comparison
Architecture:
- ML Lifecycle - Full ML training pipeline
- ML Lifecycle - Pipeline diagram
Services:
- Strategy Service - Model registry integration
CLI:
- CLI Reference - A/B testing commands