scripts/retrain.py is the single command for triggering a retrain. It runs locally with GPU-first and CPU fallback. Signal and position models are always trained together to prevent feature-count mismatches.
Developer/operator lane only. Regular users should run onboarding, select profile 1-5, pull approved model revisions from R2, and run trading.
Usage
# Standard retrain (cached data)
python scripts/retrain.py
# Fetch fresh data from MT5 API before training
python scripts/retrain.py --refresh
# Include SHAP analysis after training
python scripts/retrain.py --shap
# Tune with more Optuna trials (local)
python scripts/retrain.py --trials 100
# Warm-start from existing model weights (faster, experimental)
python scripts/retrain.py --warmstart
CLI flags
| Flag | Default | Description |
|---|
--refresh | false | Fetch fresh BTCUSD M15 candles from MT5 API before training |
--shap | false | Run SHAP analysis after training and write shap_summary.json |
--local | false | Deprecated compatibility flag (training is already local-only) |
--trials N | 50 | Optuna trials for local hyperparameter tuning |
--warmstart | false | Warm-start from existing weights (skips --no-warmstart) |
Backend priority
1. Local GPU -- auto-detected CUDA or AMD ROCm
2. Local CPU -- fallback, uses all cores (n_jobs=-1)
What gets trained
Both signal and position models are always trained in the same run:
models/ensemble_rf.pkl, ensemble_xgb.pkl, ensemble_lgb.pkl — signal models
models/ensemble_scaler.pkl — feature scaler (shared)
models/position_rf.pkl, position_xgb.pkl, position_lgb.pkl — position models
models/position_scaler.pkl — position feature scaler
models/model_compat.json — written after every successful unified train
Never train only the signal model or only the position model separately. They share a scaler. Training one separately produces a scaler mismatch that crashes inference.
After retraining
# 1. Verify model compat
python3 -c "
import json
mc = json.load(open('models/model_compat.json'))
ml = json.load(open('ml_config.json'))
assert mc['feature_count'] == len(ml['features']), 'MISMATCH'
print('OK --', mc['feature_count'], 'features, trained', mc.get('trained_at','?')[:10])
"
# 2. OOS backtest
python backtest_config.py \
--balance 500 --no-swap --leverage 500 \
--spread 16.95 --oos-only --no-chart
# 3. Push to R2
python ml/r2_hub.py --push
# 4. Smoke test
python trading.py --dry
Notes
--local is kept only for backward compatibility with older scripts and docs.