Warrior_EA/docs/Wyckoff/WYCKOFF_STRATEGIES_AND_TEMPLATES.md
2026-10-05 21:58:23 -04:00

8.5 KiB

Wyckoff strategies and builder templates

Two artefacts per setup, generated from one spec so they can never drift apart:

Where What it is
Strategy user/strategies/wyckoff/WYK_*.sqx The book setup, and nothing else. Zero random blocks, zero random groups, no allowRandom. This is the thing you read, backtest and argue with.
Template user/settings/StrategyTemplates/Wyckoff/WYK_*.sqx The same book setup plus one RandomCondition slot, its paired NegatedCondition, and builder headroom on the entry/exit stack. This is what you point the Builder at.

Both trees are written to the repo and to the install (SQX_144_2953_win_20260601), byte-identical.


The eight setups

Sources: docs/SQX/SQX_Wyckoff_Workflows.md (the verified entry/exit tables), Villahermosa La Méthodologie Wyckoff en Profondeur (wdepth) and Wyckoff 2.0 (w20).

Strategy Book Long Short Entry SL / PT
WYK_ClimaxReversal W4, wdepth Ch.15 Phase A + selling climax Phase A + buying climax market 1.5 / 3.0 ×ATR
WYK_SpringShakeout W3/W1-1, wdepth Ch.18 Phase C + Spring + prior event was ST Phase C + Upthrust + prior event was bearish ST limit @ signal-bar low / high 2.0 / 3.0
WYK_SOSBreakout W1-2, wdepth Ch.19 Phase D + SOS + (close above creek or range floor) Phase D + SOW + (close below creek or range floor) market 2.0 / 3.0
WYK_SOSBreakoutLevelStop W5-3, wdepth Ch.19 Phase ≥D + SOS Phase ≥D + SOW stop @ signal-bar high / low 2.0 / 3.0
WYK_LPSBackup W1-3/W5-1, wdepth Ch.20 Phase D + LPS + close above creek Phase D + LPSY + close below creek limit @ signal-bar low / high 1.5 / 3.0
WYK_ReaccumulationContinuation W2, wdepth Ch.11 p78 + p16 Phase ≥D + reaccumulation + SOS Phase ≥D + redistribution + SOW market 2.0 / 3.0
WYK_FailedStructureCHoCH W7, w20 §1.4–1.5 Phase ≥D + WFS zero-cross up Phase ≥D + WFS zero-cross down market 2.0 / 3.0
WYK_SOTExhaustion w20 §1.6 pp.26-27, 41-42 SOTConfirmation crosses up through 0 SOTConfirmation crosses down through 0 market 2.0 / 3.0

Every PT is ≥1.5× the SL, which is the minimum R:R the workflow tables call for. Break-even at 1×ATR everywhere; trailing stop 1.5–2×ATR activated at 1×ATR on the continuation setups, off on WYK_ClimaxReversal (Ch.15 wants a tight stop against wide-range climax bars, not a trail).

Polarity decisions that were checked against source, not guessed

  • SOTConfirmation is signed. ADShorteningOfThrust.java:475 — pendingConfirmSignal = (sot.sotDir == -1) ? 1.0 : -1.0. A shortening run to the downside emits +1, so the long takes the up-cross through zero.
  • ADWyckoffFailedStructure's signed Value is bullish-positive. ADWyckoffFailedStructure.java:205,238 — bullStructuralFailure is §1.5.2 "lack of weakness" and adds to the signed value; bearStructuralFailure is §1.5.1 "lack of strength" and subtracts. So the long is the up-cross.
  • Event codes and phases come from the install's own combo enumerations (PS=1 … SOS=7, PSY=-1 … SOW=-7; A=1 … E=5), not from memory.

Where the book could not be followed exactly

The workflow tables anchor stops and targets to ZoneTop / ZoneBottom / EventPrice. This install exposes no Wyckoff price-level block — the one price-typed Wyckoff block, ADWyckoffEventStream, returns an event code, not a price. So SL/PT are ATR multiples, which is what the same tables use for the break-even and trailing legs anyway. docs/SQX/SQX_Wyckoff_Workflows.md flags this limitation itself ("Named level blocks … do NOT exist").

