@battlegrid/mcp-server 31.2.6 → 31.2.8
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.
- package/README.md +95 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +6 -3
- package/skills/battlegrid-strategy-authoring/SKILL.md +22 -0
- package/skills/battlegrid-strategy-examples/SKILL.md +38 -169
- package/skills/battlegrid-strategy-examples/references/playbooks.md +110 -0
- package/skills/battlegrid-strategy-examples/references/recipes.md +118 -0
- package/skills/battlegrid-strategy-examples/references/tradingview-ports.md +83 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Seeing package `31.x` alongside handshake `battlegrid@33.x` — the package **be
|
|
|
24
24
|
|
|
25
25
|
**What this changes for you:** nothing about how you call anything. Upgrading the package no longer waits on a server deploy, and a server deploy no longer strands you on a package that names the wrong contract — reconnect and the announcement follows. **Contract breaking-change notes are no longer keyed to package versions**, since a contract move is no longer a release here; the v11-and-earlier notes below are kept as history, and the live vocabulary is always discovery.
|
|
26
26
|
|
|
27
|
-
## Contract history — v37 →
|
|
27
|
+
## Contract history — v37 → v51
|
|
28
28
|
|
|
29
29
|
Eleven majors reached authors while this section stopped at v36. That gap is the mechanism, not an
|
|
30
30
|
oversight: since v31 a contract move needs no release here, so nothing forced a note to be written —
|
|
@@ -32,11 +32,43 @@ and the documentation ships inside the tarball, so a note written but unpublishe
|
|
|
32
32
|
Both halves are now closed by a rule keyed to the *served* contract rather than to a release of this
|
|
33
33
|
package.
|
|
34
34
|
|
|
35
|
+
**50.0.0 and 51.0.0 arrived late, and the reason is worth naming.** The re-vendoring errand that
|
|
36
|
+
used to carry these notes is now a generated export
|
|
37
|
+
(`battlegrid-app/server/scripts/export-mcp-skills.mjs`), and it owns three paths — `skills/`,
|
|
38
|
+
`skills/EXPORT.json`, and the vendored digest. It deliberately does not touch this file. So the
|
|
39
|
+
digest kept arriving on time while the note stopped travelling with it, and this section sat at
|
|
40
|
+
v49.5 against a served contract of 51.0.0. Nothing a client could observe was wrong; what was
|
|
41
|
+
missing was the sentence telling them so. **A contract move still needs a human-authored entry
|
|
42
|
+
here, and the export lane will not remind you.**
|
|
43
|
+
|
|
35
44
|
### Accepted again — input that was rejected now compiles
|
|
36
45
|
|
|
37
46
|
Nothing to migrate. This is the one direction that cannot break a client: a body the server used to
|
|
38
47
|
refuse is now stored. Listed because a client that special-cased the refusal can delete that branch.
|
|
39
48
|
|
|
49
|
+
- **A signal rule patch no longer forces you to restate `allocation` and `required`** (51.0.0,
|
|
50
|
+
`fix-signal-rule-patch-semantics`). On `update_strategy_signal_rule`, and on the `rules` element
|
|
51
|
+
of `compile_strategy_plan`, both fields become optional and join `params` under ONE omission
|
|
52
|
+
rule: **an omitted mutable field preserves the stored value for that signal.** "Raise this
|
|
53
|
+
signal's weight" is now expressible.
|
|
54
|
+
|
|
55
|
+
```jsonc
|
|
56
|
+
{ "strategyId": "…", "expectedRevision": 7, "signalId": "volume_surge",
|
|
57
|
+
"allocation": 3 } // `required` and `params` keep exactly what is stored
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Every existing client keeps working** — a complete payload is still a valid patch — so this is
|
|
61
|
+
listed for what you can now STOP sending. Before it, both fields were mandatory on every rule
|
|
62
|
+
surface, so a caller that had not first read the current rule had to invent a value it was never
|
|
63
|
+
asked about. That is not hypothetical: revision 5 of a production strategy flipped `required`
|
|
64
|
+
false → true unasked while moving a weight 2 → 3, turning a scoring signal into a **mandatory
|
|
65
|
+
gate** — which changes whether the agent takes trades at all.
|
|
66
|
+
|
|
67
|
+
One boundary on the newly legal ground, and it breaks nothing: a patch carrying **no** mutable
|
|
68
|
+
field is refused — *"A rule patch must change something: supply at least one of allocation,
|
|
69
|
+
required or params."* — rather than minting a no-op revision. Under 50.0.0 that request could not
|
|
70
|
+
be formed at all, so nothing that used to work is now refused.
|
|
71
|
+
|
|
40
72
|
- **An arming trigger no longer constrains its required conditions' clock** (49.4.0,
|
|
41
73
|
`restore-arming-trigger-authoring`). `compile_strategy_plan`, `apply_strategy_plan` and
|
|
42
74
|
`fork_strategy` accept a strategy whose entry trigger is `ON_CANDLE_CLOSE`, `STOP_THROUGH_LEVEL`
|
|
@@ -55,6 +87,14 @@ refuse is now stored. Listed because a client that special-cased the refusal can
|
|
|
55
87
|
|
|
56
88
|
### Changed meaning, unchanged shape
|
|
57
89
|
|
|
90
|
+
- **`blocksScanGate` reads `true` for a class it did not** (50.0.0, `own-scan-served-set-once`), on
|
|
91
|
+
`preview_radar_resolution`. Blocking is now derived from the lane's **served set** rather than
|
|
92
|
+
switched over the reach reason, so a `FEED`-reason refusal on an operand no reader in the lane
|
|
93
|
+
serves BLOCKS instead of deferring. No field changes shape, and a client that already renders the
|
|
94
|
+
key renders the new answer — but a client that treated `blocksScanGate: false` as "this will
|
|
95
|
+
resolve once data arrives" now sees a deployment that will not fire. Nothing in the payload tells
|
|
96
|
+
you this moved.
|
|
97
|
+
|
|
58
98
|
- **Three tools serve different values for identical input** (47.3.0, `derive-scan-fetch-from-report`).
|
|
59
99
|
The radar scan leg now derives its timeframe fetch from the strategy's **report** rather than the
|
|
60
100
|
on-duty agent's three perception rungs, so a required condition addressing an absolute timeframe
|
|
@@ -72,6 +112,44 @@ refuse is now stored. Listed because a client that special-cased the refusal can
|
|
|
72
112
|
|
|
73
113
|
### Rejected input — something you author is no longer accepted
|
|
74
114
|
|
|
115
|
+
- **`update_strategy_signal_rule` requires `confirm: true` when the strategy has bound agents**
|
|
116
|
+
(51.0.0, `fix-signal-rule-patch-semantics`). The write re-materializes scoring configuration onto
|
|
117
|
+
every bound agent immediately — including agents holding open USDC positions — and until now
|
|
118
|
+
nothing on the server asked. A rule edit on a strategy with one or more bound agents is refused
|
|
119
|
+
without the flag, and the message names the count. An edit on a strategy with **nothing bound is
|
|
120
|
+
unaffected**, and so is every path through the web editor.
|
|
121
|
+
|
|
122
|
+
**Send `confirm: true`.** That is the whole migration, and `confirm` is published on the input
|
|
123
|
+
schema — but nothing in the schema says WHEN it becomes mandatory, because the condition is the
|
|
124
|
+
bound-agent count rather than the shape of your body. The refusal rides the existing
|
|
125
|
+
`VALIDATION_ERROR` code, so a client that omits it discovers the rule at the refusal.
|
|
126
|
+
|
|
127
|
+
Why it moved to the server: the guard existed, but only as served prose the calling model could
|
|
128
|
+
decline — and did, twice in production on 2026-08-24. `archive_strategy` and
|
|
129
|
+
`rebind_intelligence_agent` have taken a server-enforced `confirm` all along; single-rule tuning
|
|
130
|
+
was the outlier among its own siblings, and it is the one that writes to scoring.
|
|
131
|
+
|
|
132
|
+
- **A radar deployment is refused when its strategy reads a session-field scalar** (50.0.0,
|
|
133
|
+
`own-scan-served-set-once`). `upsert_radar_deployment` refuses a deployment whose slot agents'
|
|
134
|
+
bound strategy carries a condition reading one of five SESSION-FIELD scalars — `fieldPlayers`,
|
|
135
|
+
`fieldUpBias`, `fieldBiasDir`, `captConc`, `picksSpread` — with
|
|
136
|
+
`CONDITION_OPERAND_UNSERVED_IN_LANE`. A body accepted under 49.5.0 is refused under 50.0.0
|
|
137
|
+
without one byte of it changing.
|
|
138
|
+
|
|
139
|
+
**Why a refusal and not a warning.** Those five describe a game SESSION, and radar runs outside a
|
|
140
|
+
session at BOTH its stages — so such a condition can never resolve there. The deployment formed
|
|
141
|
+
no fire edge and the agent did nothing on that coin, silently, forever. The refusal converts a
|
|
142
|
+
permanent silence into an error at the moment you author it.
|
|
143
|
+
|
|
144
|
+
**Migrate** by moving the clause to a scalar radar reads — the Market Breadth or Reference Pairs
|
|
145
|
+
families, which are market-wide reads with no session dimension — or by binding the strategy to
|
|
146
|
+
an arena agent instead. The error carries both halves: `allowedDomain` enumerates every servable
|
|
147
|
+
header, and the message names the sections.
|
|
148
|
+
|
|
149
|
+
**Strategy authoring is untouched by this bump.** The same strategy is legal, and reads those
|
|
150
|
+
scalars correctly, on an arena agent — which is why the refusal is on the DEPLOYMENT and not on
|
|
151
|
+
`compile_strategy_plan` / `apply_strategy_plan`.
|
|
152
|
+
|
|
75
153
|
- **A benchmark-bound section no longer accepts crowd metrics or rank transforms** (49.0.0,
|
|
76
154
|
`fix-benchmark-legality-save-path`). On a custom section carrying a non-null `benchmarkTicker`, a
|
|
77
155
|
column whose metric is enrichment-stage (the `CROWD_*` family, `FLOW_ALIGN`, `SMART_RETAIL`,
|
|
@@ -179,6 +257,22 @@ refuse is now stored. Listed because a client that special-cased the refusal can
|
|
|
179
257
|
|
|
180
258
|
### Reshaped output — the same call returns a different shape
|
|
181
259
|
|
|
260
|
+
- **`update_strategy_signal_rule` gains its own response envelope** (51.0.0,
|
|
261
|
+
`fix-signal-rule-patch-semantics`). It no longer shares `{ strategy }` with its siblings. The
|
|
262
|
+
response is `{ strategy, ruleChanges }`, where `ruleChanges` is the server's own before/after
|
|
263
|
+
pair for the edited signal — `[{ signalId, before, after }]`, each side a full rule object. It is
|
|
264
|
+
`null` when the mutation changed no rule, **never `[]`**.
|
|
265
|
+
|
|
266
|
+
**Report the change from that pair, not from memory.** The planner always computed the diff and
|
|
267
|
+
the tool discarded it, so a caller narrating what it just did had only its own recollection of
|
|
268
|
+
the before-value. One production edit shipped a wrong receipt on top of a wrong write that way,
|
|
269
|
+
and the write was unreconstructable from the audit trail afterwards.
|
|
270
|
+
|
|
271
|
+
Additive, but published on a `.strict()` shape — a decoder pinned to the old two-key object
|
|
272
|
+
rejects the new key. `fork_strategy`, `archive_strategy` and `restore_strategy` keep the shared
|
|
273
|
+
`StrategyResponseSchema` and publish exactly what they did; it was deliberately NOT widened for
|
|
274
|
+
them, so this reshape reaches one tool only.
|
|
275
|
+
|
|
182
276
|
- **An entry void now names the gate that refused it** (49.5.0,
|
|
183
277
|
`fix-arming-trigger-clock-authority`), on `get_radar_activity_summary`. In the cause rollup, the
|
|
184
278
|
`ENTRY_VOID` group's `gateCode` widens from always-`null` to `QualificationGateCode | null`: a
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ import { type Implementation, type Prompt, type Resource } from '@modelcontextpr
|
|
|
49
49
|
* being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
|
|
50
50
|
* correction. Never move it to track the server.
|
|
51
51
|
*/
|
|
52
|
-
export declare const PACKAGE_VERSION = "31.2.
|
|
52
|
+
export declare const PACKAGE_VERSION = "31.2.8";
|
|
53
53
|
export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
|
|
54
54
|
export interface EnvConfig {
|
|
55
55
|
apiKeys: string[];
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema
|
|
|
52
52
|
* being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
|
|
53
53
|
* correction. Never move it to track the server.
|
|
54
54
|
*/
|
|
55
|
-
export const PACKAGE_VERSION = '31.2.
|
|
55
|
+
export const PACKAGE_VERSION = '31.2.8';
|
|
56
56
|
export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
|
|
57
57
|
const MAX_RETRIES = 3;
|
|
58
58
|
const RETRY_DELAYS_MS = [2000, 4000, 8000];
|
package/package.json
CHANGED
package/skills/EXPORT.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "51.0.0",
|
|
4
4
|
"files": {
|
|
5
5
|
"battlegrid-agent-management/SKILL.md": "b6ea75b3d838c1dbaaf3984d7e99a11c81686de2c1a1910c35699ee7e70dafa5",
|
|
6
6
|
"battlegrid-arena-play/SKILL.md": "03fa153bf82be18bf5ed01e3ba12e2cd9f99a81b08d903a77b52169e7519a182",
|
|
7
7
|
"battlegrid-market-analysis/SKILL.md": "22fde9c4eac0261c89a8056ab8b20b66fcbe1fd58447fa0ac84fd410b4d134d7",
|
|
8
8
|
"battlegrid-radar-deployment/SKILL.md": "d2a012b154e1e529db19529bd7c3d75d069f4e410d5804add51165f0f0902b2e",
|
|
9
|
-
"battlegrid-strategy-authoring/SKILL.md": "
|
|
9
|
+
"battlegrid-strategy-authoring/SKILL.md": "6857956221375f45c42bdadbf032f6858f6534f91fa4d733f6f572cb4c53a1ff",
|
|
10
10
|
"battlegrid-strategy-doctor/SKILL.md": "9ab3f79128ec81d3e90167c3f1ea4bd6f6b570290e5ebe2bf855195263c58239",
|
|
11
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
11
|
+
"battlegrid-strategy-examples/SKILL.md": "0d6d9611d10bd7683c9bc5186d3db26f2fd4123a5b57330f827ff45cd9ef82c7",
|
|
12
|
+
"battlegrid-strategy-examples/references/playbooks.md": "35c543c090d00ba19e4bcf0a80502afac7a4954643fc535de0b24381c91ecbf4",
|
|
13
|
+
"battlegrid-strategy-examples/references/recipes.md": "53887daece17995543e7d3fe1bf2577fc41a6254c291acacde48d7de63853641",
|
|
14
|
+
"battlegrid-strategy-examples/references/tradingview-ports.md": "2f84758ba6ec015c7db3cf996f663971a7017eddbebe42d6ecba6386c7b78362",
|
|
12
15
|
"battlegrid-trade-analysis/SKILL.md": "32573a67e55d735e6ee4e36b3b48c815694102704bc453c4d1c13db39551b3da"
|
|
13
16
|
}
|
|
14
17
|
}
|
|
@@ -223,6 +223,22 @@ with the player:
|
|
|
223
223
|
- **Tuning a single rule** — how many agents are bound, and that the change reaches every one of
|
|
224
224
|
them immediately.
|
|
225
225
|
|
|
226
|
+
**Your `ask_user` is the explanation, not the mechanism.** For single-rule tuning the server
|
|
227
|
+
independently requires `confirm:true` whenever the strategy has bound agents, so stating the radius
|
|
228
|
+
and calling anyway is refused, not committed. Send `confirm:true` only on a turn where the player
|
|
229
|
+
made an explicit confirming pick — never because you judged the edit safe. Free text typed while a
|
|
230
|
+
confirmation is open is not consent: answer it, then present the same confirmation again.
|
|
231
|
+
|
|
232
|
+
**Send only the fields you were asked to change.** `allocation`, `required` and `params` are each
|
|
233
|
+
optional and each preserves on omission. "Raise volume_surge to Critical" is
|
|
234
|
+
`{ signalId, allocation: 3 }` — nothing else. Do not read the current rule merely to restate a value
|
|
235
|
+
you are not changing, and never supply `required` unless the player asked about the Required flag.
|
|
236
|
+
Restating a remembered value is how a scoring signal silently becomes a mandatory trade gate.
|
|
237
|
+
|
|
238
|
+
**Report the change from the response.** The success payload carries `ruleChanges` with the server's
|
|
239
|
+
own `before` and `after` for the edited signal. State those. Never describe the prior value from
|
|
240
|
+
memory or from an earlier read — the response is the only record that cannot be stale.
|
|
241
|
+
|
|
226
242
|
## When apply is refused
|
|
227
243
|
|
|
228
244
|
Each of these is a specific typed code. Read it and take the cheapest correct step — never retry
|
|
@@ -241,6 +257,12 @@ the same call blindly.
|
|
|
241
257
|
moved, a shifted catalog. The approved plan **survives** these: clear the cause and confirm again
|
|
242
258
|
with the same token while it still lives. Recompiling works too, but costs the player a second
|
|
243
259
|
review they did not need.
|
|
260
|
+
- **A single-rule tuning refused for missing confirmation** — the strategy has bound agents and the
|
|
261
|
+
server will not write without `confirm:true`. The refusal names the count. Present *that* count to
|
|
262
|
+
the player and re-issue the identical call only after an explicit confirming pick. **Never add
|
|
263
|
+
`confirm:true` and retry on your own judgement** — the refusal exists precisely because the
|
|
264
|
+
decision is not yours, and a retry that supplies it unasked is the defect this rule was written
|
|
265
|
+
for. Nothing was written, so there is no partial state to reconcile.
|
|
244
266
|
- **`TOKEN_BINDING_MISMATCH` while the token is still fresh** — there is nothing left to mis-copy,
|
|
245
267
|
so this is real drift: a bound agent moved, the catalog changed, or the signing key rotated. Do
|
|
246
268
|
not resubmit. Recompile, re-present, re-confirm.
|
|
@@ -70,6 +70,18 @@ the keys `get_strategy` returned** for the sections you are keeping. Either way
|
|
|
70
70
|
one. To section-qualify a duplicated header, read the key from a preview's `conditionColumns` or
|
|
71
71
|
from the qualified candidates a `CONDITION_COLUMN_AMBIGUOUS` refusal offers.
|
|
72
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
|
+
|
|
73
85
|
## Conditions
|
|
74
86
|
|
|
75
87
|
`{ conditionKey, name, definition, verdict, required, exit, clock, closes }` — all eight
|
|
@@ -109,6 +121,17 @@ under `clock: "CLOSE"`: a LIVE reading is the forming bar, and an exit fired on
|
|
|
109
121
|
intrabar exit. Orthogonal to `required` — the two act on disjoint lifecycles, pre-entry versus
|
|
110
122
|
open — so a condition may carry both, either, or neither.
|
|
111
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
|
+
|
|
112
135
|
## Entry
|
|
113
136
|
|
|
114
137
|
`{ trigger, confirmTf, closes, bandAtrMultiple, levelSource, levelOffsetAtrMultiple,
|
|
@@ -218,167 +241,6 @@ BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25) · swing breakout (4
|
|
|
218
241
|
RR 2, BE 1R, trail 1.2R/giveback 35 · swing trend (4h) 1.0–2.5 ATR, RR 2, BE 1R, trail
|
|
219
242
|
1.5R/giveback 45–55, no timeDecay.
|
|
220
243
|
|
|
221
|
-
## Playbooks (validated compositions)
|
|
222
|
-
|
|
223
|
-
*Validated* is a gate, not a claim: every playbook below is compiled against the live catalog by
|
|
224
|
-
`strategy-playbooks.compile.test.ts`, which fails the build if one stops assembling or if a header
|
|
225
|
-
its conditions name stops resolving. Edit a playbook and the gate re-checks it; move the grammar
|
|
226
|
-
underneath one and the gate catches that too.
|
|
227
|
-
|
|
228
|
-
### 1 · Volatility Compression Breakout — 4h swing
|
|
229
|
-
|
|
230
|
-
Custom `Squeeze Scan` (no `sectionKey` — this is a CREATE): `BB_WIDTH_PCT value` + `rank lo`, `ADX value`,
|
|
231
|
-
`RVOL value`, `BB_PCT_B value`, `CLOSE_CHANGE value`, `NOTIONAL_VOLUME_1D value`; plus platform
|
|
232
|
-
`includeStructureZones`. (No `includeBollingerBands`: it renders `bbWidthPct` and
|
|
233
|
-
`bbWidthPct_rank_lo` a second time, which makes both of `SQUEEZE_ON`'s clauses ambiguous, and the
|
|
234
|
-
custom section already carries them.) Conditions: `LIQUID_FLOOR` (required —
|
|
235
|
-
`vol24hUsd gte 25000000`); `SQUEEZE_ON` building block (`bbWidthPct_rank_lo lte 10` AND
|
|
236
|
-
`ADX lt 20`); `BREAK_UP` verdict UP (ref SQUEEZE_ON + `closeChg gt 0` + `RVOL gte 1.5` +
|
|
237
|
-
`pctB gte 0.85`); `BREAK_DOWN` verdict DOWN (mirror with `pctB lte 0.15`). Rules:
|
|
238
|
-
`bollinger_squeeze` 3 required · `volume_surge` 2 required `{"multiplier":1.5}` ·
|
|
239
|
-
`volatility_atr_expanding` 2 · `sr_resistance_break` 2 · `sr_support_break` 2 ·
|
|
240
|
-
`bollinger_upper_touch` 1 · `bollinger_lower_touch` 1 · `trend_adx_ranging` 1. Gates
|
|
241
|
-
0.55 / 2 / 0.8. Levels 0.75–1.75 ATR, RR 2. PM: BE 1R; trail 1.2R giveback 35 buffer 0.3.
|
|
242
|
-
Market Read: "Trade only expansions out of compression: {SQUEEZE_ON} must read TRUE… skip
|
|
243
|
-
anything failing {LIQUID_FLOOR}."
|
|
244
|
-
|
|
245
|
-
### 2 · Crowded-Positioning Fade — 1h intraday
|
|
246
|
-
|
|
247
|
-
Custom `Positioning`: `FUNDING_RATE value`→`rate`, `aggregate w24`→`rate_mean24`,
|
|
248
|
-
`FUNDING_ANN value`→`ann`, `OI_CHG value`→`oiChg`, `OI_PX_REGIME value`→`oiRegime` (vocab:
|
|
249
|
-
new longs / new shorts / short covering / long liquidation), `MARK spread ORACLE`→
|
|
250
|
-
`mark_oracle_spread`, `RSI14 value`, `CHG_24H value`→`chg24h`; plus `includeCvd`. Conditions:
|
|
251
|
-
`NO_LIQUIDATION_TAPE` required NOT-veto (`NOT [oiRegime is "long liquidation"]`);
|
|
252
|
-
`CROWDED_LONGS` building block (`ann gte 25` + `oiChg gte 3` + `oiRegime is "new longs"`);
|
|
253
|
-
`FADE_SHORT` verdict DOWN (ref + `RSI14 gte 65` + `chg24h gte 5`); mirrored `SQUEEZED_SHORTS`
|
|
254
|
-
/ `SQUEEZE_LONG` verdict UP. Rules: `funding_extreme_positive`/`_negative` 3 required
|
|
255
|
-
`{"thresholdPct":0.001}` · `oi_surge` 2 `{"thresholdPct":0.03}` · `rsi_overbought` 2
|
|
256
|
-
`{"threshold":65}` · `rsi_oversold` 2 `{"threshold":35}` · `cvd_bear_divergence` 2 ·
|
|
257
|
-
`cvd_bull_divergence` 2 · `mfi_overbought` 1 · `mfi_oversold` 1. Gates 0.6 / 1 / 0.5. Levels
|
|
258
|
-
1.0–2.5 ATR, RR 1.5. PM: BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25).
|
|
259
|
-
|
|
260
|
-
### 3 · Relative-Strength Rotation with a Benchmark Gate — 4h cross-sectional
|
|
261
|
-
|
|
262
|
-
Custom `Leadership`: `ROC12 rank hi` + `rank lo`, `RVOL rank hi`,
|
|
263
|
-
`EMA5 spread EMA13 × trajectory w4`, `NOTIONAL_VOLUME_1D value`. **Rank `ROC12`, not
|
|
264
|
-
`CLOSE_CHANGE`:** `closeChg` is the one magnitude-only rankable code, so `hi`/`lo` on it are
|
|
265
|
-
refused, and its legal `far` ordering sorts by |change| — which would put the session's biggest
|
|
266
|
-
losers in a "leaders" cohort. Custom `BTC Regime` with
|
|
267
|
-
`"benchmarkTicker": "BTC"`: `MA_ALIGN value`→`MAalign`, `ADX value`, `REGIME_TREND value` —
|
|
268
|
-
every row reads BTC. Conditions: `BTC_RISK_ON` building block (`MAalign is "bullish"` +
|
|
269
|
-
`ADX gte 20` — both read bare, since only this section renders them); `BTC_RISK_OFF`
|
|
270
|
-
(`MAalign is "bearish"`); `LEADER` (`roc12_rank_hi lte 5` + `RVOL_rank_hi lte 10` +
|
|
271
|
-
`vol24hUsd gte 100000000`); `ROTATE_IN` verdict UP (`ALL[ref BTC_RISK_ON, ref LEADER]`);
|
|
272
|
-
`ROTATE_OUT` verdict DOWN (`ALL[ref BTC_RISK_OFF, roc12_rank_lo lte 5]`). Rules:
|
|
273
|
-
`comparison_sector_momentum` 3 · `rel_roc_positive`/`_negative` 2 · `ma_ema_aligned_bull`/
|
|
274
|
-
`_bear` 2 required · `rel_ppo_bull_cross`/`_bear_cross` 1 · `volume_surge` 1. Gates
|
|
275
|
-
0.5 / 1 / 0.6. Levels 1–2 ATR, RR 1.8. PM: BE 1R; trail 1R giveback 45. Ranked cohort ≥ 40 —
|
|
276
|
-
rank conditions need a cohort wider than their thresholds.
|
|
277
|
-
|
|
278
|
-
### 4 · HTF Trend Pullback — 1h, multi-timeframe confluence
|
|
279
|
-
|
|
280
|
-
Platform `includeMtfConfluence` (`MAalign_ltf/…/_htf`, `RSI14_*_zone`, `ADX_*_state` vocab
|
|
281
|
-
weak/developing/trending/extreme), `includeMovingAverages` (`dist_EMA20`, …), `includeRsi`
|
|
282
|
-
(`RSI14_now`, `RSI14_zone`). These three render `MAalign` and `RSI14_zone` twice between them, so
|
|
283
|
-
every condition below reads a suffixed form — never bare `MAalign` or bare `RSI14_zone`, which
|
|
284
|
-
would be ambiguous. Conditions: `HTF_UP` building block (`MAalign_htf is "bullish"` +
|
|
285
|
-
`ADX_htf_state in ["trending","extreme"]`); `HTF_DOWN` mirror; `TREND_PRESENT` **required**
|
|
286
|
-
`ANY[ref HTF_UP, ref HTF_DOWN]` — chop blocks compose-trade entirely, before billing;
|
|
287
|
-
`PULLBACK_LONG` verdict UP (ref HTF_UP + `RSI14_now between 35 55` +
|
|
288
|
-
`dist_EMA20 between -3 0.5`); `PULLBACK_SHORT` verdict DOWN (mirror, 45–65 / −0.5–3). Rules:
|
|
289
|
-
`mtf_pullback_long`/`_short` 3 required · `htf_ma_aligned_bull`/`_bear` 2 required ·
|
|
290
|
-
`htf_trend_adx_trending` 2 · `ma_ema_aligned_bull`/`_bear` 1 · `rsi_oversold` 1
|
|
291
|
-
`{"threshold":40}`. Gates 0.6 / 2 / 0.7 (a pullback signal AND an HTF alignment). Levels
|
|
292
|
-
1–2.5 ATR, RR 2. PM: BE 1R; trail 1.5R giveback 30 buffer 0.3.
|
|
293
|
-
|
|
294
|
-
### 5 · Perp/Spot Flow Divergence at Structure — 15m scalp
|
|
295
|
-
|
|
296
|
-
Platform `includePerpSpotFlow` (`perpSpotFlow` vocab: confirmed_bull / confirmed_bear /
|
|
297
|
-
perp_led_fragile / spot_led_accumulation / neutral), `includeStructureZones`
|
|
298
|
-
(`zones_htf_support_dist` signed %, support below price is negative; `_age_h`), custom `Tape`
|
|
299
|
-
(`BUY_PRESSURE value`→`buyPres` 0–1, `RVOL value`, `CLOSE_CHANGE value`). Conditions:
|
|
300
|
-
`SPOT_ACCUM` (`perpSpotFlow is "spot_led_accumulation"`); `NEAR_SUPPORT`
|
|
301
|
-
(`zones_htf_support_dist between -2 0` + `zones_htf_support_age_h gte 12`); `DIP_BID` verdict
|
|
302
|
-
UP — refs plus an `N_OF(2)` quorum over `buyPres gte 0.55` / `RVOL gte 1.2` / `closeChg gt 0`
|
|
303
|
-
(quorum beats a brittle ALL); `FRAGILE_POP` verdict DOWN (`perp_led_fragile` +
|
|
304
|
-
`zones_htf_resist_dist between 0 2` + `RVOL gte 1.3`). Rules:
|
|
305
|
-
`flow_perp_spot_bull_divergence`/`_bear_divergence` 3 required · `cvd_bull_divergence`/
|
|
306
|
-
`_bear_divergence` 2 · `sr_at_support`/`_at_resistance` 2 · `structure_ob_approach` 1 ·
|
|
307
|
-
`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
|
|
308
|
-
giveback 30 buffer 0.15; timeDecay ON aggressive (45/15, 15→60, stale 30).
|
|
309
|
-
|
|
310
|
-
## TradingView ports — familiar processes, studio vocabulary
|
|
311
|
-
|
|
312
|
-
Players often ask for strategies by the name of a popular TradingView script. Port the
|
|
313
|
-
**process** (regime filter → setup state → trigger → stop engine), and where the catalog lacks
|
|
314
|
-
the primitive, name the substitution in the spec-lock question — never present a substitute as
|
|
315
|
-
the thing itself. Most TV strategies run on the daily chart: carry that with the
|
|
316
|
-
daily-strategy pattern above (pinned-1d thesis at `offset: 1` on an intraday anchor), which
|
|
317
|
-
binds decisions to daily closes while the studio keeps managing risk intraday. Event columns (`MACD_cross`, `EMA5_13`: Bullish/Bearish) print only on the
|
|
318
|
-
crossing bar and are otherwise null → UNRESOLVED; use them as triggers inside a carrier and
|
|
319
|
-
pair with a persistent state (spread sign, `MAalign`) for regime — as the source scripts do.
|
|
320
|
-
|
|
321
|
-
- **Squeeze Momentum [LazyBear] / TTM Squeeze** → `KC_SQUEEZE is "on"` is the native one-condition
|
|
322
|
-
read: the Bollinger pair sitting inside the Keltner channel, at the script's own multiplier.
|
|
323
|
-
The four boundaries stay addressable, so `BB_UPPER spread KC_UPPER` and
|
|
324
|
-
`BB_LOWER spread KC_LOWER` still express the same reading at a threshold you choose. Release
|
|
325
|
-
direction from the MACD trajectory, `bollinger_squeeze` Critical-required.
|
|
326
|
-
**Name the anchor when you port it:** on crypto the squeeze reads *on* about 57% of 1h bars even
|
|
327
|
-
at canonical parameters — σ/ATR runs low here because Bollinger σ is close-to-close while ATR
|
|
328
|
-
captures intrabar range — against about 19% at 15m. It is a selective filter at 15m and below,
|
|
329
|
-
not at 1h. (`bbWidthPct_rank_lo` + `ADX lt 20` remains
|
|
330
|
-
a serviceable board-relative compression proxy, but it is no longer the only option.)
|
|
331
|
-
- **Supertrend / UT Bot / Chandelier Exit** → `ST_LINE` and `ST_DIR` are native, so the regime
|
|
332
|
-
half is a direct port: `ST_DIR is "bullish"` as the persistent state, `dist_ST_LINE` for
|
|
333
|
-
distance to the plotted stop. The plotted trailing line is still best executed by the studio's
|
|
334
|
-
own stop engine — `trailingTriggerR: 0` (trail from entry), giveback ~30–40 (tight factor) or
|
|
335
|
-
45–55 (loose/chandelier) — because a trailing stop is a position-management mechanism, not a
|
|
336
|
-
column. **`ST_DIR` is a persisting state, not a flip event**: it reads the same on every bar of
|
|
337
|
-
a trend, so the FLIP still needs an event column beside it (`EMA5_13` cross, or a `ST_DIR`
|
|
338
|
-
trajectory whose `_trend` changes). Name that as a substitution.
|
|
339
|
-
- **MACD + 200 MA filter** → `ABOVE_200` building block (`dist_SMA200 gt 0`) referenced by a
|
|
340
|
-
carrier with `MACD_cross is "Bullish"`; rules `macd_bull/bear_cross` 3 required +
|
|
341
|
-
`ma_sma200_above/below` 2 required; swing-trend geometry.
|
|
342
|
-
- **Golden / Death Cross** → `SMA50 spread SMA200 × trajectory` gives state and freshness:
|
|
343
|
-
`SMA50_SMA200_spread_now gt 0` AND `_trend is "rising"`; optional breadth gate on
|
|
344
|
-
`mktBreadth_crypto gte 0` — every published scope resolves, not only `all`, because the leg reads
|
|
345
|
-
the `(timeframe, scope)` pairs a condition names; position-persona geometry. `SMA50 × SMA200` is
|
|
346
|
-
the canonical definition and stays the default port; `EMA50` is available as the crypto variant
|
|
347
|
-
when a player asks for it by name.
|
|
348
|
-
- **RSI-2 (Connors)** → `RSI2` is native, so this ports exactly: `RSI2 lte 10` gated by required
|
|
349
|
-
`ABOVE_200`; the source's fast exit is time — timeDecay ON (180/60, 15→50, stale 20);
|
|
350
|
-
`rsi_oversold` 3 required with a tuned threshold. Keep the literal `lte 10` as the GATE:
|
|
351
|
-
`RSI2 × classifyZone` exists and reads on the same Connors bands, but a zone label is a fixed
|
|
352
|
-
reading while the literal is a threshold the author can see and tune. Use the zone as a report
|
|
353
|
-
column, never as the substitute for the gate.
|
|
354
|
-
- **VWAP reversion** → `dist_VWAP` band + `dist_VWAP_rank_far lte 5` for board-relative
|
|
355
|
-
stretch; required `NOT [ADX_state in ["trending","extreme"]]` veto; scalp geometry +
|
|
356
|
-
aggressive timeDecay (VWAP anchors daily at 00:00 UTC).
|
|
357
|
-
- **Donchian / Turtle breakout** → `zone is "breakout high"` + `dist_swingHi gte 0` +
|
|
358
|
-
`RVOL gte 1.5`; `sr_resistance_break` 3 required; turtle exits = trend preset (trail from
|
|
359
|
-
1R, giveback 50). Mirror with `"breakdown low"`. **Daily-breakout variant on any anchor:**
|
|
360
|
-
pin the structure at 1d — `zone_1d is "breakout high"`, `dist_swingHi_1d gte 0` (validated).
|
|
361
|
-
**Literal previous-day levels are native**: `PDH` and `PDL` are catalogued price levels, so
|
|
362
|
-
`dist_PDH gte 0` composes directly. They are ANCHORED to `1d` — bind `{abs: '1d'}`, which is the
|
|
363
|
-
only reference the save path accepts on them, and read them from any anchor that way. `distance`
|
|
364
|
-
still rejects an `offset` and a clause still compares a column against a literal; neither of
|
|
365
|
-
those shapes is what a previous-day level needed.
|
|
366
|
-
- **ICT / SMC (FVG + order blocks)** → `STRUCT_ZONES` is the native zone engine:
|
|
367
|
-
`zones_htf_support_type` (`bullish FVG`/`bullish order block`), `zones_htf_support_dist
|
|
368
|
-
between -1.5 0`, `_age_h gte 12`, HTF bias required via `MAalign_htf`; rules
|
|
369
|
-
`structure_fvg_approach`/`structure_ob_approach` required (their `proximityPct` is the
|
|
370
|
-
in-zone dial). Liquidity sweeps, displacement, killzones and event *sequencing* are shapes the
|
|
371
|
-
grammar does not have — a clause compares one column against a literal, so an ordered sequence
|
|
372
|
-
of events cannot be stated at all. That is a grammar limit, not a missing metric; name it as
|
|
373
|
-
one.
|
|
374
|
-
- **Now native, formerly substituted** — WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`),
|
|
375
|
-
Hull (`HMA20`), Ichimoku (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`),
|
|
376
|
-
Parabolic SAR (`PSAR`), Keltner (`KC_UPPER`/`KC_MID`/`KC_LOWER`), daily pivots
|
|
377
|
-
(`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`), Williams %R (`WILLR14`), Stochastic RSI
|
|
378
|
-
(`STOCH_RSI14`), the TTM squeeze (`KC_SQUEEZE`), Connors RSI-2 (`RSI2`), the 9/21/50 EMAs
|
|
379
|
-
(`EMA9`/`EMA21`/`EMA50`), and literal previous-day levels (`PDH`/`PDL`). Port these directly —
|
|
380
|
-
do not offer a substitute for a primitive the catalog serves.
|
|
381
|
-
|
|
382
244
|
## Not expressible — the catalog keys this needs
|
|
383
245
|
|
|
384
246
|
The one place a claim that the catalog LACKS something may live, and every row names the key it
|
|
@@ -390,12 +252,19 @@ permanent. A claim that a metric is absent belongs here, or nowhere.
|
|
|
390
252
|
|---|---|---|
|
|
391
253
|
| 100-period SMA | `SMA100` | the shipped 50- or 200-period simple average, whichever the thesis leans on |
|
|
392
254
|
|
|
393
|
-
##
|
|
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.
|
|
394
268
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
calibrate literals against the previewed live values → compile once → review the compiled
|
|
398
|
-
scorecard, condition outcomes, verdict tally, and `marketReadMarkers` (fix `unknown` /
|
|
399
|
-
`ambiguous` markers) → apply per the strategy-authoring flow. Coin selection is call context,
|
|
400
|
-
not strategy state: explicit tickers for focused work, `ranked` (with an optional category) for
|
|
401
|
-
scanning books.
|
|
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.
|