topstep-backtest 0.2.2__tar.gz → 0.2.3__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.
Files changed (116) hide show
  1. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/CHANGELOG.md +29 -0
  2. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/PKG-INFO +4 -2
  3. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/README.md +1 -1
  4. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/docs/DESIGN.md +2 -1
  5. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/pyproject.toml +12 -1
  6. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_wrangler.py +36 -0
  7. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/.gitignore +0 -0
  8. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/AGENTS.md +0 -0
  9. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/LICENSE +0 -0
  10. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/data/sample_mnq_1m.csv +0 -0
  11. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/docs/INDICATORS.md +0 -0
  12. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/docs/ROADMAP.md +0 -0
  13. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/docs/TUTORIAL_EMA_CROSSOVER.md +0 -0
  14. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/docs/topstep-rules.md +0 -0
  15. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/ema_cross.py +0 -0
  16. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/hand_wired.py +0 -0
  17. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/run_combine.py +0 -0
  18. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/run_montecarlo.py +0 -0
  19. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/run_real_data.py +0 -0
  20. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/run_tearsheet.py +0 -0
  21. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/run_windows.py +0 -0
  22. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/sma_cross.py +0 -0
  23. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/examples/talib_macd.py +0 -0
  24. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/__init__.py +0 -0
  25. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/_render.py +0 -0
  26. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/clock/__init__.py +0 -0
  27. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/clock/live_clock.py +0 -0
  28. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/clock/test_clock.py +0 -0
  29. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/core/__init__.py +0 -0
  30. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/core/ids.py +0 -0
  31. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/core/instruments.py +0 -0
  32. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/core/money.py +0 -0
  33. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/core/time.py +0 -0
  34. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/__init__.py +0 -0
  35. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/clean.py +0 -0
  36. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/continuous.py +0 -0
  37. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/feed.py +0 -0
  38. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/synthetic.py +0 -0
  39. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/validator.py +0 -0
  40. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/data/wrangler.py +0 -0
  41. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/engine/__init__.py +0 -0
  42. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/engine/backtest.py +0 -0
  43. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/execution/__init__.py +0 -0
  44. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/execution/rejections.py +0 -0
  45. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/execution/sim_broker.py +0 -0
  46. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/fills/__init__.py +0 -0
  47. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/fills/bar_fill.py +0 -0
  48. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/fills/fees.py +0 -0
  49. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/fills/path.py +0 -0
  50. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/harness.py +0 -0
  51. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/indicators/__init__.py +0 -0
  52. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/indicators/base.py +0 -0
  53. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/indicators/library.py +0 -0
  54. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/indicators/talib_adapter.py +0 -0
  55. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/__init__.py +0 -0
  56. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/economics.py +0 -0
  57. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/montecarlo.py +0 -0
  58. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/overfitting.py +0 -0
  59. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/stats.py +0 -0
  60. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/walkforward.py +0 -0
  61. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/metrics/windows.py +0 -0
  62. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/protocols.py +0 -0
  63. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/py.typed +0 -0
  64. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/rules/__init__.py +0 -0
  65. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/rules/kernel.py +0 -0
  66. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/rules/params.py +0 -0
  67. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/strategy/__init__.py +0 -0
  68. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/strategy/base.py +0 -0
  69. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/strategy/symbol.py +0 -0
  70. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/strategy/tracker.py +0 -0
  71. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/tearsheet/__init__.py +0 -0
  72. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/tearsheet/_assets/lightweight-charts.LICENSE +0 -0
  73. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/tearsheet/_assets/lightweight-charts.standalone.production.js +0 -0
  74. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/tearsheet/_assets/tearsheet.css +0 -0
  75. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/src/topstep_backtest/tearsheet/_assets/tearsheet.js +0 -0
  76. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/__init__.py +0 -0
  77. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/conftest.py +0 -0
  78. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/__init__.py +0 -0
  79. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/artifacts/verdict_failed_mll_s50k.json +0 -0
  80. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/artifacts/verdict_passed_s50k.json +0 -0
  81. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/test_combine_kernel.py +0 -0
  82. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/test_facade_equivalence.py +0 -0
  83. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/test_sugar_equivalence.py +0 -0
  84. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/golden/test_verdict_goldens.py +0 -0
  85. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/parity/__init__.py +0 -0
  86. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/parity/test_broker_conformance.py +0 -0
  87. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/property/__init__.py +0 -0
  88. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/property/test_indicator_props.py +0 -0
  89. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/property/test_kernel_props.py +0 -0
  90. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/property/test_money_props.py +0 -0
  91. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/__init__.py +0 -0
  92. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_bar_fill.py +0 -0
  93. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_clean.py +0 -0
  94. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_clock.py +0 -0
  95. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_continuous.py +0 -0
  96. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_data_feed.py +0 -0
  97. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_economics.py +0 -0
  98. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_engine.py +0 -0
  99. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_fees.py +0 -0
  100. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_harness.py +0 -0
  101. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_indicators.py +0 -0
  102. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_instruments.py +0 -0
  103. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_montecarlo.py +0 -0
  104. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_overfitting.py +0 -0
  105. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_path.py +0 -0
  106. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_sim_broker.py +0 -0
  107. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_stats.py +0 -0
  108. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_symbol_strategy.py +0 -0
  109. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_synthetic.py +0 -0
  110. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_talib_adapter_hardening.py +0 -0
  111. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_tearsheet.py +0 -0
  112. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_time.py +0 -0
  113. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_tracker.py +0 -0
  114. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_validator.py +0 -0
  115. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_walkforward.py +0 -0
  116. {topstep_backtest-0.2.2 → topstep_backtest-0.2.3}/tests/unit/test_windows.py +0 -0
