CAT researchOverviewEURUSDGBPUSDXAUUSDGBPCADEURCADAUDJPYGBPJPYResearch notes

ZMS Python port — parity against TradingView (2026-08-27)

Question asked: barring the EURGBP data gap, has a faithful port to Python actually happened?

Answer: yes, confirmed — with one operational condition. Once the percentile window is full the Python port reproduces TradingView exactly: 599 consecutive bars, zero state mismatches, z agreeing to 5e-09, which is the precision the export was rounded to. Before the window fills, states legitimately differ, and that is a warm-up property rather than a defect.

Method

Fidelity is only meaningful if the two engines see identical input, so TradingView's own bars were exported alongside its computed columns and fed into the Python port. Any difference is then purely implementation.

Exported over CDP from the live EURUSD 5m chart (2026-08-11 → 2026-08-26, 2,723 bars, strictly increasing, 2,720 of 2,722 gaps exactly 300s — the two exceptions are weekends):

time, open, high, low, close, volume, z_chart, z_h1, thr_chart, state_chart, t_h1

Results

HTF grid alignment — the risk that was flagged and is now closed

t_h1: 2,718 exact matches, 0 mismatches. Calibrated offset 0s.

This was the largest known unknown. TradingView aligns intraday HTF bars to the venue session rather than midnight UTC, so an off-by-one here would have silently shifted every H1/H4 bias. It does not.

Core engine

column bars compared max abs diff
z chart (5m) 2,523 5.0e-09
thrX chart (late window) 523 4.9e-09
z H1 1,254 9.0e-05

z and the threshold match to the export's rounding precision — i.e. exactly. The H1 leg is looser (median 3.5e-06) because Python resamples H1 from a 2,723-bar chart buffer whose first H1 bar is partial, while TradingView aggregates H1 natively over deeper history. It is a windowing artifact, not a formula difference, and at 9e-05 it cannot move a state: the closest |z| ever came to its threshold in this sample was 2 bars out of 2,523 within 1e-4, against a median margin of 1.78.

The state discrepancy, and its cause

A first pass showed 106 state mismatches out of 2,523 (4.2%) — investigated rather than waved off, because states are what generate signals.

bar range state mismatches threshold max diff
200–800 (window cold) 10.83% 1.230
800–1,400 3.17% 0.512
1,400–2,124 3.04% 0.337
2,124+ (window FULL) 0.00% 4.9e-09

The pattern is monotone and it terminates exactly where the theory says it should. Python accumulates |z| from ~bar 124, so its 2,000-wide percentile window fills at ~bar 2,124. Before that its thresholds are computed over a shorter effective window than TradingView's, which has been running over far more history than any exportable buffer retains — the "study origins are not client-reproducible" property already documented in this project.

Beyond bar 2,124: 599 bars compared, 0 state mismatches, z max diff 5e-09.

What this does and does not establish

Established: the z engine, the rolling-24 basis, the percentile thresholds, the tier logic and the HTF completed-bar alignment are faithful. Combined with the existing 129 tests — including the pooled random-walk guard (PF 0.912 over ~700 trades, no look-ahead) — the signal layer is trustworthy.

