Skip to content

feat(rl): fractal-zigzag entry plug-in on 3-minute Parquet - #12

Merged
lgbarn merged 5 commits into
mainfrom
issue-7-fractal-zigzag-plugin
Jul 12, 2026
Merged

feat(rl): fractal-zigzag entry plug-in on 3-minute Parquet#12
lgbarn merged 5 commits into
mainfrom
issue-7-fractal-zigzag-plugin

Conversation

@lgbarn

@lgbarn lgbarn commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • New RLStrategy plug-in futures_foundation/rl/fractal_zigzag.py: detect_entries driven by detect_fractal_zigzag_pivots (the trigger-scan causal winner) with live_edge=True newest-bar confirms, so entries are truncation-invariant at the data edge.
  • Every candidate carries direction, detection-time entry reference price (close of the confirm bar; actual fill = next bar's open, enforced centrally by the pipeline), sl_distance = 1x Wilder ATR(20) at the signal bar, and the absolute stop_price.
  • compute_obs_features reuses the causal pivot-structure block from futures_foundation.pivots (htf_dir excluded — its ATR floor is a full-series median, not truncation-invariant, so not obs-safe).
  • load_3min_parquet + SYMBOLS for the six-instrument 3-min Parquet data; registered in the RL registry as "fractal_zigzag".

Closes

Closes #7

Acceptance criteria

  • Entries are truncation-invariant: running detection on a truncated series yields identical entries up to the truncation point — test_entries_truncation_invariant (generic check_causal harness + exact prefix==full frame equality)
  • Every candidate entry has a 1x ATR initial stop attached at detection time — test_every_entry_has_1x_atr_stop
  • All six symbols load from Parquet and produce plausible entry counts over the full 5-year span — test_six_symbols_load_and_produce_plausible_counts (data-gated); counts reported below
  • Feature computation is causal — test_obs_features_truncation_invariant (byte-identical rows under truncation)

Entry counts (full span, k=2, min_leg_atr=1.25, ATR(20))

symbol bars span entries long/short bars per entry
NQ 602,765 2021-04-28 .. 2026-06-04 80,759 40,380 / 40,379 7.5
ES 602,766 2021-04-28 .. 2026-06-04 62,849 31,425 / 31,424 9.6
RTY 601,700 2021-04-28 .. 2026-06-04 73,327 36,664 / 36,663 8.2
YM 602,656 2021-04-28 .. 2026-06-04 74,138 37,069 / 37,069 8.1
GC 603,710 2021-04-28 .. 2026-06-04 69,552 34,776 / 34,776 8.7
SI 602,571 2021-04-28 .. 2026-06-04 59,828 29,914 / 29,914 10.1

The near-exact long/short split follows from the detector's strict pivot alternation; the ~1-entry-per-8-10-bars density is the intended mechanical-candidate stream (the PPO chop-veto is the filter, per the RLStrategy design).

Test plan

  • uv run pytest tests/test_fractal_zigzag_strategy.py -q — 4 pass (parquet test skips without data)
  • FFM_DATA_DIR=<path-to>/data uv run pytest tests/test_fractal_zigzag_strategy.py -q — 4 pass including the six-symbol span/count check
  • uv run pytest tests/test_fractal_pivots.py tests/test_rl_pipeline.py -q — adjacent suites unchanged, green

Notes for reviewer

  • TDD seam record (issue carries the tdd label): each acceptance criterion was one red→green cycle, in commit order — truncation invariance → 1x ATR stop → causal obs features → Parquet loader/counts.
  • live_edge=True is hardwired in detect_entries: it is both the issue's requirement (newest-bar confirms for bar-by-bar consumers) and what makes the prefix==full equality exact at the boundary (a training-mode cf+1 < n guard would drop the newest pivot on the prefix side).
  • - [x] items are author claims verified by local test runs; the parquet-gated test skips on CI (data is local-only/gitignored), so the counts table is the CI-visible evidence for AC 3.
  • Footprint: two new files only (futures_foundation/rl/fractal_zigzag.py, tests/test_fractal_zigzag_strategy.py); rl/env.py, rl/__init__.py, tests/test_rl_pipeline.py, and topstep.py untouched (sibling-owned this wave).
  • The local full-suite run shows 39 pre-existing failures, all ModuleNotFoundError for optional deps (sklearn/torch/optuna/onnxruntime) absent from the local env; CI installs requirements.txt and is the arbiter.

Out of scope

lgbarn added 4 commits July 11, 2026 21:32
…entries

First concrete RLStrategy entry side: detect_fractal_zigzag_pivots with
live_edge=True so entries are identical on any prefix (no drift at the
data edge). Seam 1 of the TDD plan: the lookahead proof runs both the
generic check_causal harness and an exact prefix==full comparison.

Refs #7
Seam 2: every candidate carries sl_distance = stop_atr * Wilder
ATR(atr_period) at the signal bar plus the absolute stop_price one stop
against the trade from the detection-time entry reference.

Refs #7
Seam 3: compute_obs_features reuses the causal pivot-structure block
from futures_foundation.pivots (htf_dir excluded — its ATR floor is a
full-series median, not truncation-invariant). Proven byte-identical
under truncation in the new test.

Refs #7
Seam 4: load_3min_parquet + SYMBOLS; the data-gated test proves all six
instruments load, span 2021-04..2026-06, and produce a plausible entry
rate (skips on CI where the Parquet data is absent).

Refs #7
@lgbarn
lgbarn marked this pull request as ready for review July 12, 2026 01:41
@lgbarn
lgbarn merged commit dc59b96 into main Jul 12, 2026
2 checks passed
@lgbarn
lgbarn deleted the issue-7-fractal-zigzag-plugin branch July 12, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fractal-zigzag entry plug-in on 3-minute Parquet

1 participant