Momentum portfolio bot
Rebalances a crypto basket every 3 days on Binance futures. The backtest drives the live code itself.
- Node.js
- ccxt
- node-cron
- Supabase
- pm2
[Add equity chart screenshot]
The problem
A backtest only means something if it behaves exactly like the code that trades. Most setups write a fast “scan” engine that imitates the live worker — and hope the two agree.
What broke — twice
First: a scan engine and the live step() matched on only 84% of entries. Different strategy, same name. Caught before going live.
Second, subtler: even with identical code, the bar model checked cancel before fill. A limit order that touched entry and then closed outside the zone counted as “no trade” — on the exchange it had already filled and lost. Switching to fill-before-cancel took expected R from +0.224 to −0.04 and max drawdown from 10% to 30%.
Rules that came out of it
- The backtest drives the real live
step()over history — no look-alike engine. - A separate fast engine must entry-match the live code at ≥98% before its numbers count.
- Model fills conservatively: price touches the level = filled.
- Entry-match a new paper worker at ~10 trades, then flip small with real money.