WYK_ReaccumulationContinuation gates on Phase ≥D rather than the table's Phase ≥E: the SOS is the Phase D event that resolves the range (wdepth pp.172-174), so requiring E would put the entry after the move it is meant to catch.


What makes the template a template

One random block, not a scattering of them — SQX expands a single RandomCondition into as many blocks as the Builder is configured to use:

long  signal :  <book setup>  AND  RandomCondition(id, group)
short signal :  <book setup>  AND  NegatedCondition(id)

The short side carries explicit bearish book logic rather than relying on the mirror to invert the long side. That matters here: no block in this install declares an oppositeBlockKey, so nothing guarantees IsSpring would mirror to IsUpThrust. Writing both sides out means the setup is correct regardless of how SQX resolves the negation.

Each template draws its random slot from the curated Wyckoff pool that accompanies its setup — Wyckoff_Context_Bull for the range setups, Wyckoff_Phase_Filter for the breakouts, Wyckoff_Recent_Bull for continuation, Wyckoff_Structure_Bull for CHoCH, Wyckoff_SOT_Bull for exhaustion. Group ids are resolved from the install's blockGroups.xml, so importing a template will not fork a duplicate group.

Headroom (generate="random" on the long leg, "same" on the short):

  • entry/exit stack — stop loss, profit target, break-even, trailing stop and activation, exit-after-bars, and bars-valid on the pending-order setups;
  • the Wyckoff engine parameters — lookback, zigzag strength, AR legs, boundary tolerance, max range bars, plus SOT lookback/threshold/min-impulses.

Never randomised: #Phase#, #TargetEventCode#, #Slot#, #SignalSeries#, #ContextMode#, #SessionType#. Those Selection params are the book setup, and giving a Selection param numeric bounds also trips SQX's parseDouble NPE.

All eight ADWyckoff blocks in a given file share one set of engine parameters — otherwise the blocks would be reading different state machines and the "book" logic would be comparing unrelated ranges. The check pass enforces this.


Regenerating and checking

python -m scripts.wyckoff_builder.build            # repo only
python -m scripts.wyckoff_builder.build --install  # repo + SQX install
python -m scripts.wyckoff_builder.check            # the check pass

scripts/wyckoff_builder/ holds the generator. Blocks are materialized straight out of the install's config.xml, so parameter names, types, ranges and defaults cannot be invented; groups are resolved out of blockGroups.xml. Rules are transplanted into a stripped skeleton (skeleton/) that preserves SQX's signal-variable protocol — the four boolean variables (33333333-1111-1111-… long entry, -2222-1111- short entry, -1111-2222- long exit, -2222-2222- short exit) and the If trees that consume them.

check.py verifies, for all 16 files: archive shape; name/StrategyName/filename agreement; allowRandom present on templates and absent on strategies; the signal variable wiring intact; strategies containing zero random blocks or groups; templates containing exactly one RandomCondition and one paired NegatedCondition, the random slot on the long side and the negated one on the short; embedded group ids matching the install; every block key existing in config.xml; engine parameters agreeing file-wide; typed params holding values of their declared type; semantic params never randomised; correct entry direction; SL and PT both present with R:R ≥1.5; and pending-order prices pointing the right way (buy-stop above / buy-limit below, and the mirror for shorts).

The set also passes sqx-lab's own template validator (skills/sqx-strategy-template/engine/generate.py::_validate): 16/16, no unresolved blocks, no dangling group refs, no trigger leaking into an entry rule.


What was replaced

The previous user/strategies/wyckoff/WYK_*.sqx were not strategies. Each was a template shell filed in the strategies folder whose entire long signal was RandomCondition AND RandomCondition and whose short was NegatedCondition AND NegatedCondition — no Wyckoff block appeared anywhere in the rules, so they were indistinguishable from plain random generation. They also had no stop loss and no profit target (both SQ.Formulas.SLPT.None), empty exit signals and ExitAfterBars = 0, which left a position with no exit at all; and #ExitAfterBars.ExitAfterBars#, an int param, held the string EnterAtMarket1.

Four whose names no longer map to a setup (WYK_SOSBreakoutContext, WYK_SOSBreakoutBookLevelStop, WYK_SpringShakeoutVetoStructure, WYK_SOTExhaustionVetoEvent) were removed from the repo and moved to user/strategies/wyckoff-stale-<timestamp>/ in the install.