@@ -6,6 +6,35 @@ below 1.0, minor releases may contain breaking changes.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.3] — 2026-08-16
10
+
11
+ ### Fixed
12
+
13
+ - The `[data]` and `[dev]` extras now install `pyarrow`, so **Parquet input works out of the
14
+ box**. `examples/run_real_data.py` has always branched to `pd.read_parquet` on a `.parquet` /
15
+ `.pq` suffix, but pandas ships no Parquet engine of its own — so `[data]` alone met that
16
+ branch with `ImportError: Unable to find a usable engine`, on the format most vendors
17
+ (Databento included) actually export. Deliberately unbounded, unlike `topstep-sdk` and
18
+ `ta-lib`: a breaking `pyarrow` major fails loudly at read time instead of quietly changing a
19
+ number. Reading the same bars from CSV and from Parquet produces a byte-identical report.
20
+
21
+ Released as 0.2.3, not 0.2.2: the `v0.2.2` tag was cut from a commit three minutes before
22
+ this change merged, so **0.2.2 shipped without pyarrow** and its Parquet path fails exactly
23
+ as 0.2.1's did. A PyPI version can never be re-uploaded, so the fix moved forward a version
24
+ rather than the tag moving backward.
25
+
26
+ ## [0.2.2] — 2026-08-16
27
+
28
+ ### Fixed
29
+
30
+ - Repository plumbing only; no user-facing change. `ruff format` also formats Python inside
31
+ markdown fences, so the tearsheet examples added to the README and `website/results.md` in
32
+ 0.2.1 were unformatted code and CI had been red since — through two merges. And the docs
33
+ workflow's `actions/configure-pages` step called the Pages REST API under a `contents: read`
34
+ token, failing every deploy with "Resource not accessible by integration" while blaming the
35
+ repository's Pages settings; MkDocs takes its base URL from `site_url`, so the step was
36
+ removed rather than the token widened.
37
+
9
38
  ## [0.2.1] — 2026-08-16
10
39
 
11
40
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: topstep-backtest
3
- Version: 0.2.2
3
+ Version: 0.2.3
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
@@ -23,9 +23,11 @@ Requires-Dist: topstep-sdk<0.2,>=0.1.2
23
23
  Requires-Dist: tzdata>=2024.1; sys_platform == 'win32'
24
24
  Provides-Extra: data
25
25
  Requires-Dist: pandas>=2.2; extra == 'data'
26
+ Requires-Dist: pyarrow>=17; extra == 'data'
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: hypothesis>=6.100; extra == 'dev'
28
29
  Requires-Dist: pandas>=2.2; extra == 'dev'
30
+ Requires-Dist: pyarrow>=17; extra == 'dev'
29
31
  Requires-Dist: pyright>=1.1.380; extra == 'dev'
30
32
  Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
31
33
  Requires-Dist: pytest-cov>=5.0; extra == 'dev'
@@ -80,7 +82,7 @@ framework helper that compares two TA-Lib outputs rather than computing anything
80
82
 
81
83
  ```bash
82
84
  pip install topstep-backtest
83
- pip install "topstep-backtest[data]" # adds pandas, for DataFrame input
85
+ pip install "topstep-backtest[data]" # adds pandas + pyarrow: DataFrame and Parquet input
84
86
  ```
85
87
 
86
88
  Requires Python 3.12+. TA-Lib is a core dependency and ships wheels for common platforms;
@@ -40,7 +40,7 @@ framework helper that compares two TA-Lib outputs rather than computing anything
40
40
 
41
41
  ```bash
42
42
  pip install topstep-backtest
43
- pip install "topstep-backtest[data]" # adds pandas, for DataFrame input
43
+ pip install "topstep-backtest[data]" # adds pandas + pyarrow: DataFrame and Parquet input
44
44
  ```
45
45
 
46
46
  Requires Python 3.12+. TA-Lib is a core dependency and ships wheels for common platforms;
