Warrior_EA/docs/Wyckoff/WYCKOFF_STRATEGIES_AND_TEMPLATES.md

151 lines
8.5 KiB
Markdown

# 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
```bash
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.