Date: 2026-09-04
Status: approved approach (option 1, standalone script); spec pending Daniel's review
Parents: avwap_zscore_band.pine (AVWAP Z-Score Band [Stage-0] v38), playbook_indicator.pine (AVWAPxAKAO Playbook [PBK] v4)
Deliverable: ztd_divergence.pine — one new TradingView indicator, saved on Daniel's account as
"AVWAP-Z x PBK Zero-Touch Divergence [ZTD]", short title ZTD.
A same-bar, non-repainting divergence detector that combines the two parents' outputs into one rule. Daniel's rule, upper side (lower side mirrors it):
Neither parent alone can do this: AVWAP-Z has no view of PBK's Rolling-24 z, and PBK has no Auto-anchor triangle state. Reading the parents through source inputs was rejected because it breaks Daniel's zero-configuration requirement, so the script recomputes both z measures.
Both parents compute the same thing: volume-weighted anchored VWAP of hlc3, spread
close − basis, sd = ta.stdev(spread, sigmaLen), z = spread / sd, and tier thresholds
either fixed (2.0 / 2.5) or percentile-calibrated (ta.percentile_nearest_rank(|z|, calibLen, p)).
They differ only in anchor set and defaults.
ZTD carries one engine block, written out twice at top level (Pine function-parameter
history is unreliable per tradingview-mcp-quirks; every ta.* call stays unconditional at
top level). Each instance has its own input group:
| Instance | Role | Anchor default | Threshold default | Anchor options |
|---|---|---|---|---|
| T (triangle engine) | reproduces AVWAP-Z triangles | Rolling (24-bar) | Percentile 90 / 95 over 2000 bars | Auto, Auto (24-bar), Rolling (24-bar), Swing, Flip, Daily, 4 Hour, Weekly, Monthly, Quarterly, Yearly |
| O (oscillator engine) | reproduces PBK's pane z line | Rolling (24-bar) | Percentile 90 / 95 over 2000 bars | same list |
Shared inputs: sigma lookback (200), volume weighting (on), Swing pivot strength (5), Flip debounce (5).
Defaults mirror Daniel's live chart instances (read 2026-09-04 from the EURUSD 1h layout, not the
scripts' stock defaults): AVWAP-Z runs Rolling (24-bar) / Percentile / sigma 200, and PBK's z layer runs
Rolling (24-bar) / Percentile / sigma 200. Consequence: with defaults, zT and zO are the same series,
so the arm condition reduces to "z at or beyond the extreme percentile"; the two engines stay separate so
either side can be re-anchored (Swing, Flip, Daily) without touching the other. The Fixed date anchor mode is dropped (irrelevant to the rule).
Engine outputs per instance: basis, z, thrElev, thrExt, state ∈ {−2,−1,0,1,2}
using exactly AVWAP-Z's classification (z >= thrExt → 2, z >= thrElev → 1, mirrored).
Agreement requirement: with T set like the chart's AVWAP-Z instance and O set like the
chart's PBK instance, ZTD's zT and zO series must match the parents' exported plots to
1e-9 at every bar. This is a verification gate, not a hope.
triBear = stateT >= minTier where minTier input is Elevated (1, default) or Extreme (2).
triBull = stateT <= −minTier.obLevel = obTier == "Extreme" ? thrExtO : thrElevO (default Extreme).
overbought = zO >= obLevel, oversold = zO <= −obLevel.armBear = triBear and overbought, armBull = triBull and oversold.zeroTouchFromAbove = zO <= zeroTol, zeroTouchFromBelow = zO >= −zeroTol
(zeroTol input, default 0.0, so "touch" means at or through zero).tfOk = not chartGate or timeframe.period in {"60", "240", "D"} (input default on).Daniel (2026-09-05): "get the extreme in oscillator and extreme in price as per the 2 negative/positive ranges of the oscillator which are on either side of the zero line."
Ranges. A range is one excursion of the oscillator on one side of zero, bounded by zero
touches. On the bear side a bar belongs to a positive range while zOzb > zeroTol; the range ends
on the bar where zOzb <= zeroTol (the zero touch; that bar is outside the range, so its own high/low do NOT count — the range extreme is the previous bar's running value) — membership is
the exact complement of the touch, so a range never starts and ends on one bar. zOzb is the close-based
z by default (input "Zero touch / range reading"); in intrabar mode with "Bar extreme" it is z of the
low (bull: high). Arming, peaks and break comparisons use zOb = z of the high in intrabar mode.
Range extremes are taken over ALL bars of the range: highest high and highest z (bear),
lowest low and lowest z (bull). A range is qualified if any of its bars arms (triBear and zOb >= obLevel, and the chart-timeframe gate passes).
States: IDLE (0), RANGE 1 (1) = a qualified range is open, LOCKED (2) = range 1 is final
and the next same-side range is awaited, RANGE 2 (3) = the comparison range is open.
Per bar, in this order:
bar_index − since > timeoutBars → IDLE (range tracking continues; the range
may still qualify and become range 1 at its end).curTri, curOB, curQual). If the side was LOCKED, the new range is
range 2 (→ RANGE 2, fired := 0, contra := false). Otherwise update running extremes.
Then curTri |= triBear, curOB |= zOb >= obLevel, and on an arm curQual := true
(IDLE → RANGE 1).curHi > refHigh and curPk < peakHi → RDIV−;
else curPk > peakHi and curHi < refHigh → HDIV−. The values used are snapshotted
to f* plots and to the drawings.curPk >= peakHi;
hidden: curHi >= refHigh. The label is marked X and dimmed, never deleted.! prefix.dz; a new price extreme raises the extended alert with the new price.flag = attention(1) + contradicted(2).
If the range qualified it becomes range 1 (refHigh/peakHi := cur*, → LOCKED); otherwise
→ IDLE. fired/contra reset.The opposite side arming does not by itself invalidate a pending comparison (a negative range between
two positive ranges is the normal path). Two later additions (2026-09-05): reference lifetime — by
default a qualified range 1 stays the reference for every later same-side range until a new qualified
range replaces it or the timeout (240 bars from the lock) expires, at most one signal per reference
(inputs allow next-range-only and unlimited); and opposite-extreme cancel (input, default OFF since v21 on 2026-09-14; on until then) - if the
oscillator reaches the other side's OB/OS level while the reference stands, the reference is kept and
marked (~opp on the labels and HUD, cancelB/cancelU = 2 in the data window); with the input on the
reference is dropped instead (cancelB/cancelU = 1 on that bar). One signal per side per range 2. A range 2 that qualifies is
automatically range 1 for the next comparison, so consecutive divergences chain.
Price pane (force_overlay=true, indicator is overlay=false):
RDIV − / HDIV − red above high; RDIV + / HDIV + teal
below low. Text shows the trigger price (the bar's high / low, format.mintick), the
reference price it is measured against (refHigh / refLow), and Δz = zO − peakO to
two decimals. (Daniel, 2026-09-04: the price where the divergence occurs must be readable.)(refBar, refHigh) to (bar_index, high) for bearish signals
(mirror for bullish). Regular lines solid, hidden lines dotted.triBear and zO >= obLevel, mirror below),
print AVWAP-Z's triangle in AVWAP-Z's colours (extreme red / elevated orange above the bar,
extreme teal / elevated blue below), size small. These are exactly the bars that arm a cycle
(before the chart gate), so the user sees the cluster the rule is tracking. (Daniel,
2026-09-04.)! when range 2 printed a triangle/OB reading and X (dimmed) when range 2
later contradicted the divergence (2026-09-05).npKeepLbl.Indicator pane:
zO line coloured by tier (grey / orange / red), ±thrElevO silver, ±thrExtO red,
zero dotted — visually identical to PBK's pane so the user recognises it.(peakBar, peakO) reading peak with the value;
deleted when the cycle resolves or is abandoned.(peakBar, peakO) to (bar_index, zO),
same colour and solid/dotted convention as the price-pane line, plus a label at
(bar_index, zO) reading the signal name and the trigger price, so the divergence is
visible on both panes. Both are FIFO-capped by "Recent signals kept", separately from the
price-pane drawings. (Daniel, 2026-09-04.)HUD table (top right, default on), one row per side: state name, bars in state, ref price,
peak z, plus a header row showing the two resolved anchors ("T: Daily · O: Roll 24") and
the chart-gate verdict.
Data window plots: zT, stateT, zO, bearState, bullState, refHigh, refLow,
peakHi, peakLo, sig. All hidden from the pane (display.data_window). These are the
export surface for verification.
Alerts (alertcondition): ZTD: regular bearish, ZTD: regular bullish,
ZTD: hidden bearish, ZTD: hidden bullish, ZTD: cycle locked (zero touch) (either side).
| Group | Input | Default |
|---|---|---|
| Triangle engine (AVWAP-Z) | Anchor mode | Rolling (24-bar) |
| Threshold mode / Elev / Ext / calib / pctElev / pctExt | Percentile / 2.0 / 2.5 / 2000 / 90 / 95 | |
| Triangle tier that arms | Elevated | |
| Oscillator engine (PBK) | Anchor mode | Rolling (24-bar) |
| Threshold mode / Elev / Ext / calib / pctElev / pctExt | Percentile / 2.0 / 2.5 / 2000 / 90 / 95 | |
| Overbought/oversold tier | Extreme | |
| Shared | Sigma lookback; Volume-weight; Swing pivot strength; Flip debounce | 200; on; 5; 5 |
| Rule | Only fire on 1h / 4h / D charts | on |
| Cycle timeout (bars) | 120 | |
| Zero-line tolerance (z) | 0.0 | |
| Display | Show labels; Show lines; Show overlap triangles; Show all T triangles; Show HUD; Recent signals kept | on; on; on; off; on; 12 |
No per-symbol settings. Anchor Auto resolves per chart timeframe exactly as in AVWAP-Z.
Every decision uses the current bar's confirmed high, low, close, volume and the two z
series computed from them. No request.security, no lookahead, no pivot-based labels placed
on past bars except the reference line's start point, which is drawn from a bar whose values
were fixed when it was recorded. Swing/Flip anchors inherit AVWAP-Z's retro-placement, which
alters only future basis values. Replay must reproduce the exported series exactly (the
established verifier).
na in zT, zO or thresholds forces that bar to skip arm/resolve; state is
preserved but the timeout clock keeps running.na / 0) falls back to weight 1.0, as in both parents.ta.percentile_nearest_rank returns na until
calibLen bars exist; the rule simply stays idle (same as AVWAP-Z's behaviour).max_labels_count=200, max_lines_count=200 plus the FIFO cap.zT, stateT, zO and the parents' Z-score,
State, z plots through CDP (pyakao/validation/export_tv.py pattern) and diff. Pass =
max abs difference < 1e-9 and identical state codes on every bar.stateT, zO, high, low series into a
~60-line Python reference implementation of §2.3 and diff its signal series against the
exported sig plot. Pass = identical. (DEFERRED 2026-09-04 by Daniel; the shipped gate is an
invariant audit. An unshipped independent replay in the final review matched 0 mismatches on
21,150 bars.)sig
and state plots at matched bars must equal the exported history.No trade-outcome claims are made; this is a marker. Forward observation as with PBK.
A TPO "naked POC zone" layer, ported and trimmed from akao_confluence_engine.pine's naked-POC
module, adds confluence to the divergence marker above (ztd_divergence.pine only).
[low, high] touched. Bucket: auto (~1.5bp of price, 1-2-5 snap,
frozen on the first bar) or manual. Value area = a configurable % of TPOs grown from the POC
outward, tie → up. No Monthly profile.timeframe.in_seconds(): chart TF shorter than the block runs a
commit-then-reset tracker; chart TF equal to the block commits the bar's own high/low; chart TF
longer than the block pulls completed intrabars via request.security_lower_tf.nPOC-D / nPOC-W), both → the ASCII tag nPOC x2; a live
POC within max(cross buffer, half a bucket) of the extreme adds a POC-touch flag (a *
suffix). The result replaces the label's previous tag rather than stacking.