@battlegrid/mcp-server 31.2.5 → 31.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,270 @@
1
+ ---
2
+ name: battlegrid-strategy-examples
3
+ description: Full-surface composition patterns for the strategy studio — validated desk-grade examples of custom report sections, benchmark sections, condition trees with verdicts and enforcement gates, tiered signal weights, routing gates, ATR trade levels, and position management. Activate beside strategy-authoring whenever a strategy is being composed or upgraded beyond a basic template.
4
+ ---
5
+
6
+ # Strategy Studio — full-power composition patterns
7
+
8
+ `strategy-authoring` owns the flow (evidence → locked spec → discover → compile → review →
9
+ apply). This skill owns **what to compose**: a default build — a few platform sections, no
10
+ conditions, untouched weights — wastes the studio. The playbooks below are compiled in CI, so their
11
+ shapes are binding in the sense that matters — they are checked, not merely asserted. Loose tokens
12
+ elsewhere in this file are not covered by that gate, and vocabulary moves with deploys, so discovery
13
+ in the conversation stays the authority — prefer what `list_strategy_vocabulary`,
14
+ `get_strategy_column_contract`, and `get_strategy_signal_definition` return over anything
15
+ printed here, and read exact headers from a preview's `conditionColumns` before conditioning on
16
+ them.
17
+
18
+ ## The full-power checklist
19
+
20
+ Before compiling a CREATE, every "no" here is a decision to state, not an omission:
21
+
22
+ 1. At least one **custom section** whose columns encode the thesis — not only platform modules.
23
+ 2. **Conditions** encode the entry logic: building blocks (`verdict: null`) + verdict carriers,
24
+ and at least one `required: true` condition vetoing obvious disqualifiers **before any
25
+ billing or LLM call**.
26
+ 3. **Every signal meant to score is named in `rules`** with a deliberate tier — unnamed signals
27
+ keep server defaults (typically Off). Verify in the compiled scorecard, never assume.
28
+ 4. **Gates** (`minAggregateScore`, `minRequiredCount`, `minAtrPct`) are computed against the
29
+ chosen weight budget, not guessed.
30
+ 5. **Trade levels + position management** match the setup's geometry and holding period.
31
+ 6. `marketReadText` states standing orders with `{...}` markers so live values render inline.
32
+
33
+ ## Header grammar (system-generated — never named by the author)
34
+
35
+ | Transform | Header | Validated example |
36
+ |---|---|---|
37
+ | `value` | `<code>` | `bbWidthPct`, `RVOL`, `rate` |
38
+ | `trajectory` w4 | `<code>_t3…_t1`, `_now`, `_trend` (rising/falling/flat) | `RSI14_now`, `RSI14_trend` |
39
+ | `distance` | `dist_<code>` (signed % from price) | `dist_SMA50` |
40
+ | `spread` | `<base>_<operand>_spread` | `mark_oracle_spread` |
41
+ | `aggregate` wN | `<code>_mean<N>` | `rate_mean24` |
42
+ | `rank` | `<code>_rank_<hi\|lo\|far\|near>` — ordinal, `lte N` = top-N | `bbWidthPct_rank_lo` |
43
+ | `efficiency` wN | `<code>_er` (1 straight, ~0 chop) | `close_ltf_er` |
44
+ | `maxShare` wN | `<code>_maxShare` | `volBase_ltf_maxShare` |
45
+ | `classifyZone` / `classifyState` | `<code>_zone` / `<code>_state` | `RSI14_zone`, `ADX_state` |
46
+
47
+ Non-anchor rungs affix `_ltf` (lower) / `_htf` (regime): `MAalign_htf`,
48
+ `zones_htf_support_dist`. Chains are bounded at two: inner `distance`/`spread` → outer
49
+ `trajectory`/`aggregate`/`efficiency`/`maxShare`/`rank` (`EMA5 spread EMA13 × trajectory` →
50
+ `EMA5_EMA13_spread_now` + `_trend`).
51
+
52
+ **Timeframe references are two families.** *Relative* (`anchor`/`lower`/`regime`) re-resolve
53
+ when the strategy timeframe changes — `regime` is the anchor's ladder successor (1d for a 4h
54
+ anchor). *Pinned* (`{abs: "<tf>"}`) is fixed, ignores anchor retunes, suffixes the literal
55
+ (`RSI14_1d`, `dist_SMA200_1d`, `MAalign_1d` — validated), and binds to discovery's
56
+ `rankedTimeframes`, a **superset** of the authorable anchor set — `{abs: "1d"}` is valid while
57
+ `1d` is not an anchor. `offset: 1` on a pinned `value` column reads the last **closed** bar of
58
+ that timeframe (`RSI14[t - 1]`) — the deterministic daily-close read; offset 0 reads the
59
+ forming bar (provisional), and offset does not change the header, so one offset per
60
+ `metric × timeframe` per section. **Daily-strategy pattern:** anchor 4h, pinned-1d thesis
61
+ columns at `offset: 1`, `required: true` daily conditions gating every carrier, anchor-rung
62
+ columns only for entry timing and risk — daily inputs then move once per daily close while
63
+ stops and time decay keep managing intraday. **Benchmark sections** (`benchmarkTicker: "BTC"`,
64
+ required-nullable on every custom section) read the benchmark's values on every row — the
65
+ standard market-leader regime gate. Platform `sectionKey`s are their literal keys
66
+ (`includeMtfConfluence`). Custom `sectionKey`s are the **server's** to issue, and which of the two
67
+ things you do depends on the operation: on a **CREATE, omit `sectionKey` entirely** — supplying one
68
+ is refused, since a new strategy owns no custom sections yet; on an **UPDATE or RESTORE, send back
69
+ the keys `get_strategy` returned** for the sections you are keeping. Either way you never invent
70
+ one. To section-qualify a duplicated header, read the key from a preview's `conditionColumns` or
71
+ from the qualified candidates a `CONDITION_COLUMN_AMBIGUOUS` refusal offers.
72
+
73
+ **Event columns print only on their event.** `MACD_cross` and `EMA5_13` (Bullish/Bearish) carry a
74
+ value on the crossing bar and are null on every other one, which reads as UNRESOLVED. Use an event
75
+ column as a TRIGGER inside a carrier, and pair it with a persistent state — a spread sign,
76
+ `MAalign` — for regime. A condition that treats an event column as a standing state is unresolved on
77
+ nearly every bar, which is a gate that never gates.
78
+
79
+ **`PDH` and `PDL` are anchored to `1d`.** They are catalogued price levels, so `dist_PDH gte 0`
80
+ composes directly — but bind them `{abs: '1d'}`, which is the only reference the save path accepts on
81
+ them, and read them from any anchor that way. (`distance` still rejects an `offset`, and a clause
82
+ still compares a column against a literal; neither of those shapes is what a previous-day level
83
+ needed.)
84
+
85
+ ## Conditions
86
+
87
+ `{ conditionKey, name, definition, verdict, required, exit, clock, closes }` — all eight
88
+ required, no defaults. Clauses: numeric/rank headers take `lt|lte|gte|gt|between`;
89
+ classification/direction headers take `is|in` with the served vocabulary. Groups:
90
+ `ALL | ANY | NOT | N_OF` (with `n`), depth ≤ 2. `conditionRef` composes named conditions (no
91
+ cycles; forward refs legal). `sectionKey: null` is sugar for a report-unique header only.
92
+ Verdicts: first TRUE carrier **in declaration order** decides UP/DOWN/NEITHER — order carriers
93
+ most-specific first; building blocks carry `null`. `required: true` = FALSE blocks compose-trade
94
+ before billing. Evaluation is three-valued: UNRESOLVED never collapses to FALSE; forming-bar
95
+ reads are provisional.
96
+
97
+ **The evidence clock.** `clock: "LIVE"` reads the forming bar; `clock: "CLOSE"` reads settled
98
+ bars, and `closes` is how many consecutive closed bars must read TRUE (1–5) — always `1` under
99
+ LIVE, which has exactly one frame. A CLOSE clock is legal **only** over a header resolved from
100
+ this coin's own candle series at offset 0. Frame-inert operands are refused
101
+ (`CONDITION_CLOCK_OPERAND_ILLEGAL`): perp-payload scalars, published rolling changes, ranks, zone
102
+ entities, MDS regime labels, enrichment metrics, session scalars, and any clause authored at a
103
+ non-zero offset. A closed frame cannot move them, so "held for N closes" would describe reads
104
+ that never happened. The remedy is a split, not a re-clock: move that clause into its own LIVE
105
+ condition and `conditionRef` it. **Worked liquidity floor:** `LIQUID_FLOOR` is LIVE because
106
+ `vol24hUsd` is a bundle scalar; the carrier that refs it may be CLOSE over its own candle-series
107
+ clauses.
108
+
109
+ **The lane a strategy is deployed to.** Report-level scalars split by LANE, and the split is not a
110
+ quality of the header — it is which reader runs. Market breadth and the reference pairs are ordinary
111
+ market-wide reads with no session dimension, so they resolve everywhere. The five **session-field**
112
+ scalars (`fieldPlayers`, `fieldUpBias`, `fieldBiasDir`, `captConc`, `picksSpread`) describe a game
113
+ session the agent is playing in, and **radar runs outside one** — so a radar deployment whose
114
+ strategy reads one is refused outright (`CONDITION_OPERAND_UNSERVED_IN_LANE`), naming the scalars it
115
+ can read instead. The refusal lands at DEPLOY rather than at save, because a strategy carries no lane
116
+ of its own: the same strategy is legal, and reads those scalars correctly, on an arena agent.
117
+
118
+ **The exit role.** `exit: true` makes a settled TRUE reading close open positions its verdict
119
+ opposes — UP exits SHORTs, DOWN exits LONGs, a NEITHER or `null` verdict exits both. Legal only
120
+ under `clock: "CLOSE"`: a LIVE reading is the forming bar, and an exit fired on one is an
121
+ intrabar exit. Orthogonal to `required` — the two act on disjoint lifecycles, pre-entry versus
122
+ open — so a condition may carry both, either, or neither.
123
+
124
+ **A state column is not a flip event.** `ST_DIR` reads the same on every bar of a trend, so
125
+ `ST_DIR is "bullish"` is a regime filter and never an entry signal. The flip needs an event column
126
+ beside it — an `EMA5_13` cross, or a `ST_DIR` trajectory whose `_trend` changes. The same distinction
127
+ applies to every persisting classification: `MAalign`, `ADX_state`, `zone`.
128
+
129
+ **Name the anchor when a metric is calibrated for one.** `KC_SQUEEZE is "on"` reads *on* about 57% of
130
+ 1h crypto bars even at canonical parameters — Bollinger σ is close-to-close while ATR captures
131
+ intrabar range, so σ/ATR runs low here — against about 19% at 15m. It is a selective filter at 15m and
132
+ below and close to useless at 1h. A metric whose selectivity depends on the anchor is stated with the
133
+ anchor, or the author gates on something that admits most bars.
134
+
135
+ ## Entry
136
+
137
+ `{ trigger, confirmTf, closes, bandAtrMultiple, levelSource, levelOffsetAtrMultiple,
138
+ validForBars }` — all seven required on every CREATE, no defaults. This axis is replaced WHOLE
139
+ on save, so an omitted key would silently revert an author's discipline rather than be refused.
140
+
141
+ **The trigger decides WHEN, and for two of them WHERE, an entry is taken.**
142
+
143
+ - `AT_SIGNAL` — fire the moment the radar observes the qualification flip, at whatever bar is on
144
+ the tape, keeping the platform's flat wall-clock entry window. Today's behaviour.
145
+ - `ON_CANDLE_CLOSE` — the flip ARMS the pair; the entry is taken only after a close on
146
+ `confirmTf` that still reads the conditions true and has not displaced beyond the band.
147
+ - `STOP_THROUGH_LEVEL` — a TRIGGER order rests at the authored level ± offset and the exchange
148
+ book is the watcher; the entry is taken when price trades through, not when the platform
149
+ notices.
150
+ - `ON_RETEST` — a LIMIT order rests at the broken level, waiting for a return to it. Not filling
151
+ is a correct outcome, not a failure.
152
+
153
+ `confirmTf` is the bar whose close confirms. Exactly two values are legal: the strategy's own
154
+ timeframe and the rung below it — one only, when the strategy sits on the ladder floor. It is
155
+ NOT the authorable main-candle set; a rung further down names a bar nothing else in the strategy
156
+ observes and makes the radar sweep on every one of its closes.
157
+
158
+ `closes` (1–5) is how many consecutive confirming closes are required, and `bandAtrMultiple` is
159
+ the veto width: the entry is VOIDED when the confirming close has moved at or beyond that many
160
+ ATR against the armed verdict. Strictly greater than zero — zero is not "no filter" but a filter
161
+ that voids on any adverse move — and at or below the platform's own entry-deviation gate, since
162
+ a wider band cannot refuse anything the platform will not refuse anyway.
163
+
164
+ `levelSource` is one of `SWING_HIGH`, `SWING_LOW`, `BOLLINGER_UPPER`, `BOLLINGER_LOWER`,
165
+ resolved once at decision time. `levelOffsetAtrMultiple` (0–2) is an UNSIGNED magnitude — the
166
+ direction is implied by the trigger and the verdict, so a signed value would invert the
167
+ trigger's meaning. `validForBars` (1–24) denominates validity in the strategy's OWN bars rather
168
+ than minutes, because a 1h setup waiting for a retest has not failed after fifteen minutes.
169
+
170
+ **The legality matrix runs one way.** All seven keys are always present, so the question is
171
+ never "is it set" but "is it set to something that MEANS anything under this trigger".
172
+ `closes` ≠ 1 is refused under any trigger but `ON_CANDLE_CLOSE`; `levelOffsetAtrMultiple` ≠ 0 is
173
+ refused under a non-level trigger. Leave a dial at its inert value rather than setting one the
174
+ platform will ignore.
175
+
176
+ ## Report sections
177
+
178
+ `{ kind, sectionKey, title, benchmarkTicker, notes, columns }` — the custom section shape. A
179
+ section carries no `timeframe`: its relative columns resolve against the strategy timeframe, and a
180
+ column reaches any other timeframe by pinning it (`timeframe: { abs: '4h' }`). `benchmarkTicker` and `notes` are **required-nullable**: send an explicit `null` rather
181
+ than omitting them, because the section is rebuilt whole on save and an omitted key clears the
182
+ author's value silently. On a CREATE, omit `sectionKey` — it is derived from the section itself.
183
+
184
+ ## Signal rules
185
+
186
+ `{ signalId, allocation, required, params }` — one entry per signal you want scoring.
187
+ `allocation` is the tier (0–3) and `params` replaces canonical defaults only when present.
188
+
189
+ ## Signal weights and gate math
190
+
191
+ Tiers: 0 Off · 1 Normal · 2 Important · 3 Critical.
192
+
193
+ ```
194
+ aggregateScore = Σ(score × allocation) / Σ(allocation) over triggered signals
195
+ ```
196
+
197
+ Weights are relative — build a pyramid: 1–2 Critical (thesis, usually `required`), 2–4
198
+ Important (independent confirmation, different modules), 1–3 Normal (context), rest Off so
199
+ noise cannot dilute the average. Gate check: with 3/2/2/1 weights, Critical + one Important at
200
+ score 1.0 → (3+2)/8 = 0.625, so a 0.6 gate means "thesis plus one confirmation".
201
+ `simulate_aggregate_score` does this arithmetic from compiled values at review time.
202
+ `required: true` counts the signal toward `minRequiredCount` when triggered; at
203
+ `allocation: 0` it is rejected (contract 34). `params` replace canonical defaults only when
204
+ present — read `get_strategy_signal_definition({ signalId, timeframe })` before tuning (e.g.
205
+ `rsi_overbought {"threshold": 65}` for a fade book; `volume_surge {"multiplier": 1.5}`).
206
+ `derive_strategy_rule_view` shows which signals a draft report feeds — weighting a signal the
207
+ report never feeds is dead weight.
208
+
209
+ ## Routing gates
210
+
211
+ `{ minAggregateScore, minRequiredCount, minAtrPct }` — whether a scored setup may route to a
212
+ trade at all. `minAggregateScore` 0–1 · `minRequiredCount` 0–20 · `minAtrPct` is the dead-market
213
+ floor, with bounds from `get_trading_config_catalog`.
214
+
215
+ ## Trade levels
216
+
217
+ `{ minStopLossAtrMultiple, maxStopLossAtrMultiple, minRiskRewardRatio }` — where stops and
218
+ targets may sit. `minStopLossAtrMultiple < maxStopLossAtrMultiple` (≤ the 3×ATR structural cap),
219
+ `minRiskRewardRatio` within the served range. Sizing is risk-budget based — a wider stop means a
220
+ smaller position, never more risk.
221
+
222
+ ## Position management
223
+
224
+ `{ breakEvenEnabled, breakEvenTriggerR, trailingEnabled, trailingTriggerR, trailingGivebackPct,
225
+ trailingBufferPct, timeDecayEnabled, timeDecayGracePeriodMinutes, timeDecayIntervalMinutes,
226
+ timeDecayTightenPct, timeDecayMaxTightenPct, timeDecayStaleThresholdTpProgressPct,
227
+ decisionInvalidationExitEnabled }` — how the stop moves after entry, and when a position is
228
+ closed for reasons other than its stop.
229
+
230
+ Validated bounds: break-even trigger 0.5–2R; trailing trigger 0–2R step 0.01 (0 = trail from
231
+ entry), giveback 25–55%, buffer 0.01–1%; timeDecay grace ≥ interval, tighten 0.1–50% to max
232
+ 1–100%, stale threshold 0–100% of TP progress. Per-mechanism flags; no umbrella switch.
233
+
234
+ `decisionInvalidationExitEnabled` is the post-fill invalidation exit: a **closed**
235
+ strategy-timeframe candle beyond the decision's invalidation level closes the position
236
+ reduce-only. Bar-close only — the protective stop still owns intrabar moves.
237
+
238
+ Persona presets: scalper (5m/15m) stops 0.5–1.2 ATR, RR 1.5, BE 0.7R, trail 0.9R/giveback 30,
239
+ timeDecay ON (45/15, tighten 15→60, stale 30) · intraday mean-revert (1h) 1.0–2.5 ATR, RR 1.5,
240
+ BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25) · swing breakout (4h) 0.75–1.75 ATR,
241
+ RR 2, BE 1R, trail 1.2R/giveback 35 · swing trend (4h) 1.0–2.5 ATR, RR 2, BE 1R, trail
242
+ 1.5R/giveback 45–55, no timeDecay.
243
+
244
+ ## Not expressible — the catalog keys this needs
245
+
246
+ The one place a claim that the catalog LACKS something may live, and every row names the key it
247
+ denies so the claim can be checked. A claim about the grammar's shape (a clause compares one
248
+ column against a literal; `distance` rejects an `offset`) belongs in prose above — those are
249
+ permanent. A claim that a metric is absent belongs here, or nowhere.
250
+
251
+ | Script / primitive | Key the catalog would need | Nearest expressible neighbour |
252
+ |---|---|---|
253
+ | 100-period SMA | `SMA100` | the shipped 50- or 200-period simple average, whichever the thesis leans on |
254
+
255
+ ## Where the worked material lives
256
+
257
+ This skill's body is the contract — the axes, the header grammar, how conditions and weights behave,
258
+ and the absence section above. The worked material is disclosed on demand, so it costs nothing until
259
+ you ask for it. Read a reference with `read_skill_reference` when you reach the work it covers:
260
+
261
+ - **`references/recipes.md`** — copy-adaptable column objects and condition fragments: cross
262
+ detection, board-relative ranks, cross-venue basis, crowd positioning, coin selection, and the
263
+ discovery fields worth reading before you compose.
264
+ - **`references/playbooks.md`** — five validated desk-grade compositions end to end, and how to
265
+ adapt one rather than copy it.
266
+ - **`references/tradingview-ports.md`** — per-script port recipes for the popular TradingView
267
+ strategies, each naming its substitutions where the catalog lacks a primitive.
268
+
269
+ Every rule about how a column *behaves* is in this body, not in a reference. If a reference seems to
270
+ state one, the body is the authority.
@@ -0,0 +1,110 @@
1
+ # Playbooks — validated desk-grade compositions
2
+
3
+ Worked compositions for the strategy studio, each validated against the live grammar. Shapes are
4
+ binding; the tokens are illustrations and stay live-discovered.
5
+
6
+ **Contents**
7
+ - Playbooks (validated compositions)
8
+ - Using a playbook
9
+
10
+ Read `## Header grammar` and `## Conditions` in the skill body first — the rules about how columns
11
+ behave live there, not here.
12
+
13
+ ## Playbooks (validated compositions)
14
+
15
+ *Validated* is a gate, not a claim: every playbook below is compiled against the live catalog by
16
+ `strategy-playbooks.compile.test.ts`, which fails the build if one stops assembling or if a header
17
+ its conditions name stops resolving. Edit a playbook and the gate re-checks it; move the grammar
18
+ underneath one and the gate catches that too.
19
+
20
+ ### 1 · Volatility Compression Breakout — 4h swing
21
+
22
+ Custom `Squeeze Scan` (no `sectionKey` — this is a CREATE): `BB_WIDTH_PCT value` + `rank lo`, `ADX value`,
23
+ `RVOL value`, `BB_PCT_B value`, `CLOSE_CHANGE value`, `NOTIONAL_VOLUME_1D value`; plus platform
24
+ `includeStructureZones`. (No `includeBollingerBands`: it renders `bbWidthPct` and
25
+ `bbWidthPct_rank_lo` a second time, which makes both of `SQUEEZE_ON`'s clauses ambiguous, and the
26
+ custom section already carries them.) Conditions: `LIQUID_FLOOR` (required —
27
+ `vol24hUsd gte 25000000`); `SQUEEZE_ON` building block (`bbWidthPct_rank_lo lte 10` AND
28
+ `ADX lt 20`); `BREAK_UP` verdict UP (ref SQUEEZE_ON + `closeChg gt 0` + `RVOL gte 1.5` +
29
+ `pctB gte 0.85`); `BREAK_DOWN` verdict DOWN (mirror with `pctB lte 0.15`). Rules:
30
+ `bollinger_squeeze` 3 required · `volume_surge` 2 required `{"multiplier":1.5}` ·
31
+ `volatility_atr_expanding` 2 · `sr_resistance_break` 2 · `sr_support_break` 2 ·
32
+ `bollinger_upper_touch` 1 · `bollinger_lower_touch` 1 · `trend_adx_ranging` 1. Gates
33
+ 0.55 / 2 / 0.8. Levels 0.75–1.75 ATR, RR 2. PM: BE 1R; trail 1.2R giveback 35 buffer 0.3.
34
+ Market Read: "Trade only expansions out of compression: {SQUEEZE_ON} must read TRUE… skip
35
+ anything failing {LIQUID_FLOOR}."
36
+
37
+ ### 2 · Crowded-Positioning Fade — 1h intraday
38
+
39
+ Custom `Positioning`: `FUNDING_RATE value`→`rate`, `aggregate w24`→`rate_mean24`,
40
+ `FUNDING_ANN value`→`ann`, `OI_CHG value`→`oiChg`, `OI_PX_REGIME value`→`oiRegime` (vocab:
41
+ new longs / new shorts / short covering / long liquidation), `MARK spread ORACLE`→
42
+ `mark_oracle_spread`, `RSI14 value`, `CHG_24H value`→`chg24h`; plus `includeCvd`. Conditions:
43
+ `NO_LIQUIDATION_TAPE` required NOT-veto (`NOT [oiRegime is "long liquidation"]`);
44
+ `CROWDED_LONGS` building block (`ann gte 25` + `oiChg gte 3` + `oiRegime is "new longs"`);
45
+ `FADE_SHORT` verdict DOWN (ref + `RSI14 gte 65` + `chg24h gte 5`); mirrored `SQUEEZED_SHORTS`
46
+ / `SQUEEZE_LONG` verdict UP. Rules: `funding_extreme_positive`/`_negative` 3 required
47
+ `{"thresholdPct":0.001}` · `oi_surge` 2 `{"thresholdPct":0.03}` · `rsi_overbought` 2
48
+ `{"threshold":65}` · `rsi_oversold` 2 `{"threshold":35}` · `cvd_bear_divergence` 2 ·
49
+ `cvd_bull_divergence` 2 · `mfi_overbought` 1 · `mfi_oversold` 1. Gates 0.6 / 1 / 0.5. Levels
50
+ 1.0–2.5 ATR, RR 1.5. PM: BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25).
51
+
52
+ ### 3 · Relative-Strength Rotation with a Benchmark Gate — 4h cross-sectional
53
+
54
+ Custom `Leadership`: `ROC12 rank hi` + `rank lo`, `RVOL rank hi`,
55
+ `EMA5 spread EMA13 × trajectory w4`, `NOTIONAL_VOLUME_1D value`. **Rank `ROC12`, not
56
+ `CLOSE_CHANGE`:** `closeChg` is the one magnitude-only rankable code, so `hi`/`lo` on it are
57
+ refused, and its legal `far` ordering sorts by |change| — which would put the session's biggest
58
+ losers in a "leaders" cohort. Custom `BTC Regime` with
59
+ `"benchmarkTicker": "BTC"`: `MA_ALIGN value`→`MAalign`, `ADX value`, `REGIME_TREND value` —
60
+ every row reads BTC. Conditions: `BTC_RISK_ON` building block (`MAalign is "bullish"` +
61
+ `ADX gte 20` — both read bare, since only this section renders them); `BTC_RISK_OFF`
62
+ (`MAalign is "bearish"`); `LEADER` (`roc12_rank_hi lte 5` + `RVOL_rank_hi lte 10` +
63
+ `vol24hUsd gte 100000000`); `ROTATE_IN` verdict UP (`ALL[ref BTC_RISK_ON, ref LEADER]`);
64
+ `ROTATE_OUT` verdict DOWN (`ALL[ref BTC_RISK_OFF, roc12_rank_lo lte 5]`). Rules:
65
+ `comparison_sector_momentum` 3 · `rel_roc_positive`/`_negative` 2 · `ma_ema_aligned_bull`/
66
+ `_bear` 2 required · `rel_ppo_bull_cross`/`_bear_cross` 1 · `volume_surge` 1. Gates
67
+ 0.5 / 1 / 0.6. Levels 1–2 ATR, RR 1.8. PM: BE 1R; trail 1R giveback 45. Ranked cohort ≥ 40 —
68
+ rank conditions need a cohort wider than their thresholds.
69
+
70
+ ### 4 · HTF Trend Pullback — 1h, multi-timeframe confluence
71
+
72
+ Platform `includeMtfConfluence` (`MAalign_ltf/…/_htf`, `RSI14_*_zone`, `ADX_*_state` vocab
73
+ weak/developing/trending/extreme), `includeMovingAverages` (`dist_EMA20`, …), `includeRsi`
74
+ (`RSI14_now`, `RSI14_zone`). These three render `MAalign` and `RSI14_zone` twice between them, so
75
+ every condition below reads a suffixed form — never bare `MAalign` or bare `RSI14_zone`, which
76
+ would be ambiguous. Conditions: `HTF_UP` building block (`MAalign_htf is "bullish"` +
77
+ `ADX_htf_state in ["trending","extreme"]`); `HTF_DOWN` mirror; `TREND_PRESENT` **required**
78
+ `ANY[ref HTF_UP, ref HTF_DOWN]` — chop blocks compose-trade entirely, before billing;
79
+ `PULLBACK_LONG` verdict UP (ref HTF_UP + `RSI14_now between 35 55` +
80
+ `dist_EMA20 between -3 0.5`); `PULLBACK_SHORT` verdict DOWN (mirror, 45–65 / −0.5–3). Rules:
81
+ `mtf_pullback_long`/`_short` 3 required · `htf_ma_aligned_bull`/`_bear` 2 required ·
82
+ `htf_trend_adx_trending` 2 · `ma_ema_aligned_bull`/`_bear` 1 · `rsi_oversold` 1
83
+ `{"threshold":40}`. Gates 0.6 / 2 / 0.7 (a pullback signal AND an HTF alignment). Levels
84
+ 1–2.5 ATR, RR 2. PM: BE 1R; trail 1.5R giveback 30 buffer 0.3.
85
+
86
+ ### 5 · Perp/Spot Flow Divergence at Structure — 15m scalp
87
+
88
+ Platform `includePerpSpotFlow` (`perpSpotFlow` vocab: confirmed_bull / confirmed_bear /
89
+ perp_led_fragile / spot_led_accumulation / neutral), `includeStructureZones`
90
+ (`zones_htf_support_dist` signed %, support below price is negative; `_age_h`), custom `Tape`
91
+ (`BUY_PRESSURE value`→`buyPres` 0–1, `RVOL value`, `CLOSE_CHANGE value`). Conditions:
92
+ `SPOT_ACCUM` (`perpSpotFlow is "spot_led_accumulation"`); `NEAR_SUPPORT`
93
+ (`zones_htf_support_dist between -2 0` + `zones_htf_support_age_h gte 12`); `DIP_BID` verdict
94
+ UP — refs plus an `N_OF(2)` quorum over `buyPres gte 0.55` / `RVOL gte 1.2` / `closeChg gt 0`
95
+ (quorum beats a brittle ALL); `FRAGILE_POP` verdict DOWN (`perp_led_fragile` +
96
+ `zones_htf_resist_dist between 0 2` + `RVOL gte 1.3`). Rules:
97
+ `flow_perp_spot_bull_divergence`/`_bear_divergence` 3 required · `cvd_bull_divergence`/
98
+ `_bear_divergence` 2 · `sr_at_support`/`_at_resistance` 2 · `structure_ob_approach` 1 ·
99
+ `volume_surge` 1. Gates 0.55 / 1 / 0.4. Levels 0.5–1.2 ATR, RR 1.5. PM: BE 0.7R; trail 0.9R
100
+ giveback 30 buffer 0.15; timeDecay ON aggressive (45/15, 15→60, stale 30).
101
+
102
+ ## Using a playbook
103
+
104
+ Discover → confirm headers (`get_strategy_column_contract`, or one `preview_strategy_report`
105
+ whose `conditionColumns` lists every addressable header with operators and vocabulary) →
106
+ calibrate literals against the previewed live values → compile once → review the compiled
107
+ scorecard, condition outcomes, verdict tally, and `marketReadMarkers` (fix `unknown` /
108
+ `ambiguous` markers) → apply per the strategy-authoring flow. Coin selection is call context,
109
+ not strategy state: explicit tickers for focused work, `ranked` (with an optional category) for
110
+ scanning books.
@@ -0,0 +1,118 @@
1
+ # Recipes — copy-adaptable fragments
2
+
3
+ Column objects for a custom section's `columns[]`, condition fragments for `conditions[]`, and the
4
+ coin-selection and discovery shapes an author reaches for while writing a payload.
5
+
6
+ **Shapes are binding. Tokens are illustrations** — re-discover every metric, transform and parameter
7
+ against the live tools (`list_strategy_categories`, `list_strategy_vocabulary`,
8
+ `get_metric_construction_hints`, `get_strategy_column_contract`) before compiling. A recipe is a
9
+ worked shape, never a substitute for discovery.
10
+
11
+ Read `## Header grammar` and `## Conditions` in the skill body first: how a column's header is
12
+ generated, how event columns resolve, what `{abs: …}` binds, and which classifications are states
13
+ rather than events all live there. This document assumes them.
14
+
15
+ **Contents**
16
+ - Column recipes
17
+ - Cross detection
18
+ - Board-relative rank recipes
19
+ - Cross-venue and perp/spot basis
20
+ - Crowd positioning
21
+ - Coin selection
22
+ - Discovery fields worth reading
23
+
24
+ ## Column recipes
25
+
26
+ | Intent | Column | Header it generates |
27
+ |---|---|---|
28
+ | Live traded price | `{ "metric": "LAST", "transformId": "value", "timeframe": { "rel": "anchor" } }` | `last` |
29
+ | Momentum build-up | `{ "metric": "RSI14", "transformId": "trajectory", "timeframe": { "rel": "anchor" }, "window": 4 }` | `RSI14_t3…_now`, `RSI14_trend` |
30
+ | Closed-bar-only trajectory | add `"bars": "closed"` to any trajectory | same, drawn from closed bars |
31
+ | Extension from trend | `{ "metric": "SMA50", "transformId": "distance", "timeframe": { "rel": "anchor" } }` | `dist_SMA50` (signed %) |
32
+ | Funding paid over a day | `{ "metric": "FUNDING_RATE", "transformId": "aggregate", "timeframe": { "rel": "anchor" }, "window": 24 }` | `rate_mean24` |
33
+ | Chop filter on the entry rung | `{ "metric": "CLOSE", "transformId": "efficiency", "timeframe": { "rel": "lower" }, "window": 5, "bars": "closed" }` | `close_ltf_er` (≥0.6 directional) |
34
+ | One-bar volume concentration | `{ "metric": "VOLUME", "transformId": "maxShare", "timeframe": { "rel": "lower" }, "window": 4, "bars": "closed" }` | `volBase_ltf_maxShare` |
35
+ | Room to structure | `{ "metric": "STRUCT_ZONES", "transformId": "nearestZoneDist", "timeframe": { "rel": "regime" }, "side": "resistance" }` | `zones_htf_resist_dist` |
36
+ | Last **closed** daily read | `{ "metric": "RSI14", "transformId": "value", "timeframe": { "abs": "1d" }, "offset": 1 }` | `RSI14_1d` — `RSI14[t - 1]` |
37
+
38
+ ## Cross detection
39
+
40
+ `crossDetect` is the transform that turns a pair into an EVENT column — it prints on the crossing bar
41
+ and is null otherwise:
42
+
43
+ ```json
44
+ { "metric": "MACD", "transformId": "crossDetect", "timeframe": { "rel": "anchor" } }
45
+ ```
46
+
47
+ An event column is a trigger inside a carrier, never a standing state; pair it with a persistent
48
+ state for regime. The skill body's `## Header grammar` states that rule and is the authority on it —
49
+ a condition that reads a `crossDetect` column as a state is UNRESOLVED on nearly every bar.
50
+
51
+ ## Board-relative rank recipes
52
+
53
+ `rank` turns any column into an ordinal across the scanned board. Ordering semantics: `hi` most
54
+ positive, `lo` most negative, `far`/`near` by magnitude (offered only where sign matters), and
55
+ `lte N` reads as "top N".
56
+
57
+ | Intent | Column | Header |
58
+ |---|---|---|
59
+ | Most volatile on the board | `{ "metric": "ATR_PCT", "transformId": "rank", "timeframe": { "rel": "anchor" }, "ordering": "hi" }` | `atrPct_rank_hi` (1 = most volatile) |
60
+ | Biggest movers, sign-agnostic | `{ "metric": "CLOSE_CHANGE", "transformId": "rank", "timeframe": { "rel": "anchor" }, "ordering": "far" }` | `closeChg_rank_far` |
61
+ | Most extended from VWAP | `{ "metric": "VWAP", "transformId": "distance", "chainedTransformId": "rank", "timeframe": { "rel": "anchor" }, "ordering": "far" }` | `dist_VWAP_rank_far` |
62
+
63
+ Confirm every rank ordering against `get_metric_construction_hints({ metric }).rankOrderings` — it is
64
+ present only where rank is composable on that metric, and is already range-gated server-side. Read
65
+ the offered set; never derive one from the metric's native output.
66
+
67
+ ## Cross-venue and perp/spot basis
68
+
69
+ | Intent | Column | Header |
70
+ |---|---|---|
71
+ | Perp premium to oracle | `{ "metric": "MARK", "transformId": "spread", "timeframe": { "rel": "anchor" }, "inputs": [{ "metric": "ORACLE" }] }` | `mark_oracle_spread` |
72
+ | Basis to Binance spot | `{ "metric": "MARK", "transformId": "spread", "timeframe": { "rel": "anchor" }, "inputs": [{ "metric": "SPOT_CLOSE_BN" }] }` | `mark_bnClose_spread` |
73
+ | Basis to Coinbase spot | same with `{ "metric": "SPOT_CLOSE_CB" }` | `mark_cbClose_spread` |
74
+
75
+ A positive basis that is widening while price stalls is perp-led; the flow playbooks use it as the
76
+ fragility read.
77
+
78
+ ## Crowd positioning
79
+
80
+ `CROWD_UPBIAS` is the platform's own crowd read — the share of recent settled sessions predicting UP,
81
+ as a percentage:
82
+
83
+ ```json
84
+ { "metric": "CROWD_UPBIAS", "transformId": "value", "timeframe": { "rel": "anchor" } }
85
+ ```
86
+
87
+ Header `upBias`. It is a fade input, not a trend input: it says what the board expects, which is the
88
+ thing a crowded-positioning playbook trades against.
89
+
90
+ ## Coin selection
91
+
92
+ `coinSelection` is required on every compile and is discriminated on `mode`:
93
+
94
+ ```json
95
+ { "mode": "ranked", "limit": 25, "category": "DEFI" }
96
+ { "mode": "explicit", "tickers": ["BTC", "ETH", "SOL"] }
97
+ ```
98
+
99
+ `ranked` is top-N by 24h volume with an optional sector filter; `explicit` names a ticker set
100
+ validated against the active universe. The sector vocabulary is
101
+ `ALL` / `CRYPTO` / `L1` / `MEMES` / `DEFI` / `TRADFI` / `STOCKS` / `INDICES` / `COMMODITIES` —
102
+ discover the live set rather than assuming this list has not moved. An explicit set is capped; the
103
+ refusal names the cap.
104
+
105
+ ## Discovery fields worth reading
106
+
107
+ Two answers are frequently fetched and then left unread, and each has cost a refusal that a
108
+ single already-served field would have prevented:
109
+
110
+ - `get_strategy_column_contract({ column }).outputs[].conditionOperators` — an empty array means that
111
+ rendered header has no comparison semantics and **cannot appear in a condition clause at all**.
112
+ Legality is per rendered header, not per column: a trajectory's slot header and its `_trend` header
113
+ answer differently.
114
+ - `get_metric_construction_hints({ metric }).rankOrderings` and `.qualifiedForms` — the composable
115
+ rank orderings, and the section-qualified forms available when a header is duplicated report-wide.
116
+ - `get_trading_config_catalog()` carries the platform defaults an omitted dial inherits, including
117
+ `defaultMaxEntryDeviationAtrMultiple` — the band an entry is still admitted within. Read it before
118
+ authoring an entry band, rather than restating a number the platform already owns.
@@ -0,0 +1,83 @@
1
+ # TradingView ports — familiar processes in studio vocabulary
2
+
3
+ Per-script port recipes. Each ports the PROCESS (regime filter -> setup state -> trigger -> stop
4
+ engine) and names its substitutions where the catalog lacks a primitive.
5
+
6
+ **Contents**
7
+ - Squeeze Momentum [LazyBear] / TTM Squeeze
8
+ - Supertrend / UT Bot / Chandelier Exit
9
+ - MACD + 200 MA filter
10
+ - Golden / Death Cross
11
+ - RSI-2 (Connors)
12
+ - VWAP reversion
13
+ - Donchian / Turtle breakout
14
+ - ICT / SMC (FVG + order blocks)
15
+ - Now native, formerly substituted
16
+
17
+ ## TradingView ports — familiar processes, studio vocabulary
18
+
19
+ Players often ask for strategies by the name of a popular TradingView script. Port the
20
+ **process** (regime filter → setup state → trigger → stop engine), and where the catalog lacks
21
+ the primitive, name the substitution in the spec-lock question — never present a substitute as
22
+ the thing itself. Most TV strategies run on the daily chart: carry that with the
23
+ daily-strategy pattern above (pinned-1d thesis at `offset: 1` on an intraday anchor), which
24
+ binds decisions to daily closes while the studio keeps managing risk intraday.
25
+
26
+ Event-column behaviour, state-vs-event, `PDH`/`PDL` binding and the squeeze's anchor calibration are
27
+ rules about the columns themselves, not about porting — they live in the skill body's
28
+ `## Header grammar` and `## Conditions`. Read those first; this document is the per-script recipes.
29
+
30
+ - **Squeeze Momentum [LazyBear] / TTM Squeeze** → `KC_SQUEEZE is "on"` is the native one-condition
31
+ read: the Bollinger pair sitting inside the Keltner channel, at the script's own multiplier.
32
+ The four boundaries stay addressable, so `BB_UPPER spread KC_UPPER` and
33
+ `BB_LOWER spread KC_LOWER` still express the same reading at a threshold you choose. Release
34
+ direction from the MACD trajectory, `bollinger_squeeze` Critical-required.
35
+ Its selectivity is anchor-dependent — see `## Conditions` in the skill body before gating on it.
36
+ (`bbWidthPct_rank_lo` + `ADX lt 20` remains a serviceable board-relative compression proxy, but it
37
+ is no longer the only option.)
38
+ - **Supertrend / UT Bot / Chandelier Exit** → `ST_LINE` and `ST_DIR` are native, so the regime
39
+ half is a direct port: `ST_DIR is "bullish"` as the persistent state, `dist_ST_LINE` for
40
+ distance to the plotted stop. The plotted trailing line is still best executed by the studio's
41
+ own stop engine — `trailingTriggerR: 0` (trail from entry), giveback ~30–40 (tight factor) or
42
+ 45–55 (loose/chandelier) — because a trailing stop is a position-management mechanism, not a
43
+ column. `ST_DIR` is a persisting state and not a flip event — see `## Conditions` in the
44
+ skill body — so the FLIP needs an event column beside it. Name that as a substitution.
45
+ - **MACD + 200 MA filter** → `ABOVE_200` building block (`dist_SMA200 gt 0`) referenced by a
46
+ carrier with `MACD_cross is "Bullish"`; rules `macd_bull/bear_cross` 3 required +
47
+ `ma_sma200_above/below` 2 required; swing-trend geometry.
48
+ - **Golden / Death Cross** → `SMA50 spread SMA200 × trajectory` gives state and freshness:
49
+ `SMA50_SMA200_spread_now gt 0` AND `_trend is "rising"`; optional breadth gate on
50
+ `mktBreadth_crypto gte 0` — every published scope resolves, not only `all`, because the leg reads
51
+ the `(timeframe, scope)` pairs a condition names; position-persona geometry. `SMA50 × SMA200` is
52
+ the canonical definition and stays the default port; `EMA50` is available as the crypto variant
53
+ when a player asks for it by name.
54
+ - **RSI-2 (Connors)** → `RSI2` is native, so this ports exactly: `RSI2 lte 10` gated by required
55
+ `ABOVE_200`; the source's fast exit is time — timeDecay ON (180/60, 15→50, stale 20);
56
+ `rsi_oversold` 3 required with a tuned threshold. Keep the literal `lte 10` as the GATE:
57
+ `RSI2 × classifyZone` exists and reads on the same Connors bands, but a zone label is a fixed
58
+ reading while the literal is a threshold the author can see and tune. Use the zone as a report
59
+ column, never as the substitute for the gate.
60
+ - **VWAP reversion** → `dist_VWAP` band + `dist_VWAP_rank_far lte 5` for board-relative
61
+ stretch; required `NOT [ADX_state in ["trending","extreme"]]` veto; scalp geometry +
62
+ aggressive timeDecay (VWAP anchors daily at 00:00 UTC).
63
+ - **Donchian / Turtle breakout** → `zone is "breakout high"` + `dist_swingHi gte 0` +
64
+ `RVOL gte 1.5`; `sr_resistance_break` 3 required; turtle exits = trend preset (trail from
65
+ 1R, giveback 50). Mirror with `"breakdown low"`. **Daily-breakout variant on any anchor:**
66
+ pin the structure at 1d — `zone_1d is "breakout high"`, `dist_swingHi_1d gte 0` (validated).
67
+ **Literal previous-day levels are native**: `dist_PDH gte 0` composes directly. Their `{abs: '1d'}`
68
+ binding is stated in `## Header grammar` in the skill body.
69
+ - **ICT / SMC (FVG + order blocks)** → `STRUCT_ZONES` is the native zone engine:
70
+ `zones_htf_support_type` (`bullish FVG`/`bullish order block`), `zones_htf_support_dist
71
+ between -1.5 0`, `_age_h gte 12`, HTF bias required via `MAalign_htf`; rules
72
+ `structure_fvg_approach`/`structure_ob_approach` required (their `proximityPct` is the
73
+ in-zone dial). Liquidity sweeps, displacement, killzones and event *sequencing* are shapes the
74
+ grammar does not have — a clause compares one column against a literal, so an ordered sequence
75
+ of events cannot be stated at all. That is a grammar limit, not a missing metric; name it as
76
+ one.
77
+ - **Now native, formerly substituted** — WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`),
78
+ Hull (`HMA20`), Ichimoku (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`),
79
+ Parabolic SAR (`PSAR`), Keltner (`KC_UPPER`/`KC_MID`/`KC_LOWER`), daily pivots
80
+ (`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`), Williams %R (`WILLR14`), Stochastic RSI
81
+ (`STOCH_RSI14`), the TTM squeeze (`KC_SQUEEZE`), Connors RSI-2 (`RSI2`), the 9/21/50 EMAs
82
+ (`EMA9`/`EMA21`/`EMA50`), and literal previous-day levels (`PDH`/`PDL`). Port these directly —
83
+ do not offer a substitute for a primitive the catalog serves.