Not established: H4 could not be compared, because 2,723 chart bars yield only ~57 H4 bars against the 100 that ta.stdev needs, so Python's H4 leg is na throughout this window while TradingView's is warm. Trade-level P&L parity remains explicitly out of scope (TV's intrabar ordering is undocumented; the fill model here is switchable instead).

Consequence for an earlier open question: the H4-only configuration reading PF 0.40 on TradingView's 6.7-month window versus 1.429 on five years in Python is now attributable to the window, not to a port discrepancy. That was previously an assumption; it is now supported.

Operational rule this produced

Give the harness at least calib_len + sigma_len + 24 ≈ 2,124 bars of warm-up before treating its states as settled. run_zms now reports warmup["chart_percentile"] and warns when a run is too short, in the same way it already warns about an under-fed HTF leg.

This does not affect the long runs: 5 years of 5m EURUSD is 373,657 bars, so warm-up is 0.6% of the sample and the results in zms_h4only_prereg_2026-08-27.md stand.


ADDENDUM — 15m chart, state machines, and a REAL BUG in the H4 grid

Asked whether the 5m and 15m signals are faithful. Testing 15m closed two gaps and exposed one defect.

15m chart timeframe — faithful

TradingView's own 15m bars (2,715, EURUSD) fed into the port, compared over the 591 bars where Python's percentile window is full:

column max abs diff mismatches
z chart 5.0e-08
thrX chart 4.9e-08
state chart 0 / 591
t_h1 grid 2,711 / 2,711 exact

Same result as 5m: the chart-timeframe engine is exact once warm.

The bug: TradingView's H4 grid is NOT on midnight-UTC boundaries

t_h4 matched 0 of 2,711. TV's H4 bars open at 01:00 / 05:00 / 09:00 / 13:00 / 17:00 / 21:00 UTC — every H4 open time is congruent to 3600s mod 14400. TV aligns intraday HTF bars to the venue session, which is exactly the risk flagged in the main document — and I then defaulted htf_offset_s to (0, 0) and shipped it.

After calibrating the offset from TV's own stamps:

before (offset 0) after (offset 3600s)
t_h4 grid 0 / 2,711 2,707 / 2,707 exact
z H4 max diff 1.153 1.52e-05

Consequence: the H4-only numbers in the screen were computed on the wrong grid

Grid alignment is not a detail. On 5 years of EURUSD 5m, H4-only, changing only the H4 boundary:

H4 offset n PF ex-outlier PF net pips best month % half 1 half 2
0s (what the screen used) 295 1.429 1.395 +2,864 34.5 1.490 1.378
3600s (TradingView's) 273 1.136 1.100 +891 91.9 1.054 1.211
7200s 318 1.459 1.409 +3,411 27.2 1.461 1.457
10800s 326 1.323 1.292 +2,414 46.1 1.192 1.446

Profit factor swings 1.136 → 1.459 and month concentration 27% → 92% purely from where the H4 bar is deemed to start. That is a bookkeeping choice, not a market fact.

At TradingView's actual offset the H4-only candidate fails outright — PF 1.136 with 91.9% of the net in a single month. The PF 1.429 reported in zms_h4only_prereg_2026-08-27.md was an artifact of an arbitrary grid. The screen's FAIL verdict stands and is now better supported, but the EURUSD and GBPUSD "PASS" rows in it must be read as computed on a non-TradingView H4 grid.

The default configuration is far less sensitive (PF 1.109 at 0s vs 1.129 at 3600s) because H1 dominates it and the H1 offset is genuinely 0.

What changed as a result

  • run_zms now warns whenever the H4 leg runs with a zero grid offset.
  • Two tests lock the behaviour: the advisory fires, and different offsets produce different bias streams.
  • Standing rule: calibrate the HTF grid per venue with calibrate_offset(), or report results across offsets. Never accept the default silently.

Still not verified

The state machines (bias episodes, arm/ready, Path 1/2, fill) remain uncompared. Over the 591-bar warm window TradingView produced 5 event bars and Python 0 — but that comparison is not yet meaningful, because Python's HTF percentile windows are far shallower than TV's on a 2,715-bar buffer (H1: ~579 values vs 2,000; H4: ~71 vs 2,000), so the H1/H4 thresholds still differ and the bias stream diverges. state H1 mismatched 24/591 and state H4 96/591 for that reason, not because of alignment.

Verifying the state machines needs a TradingView export whose HTF legs are as deep as Python's — i.e. a much longer chart buffer — or feeding Python years of matching bars and comparing only the overlap. That is the next parity task and it is not yet done.


ADDENDUM 2 — H1 triangles, the bias machine and the PBK divergence detector: EXACT

Asked to verify the H1 triangles and the PBK divergences on H1 and H4, and to check whether the indicators were ported correctly at all.

The method that unblocked it

Earlier attempts compared H1/H4 from a 5m or 15m chart, where TradingView's request.security pulls far deeper HTF history than Python can rebuild by aggregating chart bars (TV reported cal_h1 = 2000 on a 15m chart holding only 679 H1 bars). Thresholds therefore differed and every downstream comparison was polluted.

Running on a 1h chart with H4 disabled removes the asymmetry: the H1 leg is the chart timeframe, so both engines have the same deep series. EURUSD 1h, 2,716 bars, compared over the 592 bars where both percentile windows are full.

Results — everything matches exactly

quantity TradingView Python agreement
z chart max diff 5.0e-08
thr chart max diff 4.8e-08
z H1 max diff 5.0e-08
thr H1 max diff 4.8e-08
H1 triangles (state ±2) 41 41 41 agreeing, 0 mismatches
H1 episode starts 13 13 13 exact bar + sign
H1 bias deaths 18 18 18 exact bar + sign
H1 divergence swings 22 22 22 exact bar + sign
driver key 0 mismatches / 592 bars

All differences are at the precision the export was rounded to (7 decimals). This is exact agreement.

What that establishes

  • H1 triangles are faithful. 41 for 41, no false positives, no misses.
  • The PBK / AKAO divergence detector is faithful. 22 divergence swings, every one on the same bar with the same direction. That is a strong test of the oscillator port specifically: a divergence fires only when the oscillator at the new swing compares correctly against the value recorded at the triangle swing, so 22/22 requires the rational-quadratic kernel, the ATR normalisation, the reference-swing memory and the ratchet all to be right simultaneously.
  • The bias episode machine is faithful — episode starts, the ratcheting stop, the expiry rules and driver resolution all reproduce exactly.

On H4 divergences specifically: the divergence code is timeframe-agnostic — bias.py runs one loop for t in (0, 1) with identical logic for both legs. Verifying H1 exactly therefore verifies the logic for H4 as well; only H4's inputs (thresholds computed over history Python cannot see from a chart buffer) differ.

What remains unverified, and why it is structural

The setup machine — arm / ready / Path 1 / Path 2 / fill. On a 1h chart TradingView produced zero setup events, and correctly so: the setup needs a chart-timeframe counter-impulse against the bias, and when the chart timeframe is the bias timeframe no such impulse can form. Verifying it needs chart TF ≪ bias TF, which immediately reintroduces the shallow-HTF-window problem.

That is a genuine dead end for chart-export parity, not an oversight:

  • TV's request.security always has deeper HTF history than the chart buffer exposes, so Python cannot reconstruct TV's HTF thresholds from exported bars at any chart timeframe below the bias timeframe.
  • The setup machine only produces events at exactly those timeframe combinations.

Closing it requires feeding Python a long independent series (Tickstory) and comparing against TradingView on the same instrument over the overlap, accepting broker-feed differences — indicative rather than exact — or verifying the setup machine by unit test and code review instead of by diff. The existing suite already covers its transitions on synthetic data; that is not TradingView parity and is not claimed as such.

Threshold convergence — the earlier claim, now measured

The shallow-window explanation was asserted before it was tested. On 5 years of Tickstory 15m (124,589 bars, H1/H4 windows fully deep):

TradingView Python (deep history)
thr H1 2.1854 median 2.2574 (range 2.07–2.44)
thr H4 2.2141 median 2.2397 (range 2.16–2.31)

Against differences of 0.40 (H1) and 1.05 (H4) on a chart-sized buffer. On a different broker feed the deep-history values land inside TradingView's range, which is what convergence looks like.

Standing summary of port fidelity

layer status
chart-TF engine (5m, 15m, 1h) exact
HTF grid alignment exact once calibrated (H4 needs offset 3600s)
H1 leg z + thresholds exact with equal history depth
H1 triangles exact (41/41)
AKAO oscillator + divergence detector exact (22/22 swings)
bias episode machine + driver exact (13/13 starts, 18/18 deaths, 0 driver mismatches)
H4 leg values close (1.5e-05); thresholds depth-limited, converge with history
setup machine (arm/ready/path/fill) unverified — structurally blocked from chart exports
trade/P&L parity out of scope by design