topstep-backtest 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/AGENTS.md +4 -3
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/CHANGELOG.md +24 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/PKG-INFO +35 -14
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/README.md +34 -13
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/docs/DESIGN.md +1 -1
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/docs/TUTORIAL_EMA_CROSSOVER.md +2 -2
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/.gitignore +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/LICENSE +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/data/sample_mnq_1m.csv +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/docs/INDICATORS.md +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/docs/ROADMAP.md +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/docs/topstep-rules.md +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/ema_cross.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/hand_wired.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/run_combine.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/run_montecarlo.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/run_real_data.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/run_tearsheet.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/run_windows.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/sma_cross.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/examples/talib_macd.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/pyproject.toml +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/_render.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/clock/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/clock/live_clock.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/clock/test_clock.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/core/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/core/ids.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/core/instruments.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/core/money.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/core/time.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/clean.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/continuous.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/feed.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/synthetic.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/validator.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/data/wrangler.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/engine/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/engine/backtest.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/rejections.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/sim_broker.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/fills/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/fills/bar_fill.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/fills/fees.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/fills/path.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/harness.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/base.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/library.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/talib_adapter.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/economics.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/montecarlo.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/overfitting.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/stats.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/walkforward.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/windows.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/protocols.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/py.typed +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/rules/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/rules/kernel.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/rules/params.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/strategy/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/strategy/base.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/strategy/symbol.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/strategy/tracker.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/_assets/lightweight-charts.LICENSE +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/_assets/lightweight-charts.standalone.production.js +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/_assets/tearsheet.css +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/_assets/tearsheet.js +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/conftest.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/artifacts/verdict_failed_mll_s50k.json +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/artifacts/verdict_passed_s50k.json +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/test_combine_kernel.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/test_facade_equivalence.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/test_sugar_equivalence.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/test_verdict_goldens.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/parity/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/parity/test_broker_conformance.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/property/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/property/test_indicator_props.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/property/test_kernel_props.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/property/test_money_props.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/__init__.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_bar_fill.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_clean.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_clock.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_continuous.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_data_feed.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_economics.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_engine.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_fees.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_harness.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_indicators.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_instruments.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_montecarlo.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_overfitting.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_path.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_sim_broker.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_stats.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_symbol_strategy.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_synthetic.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_talib_adapter_hardening.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_tearsheet.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_time.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_tracker.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_validator.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_walkforward.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_windows.py +0 -0
- {topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_wrangler.py +0 -0
|
@@ -729,7 +729,7 @@ order placement — the SDK is async and the `await`s ARE the live contract. Tra
|
|
|
729
729
|
| `self.buy(size=2, sl=…, tp=…)` | `await self.buy(2, stop_loss_ticks=40, take_profit_ticks=80)` |
|
|
730
730
|
| `stats = bt.run()` → 30-key Series | `report = bt.run()` → `Report` + `.stats`/`.result`/`.trades` |
|
|
731
731
|
| `optimize()` | `metrics.optimize(...)` — but read §5.9 first; it keeps every trial on purpose |
|
|
732
|
-
| `plot()` | `report.show()` — interactive HTML tearsheet (
|
|
732
|
+
| `plot()` | `report.show()` — interactive HTML tearsheet (`report.to_html(path)` to name the file; `bt.run_with_tearsheet(dir)` to get one from every run) |
|
|
733
733
|
|
|
734
734
|
## 8. Knobs — the calibration seam
|
|
735
735
|
|
|
@@ -816,8 +816,9 @@ breakdowns, higher fill tiers, an XFA rule set, and any holiday calendar.
|
|
|
816
816
|
**Do** write code against these — they used to be on the list above and now ship: Monte-Carlo
|
|
817
817
|
(§5.7), `optimize()` and the overfitting guards (§5.9), the sequential-Combine sweep (§5.8),
|
|
818
818
|
the continuous-contract stitcher (§5.10), and the HTML tearsheet (`report.show()` /
|
|
819
|
-
`report.to_html(path)
|
|
820
|
-
|
|
819
|
+
`report.to_html(path)` / `Backtest.run_with_tearsheet(dir)`, which writes a timestamped file
|
|
820
|
+
per run; `print(report)` remains the text render). Their signatures are in the generated
|
|
821
|
+
table in §3.
|
|
821
822
|
|
|
822
823
|
Parity status, precisely: **structural** conformance is proven — a pyright-strict protocol
|
|
823
824
|
conformance test plus a signature diff of `SimOrderApi.place` against the SDK's
|
|
@@ -6,6 +6,30 @@ below 1.0, minor releases may contain breaking changes.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.1] — 2026-08-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **The README claimed shipped subsystems do not exist.** Its status section told readers
|
|
14
|
+
there is "no walk-forward, no PBO/DSR overfitting guard, and no EV-per-attempt model" —
|
|
15
|
+
all three have shipped since 0.2.0 (`metrics/walkforward.py`, `metrics/overfitting.py`,
|
|
16
|
+
`metrics/economics.py`), and `docs/ROADMAP.md` and the site documented them correctly the
|
|
17
|
+
whole time. This is the rot the 0.2.0 doc pass fixed everywhere except the one file a new
|
|
18
|
+
reader opens first, and it was the package's PyPI project description. The bullet now keeps
|
|
19
|
+
the limitation that is still true — every analytic resamples the tape you supplied, so none
|
|
20
|
+
of them invents a regime your data never contained — without the false premise. The "Next:"
|
|
21
|
+
pointer drops the analytics that shipped and names what the roadmap actually lists as
|
|
22
|
+
remaining.
|
|
23
|
+
- **The HTML tearsheet was missing from the README and from the site's "Reading the report"
|
|
24
|
+
page** — the headline feature of 0.2.0, absent from both places a reader looks for it. Both
|
|
25
|
+
now cover it, including `Backtest.run_with_tearsheet(dir)`, which was documented only in the
|
|
26
|
+
changelog and `examples/run_tearsheet.py`. `AGENTS.md` §7 and §11 pick it up too, and the
|
|
27
|
+
examples list in the README no longer omits `run_tearsheet.py`.
|
|
28
|
+
- Version strings that describe the CURRENT state (README status, `docs/DESIGN.md`'s "proven
|
|
29
|
+
today", the captured provenance lines in the tutorial and `website/results.md`) said 0.1.0.
|
|
30
|
+
The 0.1.0 references that are historical — when the exchange calendar was removed, and why —
|
|
31
|
+
are left alone deliberately.
|
|
32
|
+
|
|
9
33
|
## [0.2.0] — 2026-08-16
|
|
10
34
|
|
|
11
35
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: topstep-backtest
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Event-driven backtesting framework for Topstep Trading Combine strategies, with backtest/live parity against topstep-sdk.
|
|
5
5
|
Author-email: Tarric Sookdeo <tarricsookdeo@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -160,6 +160,21 @@ silently is how a report lies:
|
|
|
160
160
|
how to read every figure above. The same drawdown at 5% and at 95% exposure are not the same
|
|
161
161
|
risk. Alongside `equity_peak` (what a trailing floor anchors to) and the run's window.
|
|
162
162
|
|
|
163
|
+
The same report renders as an **interactive HTML tearsheet** — one self-contained file with
|
|
164
|
+
no server and no network, so it opens offline and archives next to the run:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
report.to_html("tearsheet.html") # name the file
|
|
168
|
+
report.show() # or write a temp file and open a browser
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
It draws the candlestick tape with every fill marked, the equity curve against the trailing
|
|
172
|
+
MLL floor, daily P&L and the R-multiple distribution, plus every statistic the text render
|
|
173
|
+
prints, basis labels included — the two renders share their formatting helpers, so they
|
|
174
|
+
cannot disagree. For a sheet from *every* run without naming a file each time,
|
|
175
|
+
`Backtest(...).run_with_tearsheet("runs")` writes `tearsheet-<UTC stamp>.html` and returns
|
|
176
|
+
both the report and the path.
|
|
177
|
+
|
|
163
178
|
Then stop trusting one sample:
|
|
164
179
|
|
|
165
180
|
```python
|
|
@@ -194,7 +209,7 @@ uv run python examples/run_montecarlo.py # the same edge at two sizes, failing
|
|
|
194
209
|
|
|
195
210
|
## Status and limitations
|
|
196
211
|
|
|
197
|
-
**Pre-alpha (0.
|
|
212
|
+
**Pre-alpha (0.2.0).** The engine core is well covered — exact-Decimal money on the tick
|
|
198
213
|
grid, FIFO lot accounting, a structurally enforced no-look-ahead firewall, byte-identical
|
|
199
214
|
reruns — but read these before trusting a number:
|
|
200
215
|
|
|
@@ -205,10 +220,11 @@ reruns — but read these before trusting a number:
|
|
|
205
220
|
landing within a tick or a fee of a limit. This applies with *more* force to the
|
|
206
221
|
Monte-Carlo pass probability: a figure printed to one decimal from unverified constants is
|
|
207
222
|
precise, not accurate.
|
|
208
|
-
- **Analytics
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
- **Analytics can only resample the tape you gave them.** Walk-forward, PBO, deflated Sharpe
|
|
224
|
+
and the EV-per-attempt model all ship (`metrics/walkforward.py`, `metrics/overfitting.py`,
|
|
225
|
+
`metrics/economics.py`), but none of them escapes your sample: the Monte-Carlo resamples a
|
|
226
|
+
strategy's own observed days, so it cannot invent a market regime your tape never contained
|
|
227
|
+
and will understate tail risk on a short or single-regime sample.
|
|
212
228
|
- **No exchange holiday calendar ships with this package.** Bars on market holidays and
|
|
213
229
|
past early-close halts are not detected, flagged or filtered anywhere — filter them
|
|
214
230
|
upstream. (A built-in calendar was removed in 0.1.0: it disagreed with CME on several
|
|
@@ -252,8 +268,13 @@ built wheel into a clean venv and smoke-tests it. **`ruff format --check` is par
|
|
|
252
268
|
|
|
253
269
|
There is a full documentation site — a browsable version of everything below, plus a
|
|
254
270
|
quickstart, a page on reading the report, and an API reference generated from the live
|
|
255
|
-
docstrings
|
|
256
|
-
|
|
271
|
+
docstrings:
|
|
272
|
+
|
|
273
|
+
**<https://tarricsookdeo.github.io/topstep-backtest/>**
|
|
274
|
+
|
|
275
|
+
It is rebuilt from `main` on every push. The site is public; the **source repository is
|
|
276
|
+
not**, so there are no "Edit this page" links and there is no public issue tracker. To read
|
|
277
|
+
it offline, or to preview a change before pushing it:
|
|
257
278
|
|
|
258
279
|
```bash
|
|
259
280
|
uv run --extra docs mkdocs serve # http://127.0.0.1:8000
|
|
@@ -275,13 +296,13 @@ environment. There is no public issue tracker.
|
|
|
275
296
|
and a verify-before-trusting checklist.
|
|
276
297
|
- `docs/DESIGN.md` — architecture contract, for *modifying* the framework.
|
|
277
298
|
- `docs/ROADMAP.md` — what is built, partial, and not started. Next: **live adapter +
|
|
278
|
-
calibration** →
|
|
279
|
-
|
|
299
|
+
calibration** → per-year / per-regime breakdowns → L1/L2/MBO fill tiers; funded-account
|
|
300
|
+
(XFA) modeling is deliberately parked.
|
|
280
301
|
- `examples/` — runnable: `run_real_data.py` (your CSV/Parquet → verdict), `run_combine.py`
|
|
281
|
-
(synthetic end to end), `
|
|
282
|
-
`
|
|
283
|
-
`ema_cross.py`, `sma_cross.py`,
|
|
284
|
-
assembles).
|
|
302
|
+
(synthetic end to end), `run_tearsheet.py` (the same run as one HTML file),
|
|
303
|
+
`run_montecarlo.py` (outcome distribution + autopsy), `run_windows.py` (a long tape
|
|
304
|
+
replayed as consecutive independent Combine attempts), `ema_cross.py`, `sma_cross.py`,
|
|
305
|
+
`talib_macd.py`, `hand_wired.py` (what the facade assembles).
|
|
285
306
|
|
|
286
307
|
## Stack
|
|
287
308
|
|
|
@@ -120,6 +120,21 @@ silently is how a report lies:
|
|
|
120
120
|
how to read every figure above. The same drawdown at 5% and at 95% exposure are not the same
|
|
121
121
|
risk. Alongside `equity_peak` (what a trailing floor anchors to) and the run's window.
|
|
122
122
|
|
|
123
|
+
The same report renders as an **interactive HTML tearsheet** — one self-contained file with
|
|
124
|
+
no server and no network, so it opens offline and archives next to the run:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
report.to_html("tearsheet.html") # name the file
|
|
128
|
+
report.show() # or write a temp file and open a browser
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
It draws the candlestick tape with every fill marked, the equity curve against the trailing
|
|
132
|
+
MLL floor, daily P&L and the R-multiple distribution, plus every statistic the text render
|
|
133
|
+
prints, basis labels included — the two renders share their formatting helpers, so they
|
|
134
|
+
cannot disagree. For a sheet from *every* run without naming a file each time,
|
|
135
|
+
`Backtest(...).run_with_tearsheet("runs")` writes `tearsheet-<UTC stamp>.html` and returns
|
|
136
|
+
both the report and the path.
|
|
137
|
+
|
|
123
138
|
Then stop trusting one sample:
|
|
124
139
|
|
|
125
140
|
```python
|
|
@@ -154,7 +169,7 @@ uv run python examples/run_montecarlo.py # the same edge at two sizes, failing
|
|
|
154
169
|
|
|
155
170
|
## Status and limitations
|
|
156
171
|
|
|
157
|
-
**Pre-alpha (0.
|
|
172
|
+
**Pre-alpha (0.2.0).** The engine core is well covered — exact-Decimal money on the tick
|
|
158
173
|
grid, FIFO lot accounting, a structurally enforced no-look-ahead firewall, byte-identical
|
|
159
174
|
reruns — but read these before trusting a number:
|
|
160
175
|
|
|
@@ -165,10 +180,11 @@ reruns — but read these before trusting a number:
|
|
|
165
180
|
landing within a tick or a fee of a limit. This applies with *more* force to the
|
|
166
181
|
Monte-Carlo pass probability: a figure printed to one decimal from unverified constants is
|
|
167
182
|
precise, not accurate.
|
|
168
|
-
- **Analytics
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
- **Analytics can only resample the tape you gave them.** Walk-forward, PBO, deflated Sharpe
|
|
184
|
+
and the EV-per-attempt model all ship (`metrics/walkforward.py`, `metrics/overfitting.py`,
|
|
185
|
+
`metrics/economics.py`), but none of them escapes your sample: the Monte-Carlo resamples a
|
|
186
|
+
strategy's own observed days, so it cannot invent a market regime your tape never contained
|
|
187
|
+
and will understate tail risk on a short or single-regime sample.
|
|
172
188
|
- **No exchange holiday calendar ships with this package.** Bars on market holidays and
|
|
173
189
|
past early-close halts are not detected, flagged or filtered anywhere — filter them
|
|
174
190
|
upstream. (A built-in calendar was removed in 0.1.0: it disagreed with CME on several
|
|
@@ -212,8 +228,13 @@ built wheel into a clean venv and smoke-tests it. **`ruff format --check` is par
|
|
|
212
228
|
|
|
213
229
|
There is a full documentation site — a browsable version of everything below, plus a
|
|
214
230
|
quickstart, a page on reading the report, and an API reference generated from the live
|
|
215
|
-
docstrings
|
|
216
|
-
|
|
231
|
+
docstrings:
|
|
232
|
+
|
|
233
|
+
**<https://tarricsookdeo.github.io/topstep-backtest/>**
|
|
234
|
+
|
|
235
|
+
It is rebuilt from `main` on every push. The site is public; the **source repository is
|
|
236
|
+
not**, so there are no "Edit this page" links and there is no public issue tracker. To read
|
|
237
|
+
it offline, or to preview a change before pushing it:
|
|
217
238
|
|
|
218
239
|
```bash
|
|
219
240
|
uv run --extra docs mkdocs serve # http://127.0.0.1:8000
|
|
@@ -235,13 +256,13 @@ environment. There is no public issue tracker.
|
|
|
235
256
|
and a verify-before-trusting checklist.
|
|
236
257
|
- `docs/DESIGN.md` — architecture contract, for *modifying* the framework.
|
|
237
258
|
- `docs/ROADMAP.md` — what is built, partial, and not started. Next: **live adapter +
|
|
238
|
-
calibration** →
|
|
239
|
-
|
|
259
|
+
calibration** → per-year / per-regime breakdowns → L1/L2/MBO fill tiers; funded-account
|
|
260
|
+
(XFA) modeling is deliberately parked.
|
|
240
261
|
- `examples/` — runnable: `run_real_data.py` (your CSV/Parquet → verdict), `run_combine.py`
|
|
241
|
-
(synthetic end to end), `
|
|
242
|
-
`
|
|
243
|
-
`ema_cross.py`, `sma_cross.py`,
|
|
244
|
-
assembles).
|
|
262
|
+
(synthetic end to end), `run_tearsheet.py` (the same run as one HTML file),
|
|
263
|
+
`run_montecarlo.py` (outcome distribution + autopsy), `run_windows.py` (a long tape
|
|
264
|
+
replayed as consecutive independent Combine attempts), `ema_cross.py`, `sma_cross.py`,
|
|
265
|
+
`talib_macd.py`, `hand_wired.py` (what the facade assembles).
|
|
245
266
|
|
|
246
267
|
## Stack
|
|
247
268
|
|
|
@@ -258,7 +258,7 @@ Violations are bugs, not style. Most are pinned by a named test.
|
|
|
258
258
|
OHLCV bars are not the tape-built bars live produces. Require **Tier-1+ (quote/tick)
|
|
259
259
|
validation** for any strategy whose edge is intrabar-timing-sensitive before trusting it live.
|
|
260
260
|
|
|
261
|
-
> **What is actually proven today (0.
|
|
261
|
+
> **What is actually proven today (0.2.0).** Rungs 1–4 hold: the sim constructs real SDK models,
|
|
262
262
|
> and `tests/parity/` asserts — pyright-strict *and* at runtime — that both `AsyncTopstepClient`
|
|
263
263
|
> and `SimBroker` satisfy `Broker`, with a keyword-level signature diff on `place`. That is
|
|
264
264
|
> **structural** conformance: same call surface, same types, same `APIError` codes. Rung 5 is
|
|
@@ -655,7 +655,7 @@ daily P&L (6 closed: 1 win / 4 lose / 1 flat)
|
|
|
655
655
|
best +84.04 (consistency ratio undefined: no net profit to share)
|
|
656
656
|
stdev 40.27 <- dollars, so it compares directly against the DLL
|
|
657
657
|
|
|
658
|
-
topstep-backtest 0.
|
|
658
|
+
topstep-backtest 0.2.0 — unofficial simulation, not affiliated with Topstep. Rule and fee constants are cited config, NOT calibrated against a live account (docs/topstep-rules.md §9): treat the verdict as a diagnostic, not an authoritative pass/fail.
|
|
659
659
|
```
|
|
660
660
|
|
|
661
661
|
Line by line:
|
|
@@ -681,7 +681,7 @@ Line by line:
|
|
|
681
681
|
the rule and fee constants are not calibrated, so a screenshotted verdict
|
|
682
682
|
carries its own caveat. The version is read from the installed distribution:
|
|
683
683
|
this is the one line above that legitimately differs from your run (a source
|
|
684
|
-
checkout reports a `.devN` suffix, not `0.
|
|
684
|
+
checkout reports a `.devN` suffix, not `0.2.0`).
|
|
685
685
|
- **`REJECTED`** — *absent here, and its absence is information.* When the
|
|
686
686
|
broker refuses any order placement the render grows a `REJECTED` line above
|
|
687
687
|
the day trail, counting the refusals per gateway error code. Without it a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/__init__.py
RENAMED
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/rejections.py
RENAMED
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/execution/sim_broker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/library.py
RENAMED
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/indicators/talib_adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/montecarlo.py
RENAMED
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/overfitting.py
RENAMED
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/metrics/walkforward.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/src/topstep_backtest/tearsheet/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/golden/artifacts/verdict_passed_s50k.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{topstep_backtest-0.2.0 → topstep_backtest-0.2.2}/tests/unit/test_talib_adapter_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|