Trading loop overview
trading.py runs in a ~60-second loop. On every iteration it:
- Fetches the latest BTCUSD M15 candles from the MT5 REST API
- Engineers 59 features from the raw OHLCV data
- Scores the bar through the signal ensemble
- Applies filters and gates
- Sizes the position with the dynamic SL/TP and risk multiplier models
- Executes via the MT5 REST API
- Manages open positions through the position model
Stage 1 β Feature engineering
ml/feature_engineering.py transforms raw OHLCV candles into the 59-feature vector that all four models consume.
Feature families:
- Price structure: Bollinger Bands, ATR, range position, high/low ratios
- Trend: EMA crossovers, MACD signal, trend strength, price vs EMA200
- Momentum: RSI, RSI divergence, momentum over 5 and 10 bars
- Volatility: Normalized volatility, ATR-to-price ratio
- Volume: Volume ratio vs rolling average, volume trend
- Session/news: London, NY, Asian session flags; news proximity and risk window
- Account state: Current drawdown %, equity ratio, recent win rate, consecutive losses
Stage 2 β Signal model
ml/ensemble_predictor.py runs a majority-vote ensemble of Random Forest, XGBoost, and LightGBM classifiers.
Each model outputs a probability for BUY, SELL, or HOLD. The ensemble aggregates by majority vote, then applies a confidence threshold. Trades below confidence_threshold (default 0.633) are discarded as HOLD.
Stage 3 β Filters and circuit breakers
Before any order is placed, the signal passes through a chain of gates:| Gate | Triggers when | Action |
|---|---|---|
| ATR floor | Current ATR < min_atr | Skip β market too quiet |
| Circuit breaker | Consecutive losses β₯ max_consecutive_losses | Pause trading |
| Weekly drawdown | Weekly loss β₯ max_weekly_drawdown_pct | Pause for the week |
| Hard halt | Total equity loss β₯ hard_halt_pct | sys.exit(99) β manual restart required |
| News block | is_news_near = 1 and news_block_minutes > 0 | Skip β high-impact news window |
starting_balance_usd in config.json to your current equity.
Stage 4 β Dynamic SL/TP
ml/sltp_predictor.py uses a LightGBM regression model to predict optimal SL and TP multipliers relative to ATR.
Stage 5 β Risk multiplier
ml/risk_predictor.py runs a LightGBM model that takes 7 equity-state features and outputs a risk multiplier in [0.10, 1.25].
Features: drawdown_pct, equity_ratio, win_rate_recent, consecutive_losses, volatility_20, atr_14, hour
The multiplier scales the base risk percentage:
Stage 6 β Position sizing
Lot size is computed from the effective risk and the SL distance:pip_value = 100 USC/lot/point), risk_usd is in raw USC.
Stage 7 β Position management
After order execution, the position model (ml/position_predictor.py) evaluates every open position on each loop iteration.
Same 59-feature vector. Outputs:
- HOLD β keep the position, wait for TP or SL
- EXIT β close early (strong reversal signal)
- ADD β increase position size (strong continuation signal; currently conservative)
Weekly optimization pipeline
Every Sunday at 2 am UTC,scripts/weekly_optimize.py runs autonomously.
Key invariants:
- The sweep uses only the first 70% of the OOS window β the last 30% is a true holdout
- Models are always trained in order: Signal β Position β SLTP β Risk
- If the new score doesnβt beat baseline by β₯ 2%, everything rolls back