Skip to content

fix(magnifier): use real lower-TF input bars as waypoints - #6

Merged
luisleo526 merged 2 commits into
mainfrom
fix/magnifier-real-1m-bars
May 11, 2026
Merged

luisleo526 merged 2 commits into
mainfrom
fix/magnifier-real-1m-bars

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Summary

When 1m OHLCV is fed to the engine with script_tf=15m, each script bar's run_magnified_bar now collapses to ENDPOINTS+4 sampling per real sub-bar. Synthetic UNIFORM/COSINE/TRIANGLE/FRONT/BACK distributions injected mid-points inside each real 1m bar that did not correspond to any actual tick — driving up to ~86% drift on exit_p99 versus the ENDPOINTS reference probe.

  • src/engine_run.cpp: detect total_sub > 1 (multi-sub-bar real-bar magnifier mode) and pin to sample_price_path(sb, 4, ENDPOINTS); volume-weighted upsampling becomes a no-op in that mode.
  • tests/test_magnifier_real_bars.cpp: new — three tests pin the contract end-to-end.
  • tests/test_integration.cpp: existing test_magnifier_volume_weighted_toggle updated to assert vw is a no-op in real-bar mode.

Effect on validation_magnifier corpus (8 dist probes)

Probe exit_p99 before exit_p99 after Label before Label after
01-endpoints 1.19% 1.19% strong strong
02-uniform 40.22% 1.19% engine_only strong
03-cosine 53.81% 1.19% engine_only strong
04-triangle 54.75% 1.19% engine_only strong
05-front-loaded 86.05% 1.19% engine_only strong
06-back-loaded 49.84% 1.19% engine_only strong
07-volume-weighted-on 3.33% 1.19% engine_only strong
08b-uniform-rsi-cross 34.85% 1.12% engine_only strong

Full corpus regression

Identical aggregate: 167 excellent / 20 strong / 4 moderate / 1 weak / 1 minimal (197 total). Only the 7 magnifier-dist non-ENDPOINTS probes changed labels (engine_only -> strong). No regressions elsewhere.

Test plan

  • ctest --output-on-failure 24/24 pass
  • validation_magnifier corpus: 9/9 strong (was 2 strong, 7 engine_only)
  • Full corpus regression: identical aggregate vs main

luisleo526 and others added 2 commits May 12, 2026 00:55
When the engine is fed real lower-TF OHLCV (input_tf < script_tf, e.g. 1m
input aggregated to a 15m script TF) and bar_magnifier=true, each script
bar's run_magnified_bar() receives multiple real sub-bars. Prior to this
change the engine would still synthesize magnifier_samples_ ticks per
sub-bar using whatever MagnifierDistribution the user requested — with
non-ENDPOINTS distributions injecting spurious mid-points inside each
real 1m bar that did not correspond to any actual tick data.

This is the fix the validator's 1m corpus exposed: TV's broker emulator
walks real 1m OHLC, so synthetic UNIFORM/COSINE/TRIANGLE/FRONT/BACK
ticks added drift up to ~86% on exit_p99 metrics relative to the
ENDPOINTS reference probe.

When more than one sub-bar is passed to run_magnified_bar() (the real-
bar magnifier condition), force ENDPOINTS+4 sampling regardless of the
user-requested distribution, and skip volume-weighted upsampling.
Synthetic ticks beyond the four real OHLC corners cannot recover
information that was not in the input feed; collapsing to the natural
endpoints reproduces TV's per-1m-bar walk exactly.

Effect on the magnifier-dist corpus (8 probes, 1m → 15m):

  exit_p99 (before -> after)
    01-endpoints       1.19% -> 1.19% (unchanged baseline)
    02-uniform        40.22% -> 1.19%
    03-cosine         53.81% -> 1.19%
    04-triangle       54.75% -> 1.19%
    05-front-loaded   86.05% -> 1.19%
    06-back-loaded    49.84% -> 1.19%
    07-volume-weighted 3.33% -> 1.19%
    08b-uniform-rsi   34.85% -> 1.12%

Single-sub-bar mode (input_tf == script_tf) is unaffected — the legacy
synthesized-distribution path stays in force, so user-chosen
distributions remain meaningful for callers without lower-TF input.
The existing volume_weighted_toggle test is updated to assert the new
contract: vw is a no-op in real-bar mode (each sub-bar pinned at 4
real OHLC ticks).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Pin the new real-bar magnifier contract with three end-to-end tests:

  test_stop_fills_in_correct_script_bar
    30 1m bars + script_tf=15m -> 2 chart bars. A long entry on bar 0
    plus a stop at 95.0 (which only the 1m bar inside chart-bar #2
    crosses) must fill at exactly 95.0 with closed_trade_exit_bar_index
    == 1, proving the fill is routed to the actual 1m sub-bar where
    price crossed the stop, not to a synthesized chart-bar tick.

  test_distribution_irrelevant_when_real_sub_bars
    Same 30 1m bars + four distinct (distribution, samples) pairs
    (ENDPOINTS+4, UNIFORM+8, COSINE+16, FRONT_LOADED+12). All four runs
    must produce identical trade count, exit price, and exit bar.
    Guards against any future magnifier change reintroducing synthetic
    ticks inside real lower-TF bars.

  test_legacy_path_used_when_single_sub_bar
    input_tf == script_tf with bar_magnifier=true and 8-sample UNIFORM.
    Confirms the legacy synthesized-distribution path is still active
    when only one sub-bar arrives per script bar (16 sample ticks for
    2 sub-bars, not 8 — i.e. real-bar mode does NOT engage).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@luisleo526
luisleo526 merged commit 0d3653f into main May 11, 2026
4 checks passed
@luisleo526
luisleo526 deleted the fix/magnifier-real-1m-bars branch May 11, 2026 16:57
luisleo526 added a commit that referenced this pull request Jun 10, 2026
…it-fix sweep

- pine_v6_audit_master: mark #5/#6/#31/#32/#35 and ~14 minors [RESOLVED
  2026-06-10] against codegen 974cda7 (UNSUPPORTED_CONST_NAMESPACES,
  chart.bg/fg_color, bare ta.<name> reads, export, derived syminfo warning
  set, str.replace/timestamp//=/%=/string()/int()/openprofit_percent/
  country-GB/TZ/round_to_mintick/array fixes); headline 1 critical + ~40
  minor; next steps now point at first_index, size() int typing, O4/O5/O7,
  and corpus probe gaps
- pine_v6_coverage_detail: surgical row refresh (varip/footprint/
  volume_row/color()/color.from_gradient/max_bars_back/
  timeframe.from_seconds/indicator()/array.new_* drawing types ->
  hard-reject; syminfo metadata -> get_syminfo_metadata; prefix derived;
  tz-aware bare time vars; input.source/color/time backings); bucket
  totals re-counted by delta (181/228/216/175/141) with counting-basis
  footnote; staleness banner replaced by 2026-06-10 reconciliation note
- corpus: bump submodule for README category-table reconciliation
  (ta=61, mtf=16, input=2; per-row report sums)

Co-authored-by: Cursor <[email protected]>
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.

1 participant