@@ -219,7 +219,8 @@ Violations are bugs, not style. Most are pinned by a named test.
219
219
  `ruff format --check` is the one people forget — which is also why `ruff` is pinned
220
220
  `>=0.16,<0.17`: format output changes between minors, so an unbounded pin would fail CI on
221
221
  correctly formatted code. Runtime deps are `topstep-sdk`, `msgspec`, `ta-lib` (the last is
222
- core, not an extra); exactly two extras exist, `[data]` and `[dev]`.
222
+ core, not an extra); exactly three extras exist, `[data]` (pandas + pyarrow, so both
223
+ DataFrame and Parquet input work), `[dev]` and `[docs]`.
223
224
 
224
225
  ## 6. Testing idioms
225
226
 
@@ -37,13 +37,24 @@ dependencies = [
37
37
  ]
38
38
 
39
39
  [project.optional-dependencies]
40
- data = ["pandas>=2.2"]
40
+ data = [
41
+ "pandas>=2.2",
42
+ # pandas reads Parquet only through a third-party engine and ships none, so
43
+ # `[data]` without this turns every .parquet export — the format Databento
44
+ # and most vendors hand you — into an ImportError at load time. No upper
45
+ # bound, unlike topstep-sdk and ta-lib above: a breaking pyarrow major fails
46
+ # LOUDLY at read time rather than quietly changing a number, so pinning it
47
+ # out would buy nothing and strand the extra on an old wheel.
48
+ "pyarrow>=17",
49
+ ]
41
50
  dev = [
42
51
  "pytest>=8.0",
43
52
  "pytest-asyncio>=0.24",
44
53
  "pytest-cov>=5.0",
45
54
  "hypothesis>=6.100",
46
55
  "pandas>=2.2",
56
+ # Mirrors `[data]`: the Parquet path needs an engine to be exercised at all.
57
+ "pyarrow>=17",
47
58
  # ruff is PINNED to a minor range on purpose: `ruff format --check` runs in
48
59
  # CI, and formatting output changes between ruff minors. Unbounded, CI would
49
60
  # spontaneously fail on code that is correctly formatted locally. Bump this
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from collections.abc import Sequence
6
6
  from datetime import UTC, datetime
7
7
  from decimal import Decimal
8
+ from pathlib import Path
8
9
  from typing import Literal
9
10
 
10
11
  import pandas as pd # pyright: ignore[reportMissingTypeStubs]
@@ -354,6 +355,41 @@ class TestDataFrame:
354
355
  stamp="open",
355
356
  )
356
357
 
358
+ def test_parquet_and_csv_agree(self, tmp_path: Path) -> None:
359
+ """The `[data]` extra must be able to READ Parquet, not just accept it.
360
+
361
+ pandas ships no Parquet engine of its own, so this fails with
362
+ ``ImportError: Unable to find a usable engine`` if the extra ever loses
363
+ pyarrow again — which is exactly what shipped in 0.2.1, on the format
364
+ most vendors export. Reading the two formats through the documented path
365
+ (`examples/run_real_data.py` parses CSV timestamps itself, because the
366
+ wrangler takes ints or tz-aware datetimes, never `read_csv`'s strings)
367
+ must produce identical bars.
368
+ """
369
+ stamps = [datetime(2026, 3, 2, 14, 30 + i, tzinfo=UTC) for i in range(3)]
370
+ df = pd.DataFrame(
371
+ {
372
+ "timestamp": stamps,
373
+ "open": [100.0, 100.25, 100.5],
374
+ "high": [100.25, 100.5, 100.75],
375
+ "low": [99.75, 100.0, 100.25],
376
+ "close": [100.25, 100.5, 100.5],
377
+ "volume": [10, 20, 30],
378
+ }
379
+ )
380
+ csv_path, pq_path = tmp_path / "bars.csv", tmp_path / "bars.parquet"
381
+ df.to_csv(csv_path, index=False) # pyright: ignore[reportUnknownMemberType]
382
+ # An ImportError here means the [data]/[dev] extras lost their Parquet engine.
383
+ df.to_parquet(pq_path) # pyright: ignore[reportUnknownMemberType]
384
+
385
+ from_csv = pd.read_csv(csv_path)
386
+ from_csv["timestamp"] = pd.to_datetime( # pyright: ignore[reportUnknownMemberType]
387
+ from_csv["timestamp"] # pyright: ignore[reportUnknownArgumentType]
388
+ )
389
+ from_parquet = pd.read_parquet(pq_path) # pyright: ignore[reportUnknownMemberType]
390
+
391
+ assert self._df_bars(from_csv) == self._df_bars(from_parquet) == self._df_bars(df)
392
+
357
393
 
358
394
  @given(
359
395
  ticks=st.integers(min_value=1, max_value=1_000_000),