topstep-backtest¶
An event-driven backtesting framework for developing futures strategies that can profitably pass the Topstep Trading Combine.
Most backtesters answer "would this have made money". That is the wrong question for a funded-account evaluation, where a strategy with a positive expectancy still fails if it draws down 2,001 dollars on the wrong afternoon, or banks 60% of its profit on one day. This one answers the question the evaluation actually asks.
What makes it different¶
Backtest/live parity. A strategy is written once against structural protocols that
both the deterministic SimBroker and the live AsyncTopstepClient from topstep-sdk
satisfy. The same class runs in a backtest or against the real gateway, unchanged.
A first-class prop-firm rule engine. The two-state trailing Maximum Loss Limit, optional Daily Loss Limit, consistency target, position caps and 16:10 ET session flatten are enforced in real time — including intrabar forced liquidation with adverse slippage — not scored after the fact. A run that would have been closed out at 11:04 is closed out at 11:04, and everything after it never happens.
Determinism you can diff. Money is exact Decimal on the tick grid, lots are FIFO,
and two runs over the same inputs produce byte-identical results. Every indicator is
TA-Lib driven bar by bar, so no formula here can drift from the reference implementation.
A report you can replay. report.show() opens a self-contained HTML tearsheet — the
tape with every fill marked, equity against the trailing MLL floor, every statistic with
its basis stated. Pass record=True and it grows a
bar-by-bar replay: step through the run and watch
every decision with the broker's answer, the working orders, the indicator values and the
running statistics as they accumulated — computed by the same code as the final report, so
they cannot disagree with it. Recording is observation only; the results are byte-identical
with it on or off.
Sessions on two switches, not one. On a 24h tape you scope each indicator's input data
(use(Atr(14), session=NEW_YORK)) independently of when the strategy may trade
(trade_sessions=(NEW_YORK,)). A continuous trend filter can sit beside a session-scoped
volatility measure — the single "trade this session" flag most backtesters offer silently
changes both, and starves the indicators it filters. Windows are defined in each region's
own timezone, so daylight saving comes from the IANA database rather than a table that goes
stale for three weeks every spring.
Install¶
pip install topstep-backtest
pip install "topstep-backtest[data]" # adds pandas + pyarrow: DataFrame and Parquet input
Python 3.12+. TA-Lib is a core dependency and ships wheels for common platforms; on others you will need the TA-Lib C library first.
A complete backtest¶
from topstep_backtest import AccountSize, Backtest, SymbolStrategy
from topstep_backtest.indicators import Cross, Sma
class SmaCross(SymbolStrategy):
def __init__(self, contract_id: str) -> None:
super().__init__(contract_id)
self.fast = self.use(Sma(10)) # TA-Lib SMA
self.slow = self.use(Sma(30))
self.cross = self.use(Cross(self.fast, self.slow)) # framework helper
async def on_bar(self, bar) -> None: # gated until every indicator is ready
if self.cross.up and self.position.flat:
await self.buy(1, stop_loss_ticks=40, take_profit_ticks=80)
elif self.cross.down and self.position.is_long:
await self.close()
print(Backtest(bars, SmaCross(MNQ), account=AccountSize.S50K).run())
Where to go next¶
-
Install, run a backtest on synthetic bars, and point it at your own data. Fifteen minutes, no prior context assumed.
-
The stages in the order the questions become answerable — data, mechanics, attempts, distribution, guards, price — and the gate each must pass before the next number means anything.
-
What every number in the output means, what basis it is on, and which ones lie to you if you read them alone — through to the tearsheet and the bar-by-bar replay. Start here if you have a result and are not sure what it says.
-
A worked strategy, line by line, from an empty file to a rule-checked verdict — with the reasoning behind each seam.
-
One backtest is one sample. Monte Carlo, walk-forward, PBO and deflated Sharpe — and what each of them can and cannot rescue.
-
The rules as implemented, each with its citation and its calibration status. Read this before trusting any verdict.
-
Generated from the live docstrings. Every metric's basis is documented on the metric.
Read this before you trust a number¶
This is an unofficial simulation and is not affiliated with Topstep. Two caveats travel with every result it produces, and both are printed on the report itself rather than buried here:
The rule and fee constants are cited configuration, not calibration. They come from published documentation, not from reconciliation against a live funded account. A verdict is a diagnostic, not an authoritative pass/fail. See the rules document for what is verified and what is not.
Tier-0 fidelity means bars, not ticks. Fills are resolved along one deliberately pessimistic intrabar path rather than a real tape. That makes intrabar figures conservative estimates, not reproductions — and it means a strategy whose edge lives inside the bar cannot be evaluated here at all. Reading the report sets out where a backtest here gives a silently wrong answer, which is the section worth reading before the ones with the appealing numbers in them.