The headline backtest said 74.6% win rate. The honest one said 61.1% with a 35-month underwater period. This repo is the bot that was running live in May 2026 — and the backtest that got it switched off. Published so the failure mode is documented, not buried.
Automated intraday trading bot for NASDAQ E-mini futures (NQ / MNQ) via the Tradovate API, running an ATR Breakout (ATB) strategy with a 2-contract D3 partial-exit structure.
Status: RETIRED — code preserved for reference only
- 2026-05-18: Live bot stopped after the honest 3-year backtest exposed marginal real edge.
- 2026-05-21: systemd service
nq-bot.serviceremoved and/opt/nq-futures-bot/deleted from the VPS. No instance of this code is running anywhere.- Replaced by nq-strategy-b-bot (multi-timeframe FVG confluence — 4× more profit, 4× less drawdown on the same 3-year dataset).
This repository is read-only history. Do not deploy this code. If you want to run something on NQ, fork the successor.
Initial 7-month results (Oct 2025 → May 2026, the cherry-picked recovery window) suggested:
74.6% WR · PF 2.38 · +$10,499 net
Full 3-year out-of-sample run on the same code told a different story:
Period: Jan 2023 → May 2026 (235k 5-min bars, 849 trading days)
ATB D3 — 1 MNQ baseline:
Trades: 332
Win Rate: 61.1%
Profit Factor: 1.20 ← marginal
Net P&L: +$4,153 ← ~$1,250/year
Max DD: -$3,005 ← 72% of total profit
Underwater: Jan 2023 → Nov 2025 (35 months)
ATB D3 — 2 MNQ (the live config):
Net P&L: +$8,305 Max DD: -$6,010
The whole "edge" is the last 6 months of the test window. Everything before was choppy — strategy bled slowly for 2.5 years, then rode one trending regime back to positive. That's what the 7-month cherry-pick was capturing.
ATR breakout strategies need trending markets. NQ in 2023-2024 was range-bound and choppy, producing constant whipsaws. The strategy only worked once trends established in late 2025. Cherry-picking the recovery period gave misleadingly good numbers.
Entry signal:
- 15-min Opening Range Breakout (ORB) on 5-min bars
- Bias filter from previous-day OHLC direction
- Skip Tuesdays (statistically the worst day in backtest)
- Cutoff at 12:00 ET (no new entries after midday)
- Skip days with 30-60 point gap (too volatile or too quiet)
Position structure (D3 mode):
- Enter 2 MNQ contracts on breakout candle
- Partial exit: 1 contract at 1.5× ATR profit
- Runner: 1 contract continues toward 3× ATR
- Stop: 2× ATR (both contracts; runner stop intentionally NOT moved to BE — outperforms BE version per backtest)
Risk controls:
- Max 2 trades per day
- Daily loss cap: 40 ticks
- Daily profit cap: 80 ticks
- Hard exit: 15:45 ET
See signals/atr_breakout.py for the entry signal implementation.
See main.py for the asyncio execution loop and order placement.
.
├── main.py # Async event loop, asyncio + Tradovate
├── config.py # Reads .env into typed dataclass
├── requirements.txt # Python dependencies
├── pytest.ini # Test config
│
├── signals/ # Strategy modules
│ ├── atr_breakout.py # ATB entry signal (the live strategy)
│ ├── inverse_fvg.py # Inverse FVG (experimental)
│ └── premarket.py # Monday pre-market gap-down setup
│
├── execution/ # Broker layer
│ ├── tradovate.py # Auth + market data WebSocket
│ ├── schwab_stream.py # Backup real-time data feed
│ └── yf_stream.py # Backtest data fallback
│
├── risk/
│ └── manager.py # Daily P&L caps, trade-count limits
│
├── backtest/ # Simulation infrastructure
│ ├── simulator.py
│ ├── walk_forward.py
│ └── trade_analyzer.py
│
├── analysis/ # Research scripts
│ ├── ict_fvg_backtest.py # 3 ICT strategies — Strategy A/B/C
│ ├── smc_breakout_backtest.py # SMC variants
│ ├── atb_refinement_backtest.py
│ └── ...
│
├── data/ # Historical bars (CSVs, parquet)
│ └── fetch_databento.py # 3-year NQ via Databento
│
├── deploy/ # VPS deployment scripts
│
└── run_*.py # CLI backtest runners
├── run_atb_d3_full.py # 3-year ATB D3 backtest
├── run_atb_vs_strategy_b.py # Direct comparison vs Strategy B
└── ...
- Sign up at tradovate.com (demo or live)
- API credentials: Settings → API Access
- You'll need: username, password, app_id, cid, secret
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtcp .env.example .env
# Edit .env with your Tradovate credentials
# CRITICAL: keep TRADOVATE_ENV=demo until you've validated on paper# 3-year ATB D3 backtest with full statistics
python run_atb_d3_full.py
# Side-by-side vs Strategy B (the recommended replacement)
python run_atb_vs_strategy_b.pypython main.py
# Or as systemd service — see deploy/ for VPS setupAll numbers from data/nq_databento_5min.csv (Databento GLBX.MDP3, 235k 5-min bars Jan 2023 – May 2026). 1 MNQ contract baseline.
| Strategy variant | Trades | WR | PF | Net (1 MNQ) | MaxDD |
|---|---|---|---|---|---|
| ATB D3 (this bot, 2 MNQ live) | 332 | 61.1% | 1.20 | +$8,305 (2 MNQ) | -$6,010 |
| ATB D3 — 1 MNQ baseline | 332 | 61.1% | 1.20 | +$4,153 | -$3,005 |
| ATB + 15-min FVG filter | 78 | 64.1% | 3.08 | +$7,306 | -$674 |
| 5-min IFVG inside 15-min FVG (Strategy B) | 641 | 50.2% | 1.87 | +$22,926 | -$1,251 |
| ORB + Golden Pocket | 157 | 22.3% | 1.26 | +$1,234 | -$989 |
Strategy B dominates on every metric and is the strategy this bot was replaced with. See nq-strategy-b-bot.
- Marginal real edge — PF 1.20 over 3 years is borderline tradeable
- Long drawdown periods — 35 months underwater in 2023-2024
- Concentration risk — most profit came from late-2025/early-2026 trending markets; flat or down in chop
- Better alternative exists — Strategy B from the same research delivers 2× more profit with 5× less drawdown on the same dataset
MIT
- This is NOT financial advice. Published for educational and research purposes only.
- Trading futures involves substantial risk. Past performance does not guarantee future results.
- The author has no financial relationship with Tradovate, Schwab, Databento, or any broker mentioned here.
- If you choose to run this code with real capital, do so at your own risk. Always demo trade first for at least 30 days.
