NinjaTrader 8 + Python ML Engine
An NT8 strategy that streams 22 order-flow and price features to a Python XGBoost engine over ZeroMQ and trades on its predictions, with live retraining and mode switching.
The problem
The client had an NT8 strategy with an unfinished ML module left by a previous developer. He wanted the model moved to Python XGBoost, and he wanted to keep his existing ZeroMQ pipeline. The strategy had to be able to switch model modes, retrain and tune from inside the NinjaTrader chart.
What I built
- Split design: NinjaScript owns market data, orders and ATM management. A Python engine owns features, models and training.
- Transport: ZeroMQ between NT8 (NetMQ) and Python (pyzmq), with separate sockets for features, commands and predictions.
- Feature stream: 22 features per bar, including spread, DOM imbalance, delta-to-depth, aggressive buys and sells, several slope angles, stochastics, ATR, time of day and a linear-regression slope.
- Feature engineering: about 25 rolling features (z-scores, momentum, log returns, volatility regime, ATR percentile), compiled with numba and with a pandas fallback. Lagged copies are built from a configurable window.
- Models:
- XGBoost classifier for direction, as a 3-state output (up, down, hold) with a probability
- XGBoost regressor for magnitude
- an ATR-normalised log-return regressor with a pseudo-Huber loss
- a multi-step regressor
- Live control protocol: NT8 sends plain commands to switch the mode (classification, regression or both), set thresholds, change retrain frequency and window sizes, start an Optuna tuning run or save the model.
- Chart panel: a draggable WPF panel added to Chart Trader. It has buttons for each model mode, optimise and flatten, and shows the selected ATM template and account.
- Tests: unit, integration, model-switching and performance suites. ZeroMQ simulators stand in for NinjaTrader, so the Python engine can be tested without NT8 running.
Details
- Online retraining every N bars. If the training window holds only one class, the engine falls back from classification to regression on its own.
- Minimum training size is derived from lag + prediction horizon + a 25-bar buffer, so the model never trains on a window too small to be valid.
- NaN-safe feature extraction on the C# side, and latency measured per prediction with a nanosecond timer.
- Fixed a configuration bug where the mode command was sent twice and the second send overwrote the user’s choice.
- Each delivery stage was frozen in its own ship folder, so the client could roll back to any stage.