@battlegrid/mcp-server 32.0.18 → 32.0.21
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 +201 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +4 -4
- package/skills/battlegrid-strategy-examples/SKILL.md +19 -19
- package/skills/battlegrid-strategy-examples/references/playbooks.md +8 -8
- package/skills/battlegrid-strategy-examples/references/tradingview-ports.md +30 -14
package/README.md
CHANGED
|
@@ -38,6 +38,207 @@ Seeing package `31.x` alongside handshake `battlegrid@33.x` — the package **be
|
|
|
38
38
|
|
|
39
39
|
**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.
|
|
40
40
|
|
|
41
|
+
## Contract history — v127 (a read serves what its code computes, and says so)
|
|
42
|
+
|
|
43
|
+
**Breaking at v127: two answers narrow for calls you could already make, and every radar journal row gains one
|
|
44
|
+
field.** v127 is the contract on top of v126. No input you could send is refused. Reconnect the proxy, or restart it,
|
|
45
|
+
so discovery reads the new descriptions and schemas.
|
|
46
|
+
|
|
47
|
+
### Narrowed answer — a call you made now returns less
|
|
48
|
+
|
|
49
|
+
- **`get_trade_chart` and `get_public_agent_trade_chart` return `UNAVAILABLE` for a CLOSED trade with no stored
|
|
50
|
+
chart.** A read used to capture the chart on the spot and answer `READY`; it no longer writes anything or fetches
|
|
51
|
+
candles. The chart is captured once, by a background job after the trade closes (it retries for about two and a
|
|
52
|
+
half hours). `UNAVAILABLE` now covers: no entry decision or no position; a position that has not closed; and a
|
|
53
|
+
closed position whose capture is still waiting out its delay, failed, or predates capture. Trades closed before
|
|
54
|
+
2026-07-28 without a chart stay `UNAVAILABLE`.
|
|
55
|
+
- **`get_agent_explorer` returns 25 entries by default** (was 100). `limit` still accepts 1-100; pass it to ask for
|
|
56
|
+
more. A result over the result cap is refused with `nextAct: narrow` — ask again with a smaller `limit`.
|
|
57
|
+
|
|
58
|
+
### Added output — a field you can read
|
|
59
|
+
|
|
60
|
+
- **`signalLogId` on every `get_radar_activity` row and every `get_radar_activity_summary` recent row.** On a
|
|
61
|
+
`FIRE_DISPOSITION` row whose disposition is `FIRED`, it names the signal log that fire's evaluation wrote — open it
|
|
62
|
+
with `get_signal_log({ agentId, logId })`. It is `null` when that evaluation wrote none (screened out or blocked
|
|
63
|
+
before a log existed) and on every other row. A strict-schema client accepts the new key on each variant.
|
|
64
|
+
|
|
65
|
+
### Corrected answers — same shape, true values
|
|
66
|
+
|
|
67
|
+
- **`get_intelligence_agent`** and the receipts of create, update, activate and archive serve your rolling-24h LLM
|
|
68
|
+
spend on the agent (`last24hCostUsd`), your own active Arena deployments of it (`activeGameCount` — another
|
|
69
|
+
user's deployments of a platform agent are not counted) and `hasActiveAssignments`, the values
|
|
70
|
+
`list_intelligence_agents` serves. They used to serve `0`, `0` and `false`.
|
|
71
|
+
- **`get_agent_decision_context`** serves the open position's real `signalLogId` (it served `''`). It answers only
|
|
72
|
+
for an agent position that is OPEN on the coin; for a coin that did not trade, read `list_gate_blocks` and
|
|
73
|
+
`get_radar_activity_summary`.
|
|
74
|
+
- **`get_radar_activity`'s `FIRES` filter** keeps each fire's `EVALUATION_OUTCOME` row beside its disposition — the
|
|
75
|
+
only record of a fire refused before it wrote a signal log.
|
|
76
|
+
|
|
77
|
+
### Declared sizes — the result cap is announced
|
|
78
|
+
|
|
79
|
+
- `get_radar_activity` (narrow with `limit`, `detail` or `includeCurve`), `get_agent_explorer` (narrow with `limit`),
|
|
80
|
+
`get_public_agent_signal_log_detail` and `list_radar_deployments` (read one coin with `get_radar_deployment`)
|
|
81
|
+
carry `_meta["anthropic/maxResultSizeChars"]`.
|
|
82
|
+
|
|
83
|
+
### Corrected descriptions
|
|
84
|
+
|
|
85
|
+
`get_signal_log` names every block with its null case; `get_public_agent_signal_log_detail` states that a FAILED log
|
|
86
|
+
returns `log: null`; `get_radar_deployment` says a null `minConviction` is no floor (never inherited) and
|
|
87
|
+
`resolvedConviction` is the slot's own bar; `get_radar_activity_summary` names `ENTRY_VOID` and its 168-hour window;
|
|
88
|
+
`list_radar_deployments` names `summary.radarPaused`; `get_regime_snapshot`'s 4h is a default only — a Radar slot
|
|
89
|
+
reads its agent's own regime timeframe.
|
|
90
|
+
|
|
91
|
+
## Contract history — v126 (a queued entry executes at its close; nothing waits for an approval)
|
|
92
|
+
|
|
93
|
+
**Breaking at v126: three entry-decision tools leave the catalog you discover, two outputs narrow, and one status
|
|
94
|
+
value is renamed.** v126 is the contract on top of v125. Reconnect the proxy, or restart it, so discovery reads the
|
|
95
|
+
new catalog.
|
|
96
|
+
|
|
97
|
+
A request you queue with `propose_entry_decision` is now an entry the player has already consented to: at the
|
|
98
|
+
agent's decided strategy-bar close, a qualifying entry is placed at once, sized by the agent's own risk budget, with
|
|
99
|
+
no further step. Confirm with the player before calling it — their ask to trade that coin with that agent, or a
|
|
100
|
+
standing instruction to trade for them, is the consent. The `mcp:wager` grant is that standing permission for an MCP
|
|
101
|
+
client, and the consent screen says so.
|
|
102
|
+
|
|
103
|
+
### Removed tools — something you called is gone
|
|
104
|
+
|
|
105
|
+
A call naming one is answered as an unknown tool; there is no alias, and none has a replacement call.
|
|
106
|
+
|
|
107
|
+
- **`accept_entry_decision`**: nothing waits to be accepted. The entry executes at its decided close; consent comes
|
|
108
|
+
before `propose_entry_decision`.
|
|
109
|
+
- **`cancel_entry_decision`**: a decided entry is not cancelled. Withdraw a queued request with
|
|
110
|
+
`cancel_entry_request({ requestId })` until its close fires.
|
|
111
|
+
- **`list_pending_approvals`**: read a pending request with `get_entry_request({ requestId })`, and placing and live
|
|
112
|
+
positions with `list_user_active_positions()`.
|
|
113
|
+
|
|
114
|
+
### Narrowed output — a shape you read has fewer members
|
|
115
|
+
|
|
116
|
+
- **`propose_entry_decision`** (now titled *Queue Entry*) returns `result` as `queued | error`. The `recommendation`
|
|
117
|
+
and `no_trade` members are gone; a decided close's answer lands in the request's conversation, read with
|
|
118
|
+
`get_trade_conversation`.
|
|
119
|
+
- **`get_agents_hub`** publishes no approval: `hubStatus` is `IN_TRADE`, `RADAR_ARMED`, `ARENA_DEPLOYED` or `IDLE`
|
|
120
|
+
(in that precedence), rows have no `pendingApprovalCount`, and the summary has no `totalPendingApprovalCount`.
|
|
121
|
+
|
|
122
|
+
### Renamed value — a word you matched changed
|
|
123
|
+
|
|
124
|
+
- The execution status **`AWAITING_APPROVAL` is `PROPOSED`** wherever it is published (`get_signal_log`,
|
|
125
|
+
`get_public_agent_signal_log_detail`): a position whose order is being placed. Match `PROPOSED` where you matched
|
|
126
|
+
`AWAITING_APPROVAL`.
|
|
127
|
+
|
|
128
|
+
### Re-specified behavior
|
|
129
|
+
|
|
130
|
+
- **A queued request's entry executes at its decided close.** Its conversation's `recommendation` card is the entry
|
|
131
|
+
locator: it names the decision (`get_entry_decision`) and the position the request placed.
|
|
132
|
+
- **Every request that is not withdrawn is answered once.** A request whose deadline passed unanswered is answered by
|
|
133
|
+
the next sweep — `close_missed` for its bar, or the engine's `EVALUATION_FAULTED` for a fired one whose decision job
|
|
134
|
+
is gone — and reads as pending through `get_entry_request` until then.
|
|
135
|
+
|
|
136
|
+
## Contract history — v125 (MCP serves the read the client shows)
|
|
137
|
+
|
|
138
|
+
**Breaking at v125: nine trade and signal tools leave the catalog you discover, one tool joins it, two outputs
|
|
139
|
+
reshape, one tool refuses three inputs, one output field and three ack fields are gone.** v125 is the contract on top
|
|
140
|
+
of v124. Reconnect the proxy, or restart it, so discovery reads the new catalog.
|
|
141
|
+
|
|
142
|
+
### Removed tools — something you called is gone
|
|
143
|
+
|
|
144
|
+
Each was a parallel, narrower or dead read beside the one the web client shows. A call naming one is answered as an
|
|
145
|
+
unknown tool; there is no alias.
|
|
146
|
+
|
|
147
|
+
- **`list_trade_outcomes`**: call `list_agent_trades({ agentId })`, then
|
|
148
|
+
`get_trade_outcome_by_decision({ decisionId })` for a trade's fills, fees, gross P&L, slippage, leverage,
|
|
149
|
+
`closedBy`, duration, excursion and `exitRepriceSource`.
|
|
150
|
+
- **`get_signal_performance`**: call `get_public_agent_signal_performance({ agentId })`. The owner's slice is
|
|
151
|
+
`list_signal_logs({ agentId }).summary` and `list_agent_trades({ agentId }).summary`.
|
|
152
|
+
- **`get_agent_open_positions`**: call `get_public_agent_open_positions({ agentId, filter: { coinTicker: [...] } })`.
|
|
153
|
+
- **`get_public_agent_unrealized_pnl`**: call `get_public_agent_open_positions({ agentId, filter: { signalSource } })`;
|
|
154
|
+
its `unrealizedPnlUsd` is the figure.
|
|
155
|
+
- **`list_session_agent_positions`**: call `list_user_active_positions()`; each position carries
|
|
156
|
+
`marketGridSessionId`.
|
|
157
|
+
- **`get_order_status`**: call `list_agent_open_orders()` for the platform's record, `get_open_orders()` for the
|
|
158
|
+
venue's.
|
|
159
|
+
- **`get_decision_order_attribution`**: call `list_agent_open_orders()`, `list_user_active_positions()` and
|
|
160
|
+
`list_agent_trades()`; each row names its `decisionId`.
|
|
161
|
+
- **`list_entry_decisions`**: call `list_signal_logs({ agentId })`, then `get_signal_log({ agentId, logId })`.
|
|
162
|
+
- **`get_agent_fund_allocation`**: call `get_agent_budget({ agentId })` for `haltedAt`, `haltReason` and
|
|
163
|
+
`perTradePushEnabled`.
|
|
164
|
+
|
|
165
|
+
### Added tool and fields
|
|
166
|
+
|
|
167
|
+
- **`get_public_agent_open_positions({ agentId, filter })`** serves an agent's open book, newest open first. Each
|
|
168
|
+
row carries its decided `stopLoss` and `takeProfit` and its live `mark` (`unrealizedPnlUsd`, `movePercent`,
|
|
169
|
+
`roePercent`; `null` when the coin has no usable mark), beside the marked rows' `unrealizedPnlUsd` and an
|
|
170
|
+
`unpricedPositionCount`. The row's open instant is `openedAt`; `leverage` and `signalLogId` are never null, and
|
|
171
|
+
there is no `timeHorizon`. An archived or unknown agent reads as no positions.
|
|
172
|
+
- **`list_agent_trades`** rows carry `decisionId`, the key `get_trade_outcome_by_decision` takes. The tool accepts
|
|
173
|
+
`q` (1–64 characters after trimming).
|
|
174
|
+
- **`list_agent_open_orders`** rows carry `exchangeOrderId` (`null` until the venue acknowledges), `cloid` and
|
|
175
|
+
`decisionId`, so an agent order joins `get_open_orders` and chains to its decision.
|
|
176
|
+
|
|
177
|
+
### Reshaped output — something you read moved
|
|
178
|
+
|
|
179
|
+
- **`list_signal_logs`** serves the My Agents Signals tab's read: `{ signals, nextCursor, summary }` across every one
|
|
180
|
+
of your agents unless `agentId` narrows it. Each row names its agent and carries `rejection`, the reason and detail
|
|
181
|
+
of the signal's rejected agent position, `null` otherwise.
|
|
182
|
+
- **`list_user_active_positions`** serves every position once, in `positions`. Each `agents` entry keeps its group
|
|
183
|
+
figures and loses its nested `positions`.
|
|
184
|
+
|
|
185
|
+
### Rejected input — a key you sent is refused
|
|
186
|
+
|
|
187
|
+
- **`list_signal_logs`** refuses `direction`, `terminalStatus` and `signalSource` as unrecognised keys. Its `agentId`
|
|
188
|
+
is optional, and it accepts `coinTicker`, `q`, `cursor` and `limit` (1–50, default 10).
|
|
189
|
+
|
|
190
|
+
### Removed fields — a value you read is gone
|
|
191
|
+
|
|
192
|
+
- **`skippedCount`** leaves `get_public_agent_signal_performance`; it was always `0`. `skipCount` counts the SKIP
|
|
193
|
+
decisions, and `blockedCount` counts the agent's ineligible attempts.
|
|
194
|
+
- **`availableUsd`, `lifetimeAllocatedUsd` and `lifetimeRecalledUsd`** leave the `allocation` that
|
|
195
|
+
`halt_intelligence_agent`, `resume_intelligence_agent` and `set_agent_per_trade_push` return; it is now
|
|
196
|
+
`{ agentId, haltedAt, perTradePushEnabled }`. The three figures were zero on every agent.
|
|
197
|
+
|
|
198
|
+
### Declared result size
|
|
199
|
+
|
|
200
|
+
- **`list_signal_logs` and `list_agent_trades` declare the configured result cap.** A page past it is refused with
|
|
201
|
+
`nextAct: narrow`; ask again with a smaller `limit`.
|
|
202
|
+
|
|
203
|
+
## Contract history — v124 (an agent's record is its thought log and its activity feed)
|
|
204
|
+
|
|
205
|
+
**Breaking at v124: three agent-record tools leave the catalog you discover, and a thought-log entry loses a
|
|
206
|
+
field.** v124 is the contract on top of v123.2.0. Reconnect the proxy, or restart it, so discovery reads the new
|
|
207
|
+
catalog.
|
|
208
|
+
|
|
209
|
+
### Removed tools — something you called is gone
|
|
210
|
+
|
|
211
|
+
Each was a duplicate door onto the two per-agent reads. A call naming one is answered as an unknown tool; there is
|
|
212
|
+
no alias.
|
|
213
|
+
|
|
214
|
+
- **`get_agent_journal({ agentId, limit })`**: call `get_agent_thought_log({ agentId, limit })` and
|
|
215
|
+
`get_agent_activity_feed({ agentId, limit })`. Read the owner's `username` from `get_account_state()`.
|
|
216
|
+
- **`get_user_thought_log`**: call `get_agent_thought_log({ agentId })` for each agent
|
|
217
|
+
`list_intelligence_agents()` lists. Read a trade's reasoning from `get_signal_log`, and an arena entry's reasoning
|
|
218
|
+
from `list_my_market_grid_entries` and `get_my_market_grid_entry`.
|
|
219
|
+
- **`get_user_activity_feed`**: call `get_agent_activity_feed({ agentId })` for each agent
|
|
220
|
+
`list_intelligence_agents()` lists.
|
|
221
|
+
|
|
222
|
+
### Reshaped output — a field you read is gone
|
|
223
|
+
|
|
224
|
+
- **`get_agent_thought_log` entries no longer carry `marketSnapshot`.** For an arena decision it held the agent's
|
|
225
|
+
whole resolved prompt, larger than the result cap, so a page holding one could not be served at all, even at
|
|
226
|
+
`limit: 1`, and no later entry could be reached. Each entry still carries its outcome, written reasoning, grid pick
|
|
227
|
+
and parsed picks, confidence and the floor enforced against it, its usage-event link and its creation instant.
|
|
228
|
+
|
|
229
|
+
### Declared result size
|
|
230
|
+
|
|
231
|
+
- **`get_agent_thought_log` declares the configured result cap.** A page past it is refused with
|
|
232
|
+
`nextAct: narrow`; ask again with a smaller `limit`.
|
|
233
|
+
|
|
234
|
+
### Descriptions
|
|
235
|
+
|
|
236
|
+
- `get_agent_thought_log` names both decisions it records: an arena grid decision and a trade thesis
|
|
237
|
+
(`AGENT_TRADE_THESIS`, whose full pipeline is `get_signal_log`).
|
|
238
|
+
- `get_agent_activity_feed` names the event families its rows carry: arena auto-submit triggers and grid outcomes,
|
|
239
|
+
cost-limit and insufficient-funds blocks, funds committed to a trade or a wager, halts and admission blocks, and
|
|
240
|
+
lifecycle events.
|
|
241
|
+
|
|
41
242
|
## Contract history — v122 (only an officer's own turn records a review)
|
|
42
243
|
|
|
43
244
|
**Breaking at v122: two officer review tools leave the catalog you discover.** v122 is the contract on top of v121.
|
package/dist/index.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/cli
|
|
|
47
47
|
* being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
|
|
48
48
|
* correction. Never move it to track the server.
|
|
49
49
|
*/
|
|
50
|
-
export declare const PACKAGE_VERSION = "32.0.
|
|
50
|
+
export declare const PACKAGE_VERSION = "32.0.21";
|
|
51
51
|
export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
|
|
52
52
|
export interface EnvConfig {
|
|
53
53
|
apiKeys: string[];
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ import { Client, SdkHttpError, StreamableHTTPClientTransport } from '@modelconte
|
|
|
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 const PACKAGE_VERSION = '32.0.
|
|
52
|
+
export const PACKAGE_VERSION = '32.0.21';
|
|
53
53
|
export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
|
|
54
54
|
const MAX_RETRIES = 3;
|
|
55
55
|
const RETRY_DELAYS_MS = [2000, 4000, 8000];
|
package/package.json
CHANGED
package/skills/EXPORT.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "123.1.0",
|
|
4
4
|
"files": {
|
|
5
5
|
"battlegrid-agent-management/SKILL.md": "4c2cb5cfec27cf11769d66763e024e6900c392e27a978de3080cf55b2ece7a68",
|
|
6
6
|
"battlegrid-agent-management/references/agent-stage-examples.md": "dffcc353d61dfc7b5bb6a2b3077fc6bf8c760a34c287cafb3b59b99306f24195",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"battlegrid-strategy-authoring/SKILL.md": "39cfa46b1f640e6800edb7e85a8f2c4b6b8786cddf71bb8244d172a44fc11d75",
|
|
16
16
|
"battlegrid-strategy-authoring/references/strategy-stage-examples.md": "f10ecdc91f3660a708eee7a7bacefce2acbb46c9cf7e0ac1454bd54f839f39fd",
|
|
17
17
|
"battlegrid-strategy-doctor/SKILL.md": "e34e055b0cc9b355bc1adeb5d49d464eaa0756ca3f48dc89ca2ccdda94e1311f",
|
|
18
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
19
|
-
"battlegrid-strategy-examples/references/playbooks.md": "
|
|
18
|
+
"battlegrid-strategy-examples/SKILL.md": "9d18bf0e4bf85a28508e2881a4cdf77b823ec831e7cec5d82f82dc251402f41c",
|
|
19
|
+
"battlegrid-strategy-examples/references/playbooks.md": "a9464789df2e762faf9dbd7845101564bc1847cd193f709f8ac4b6bff08f09dd",
|
|
20
20
|
"battlegrid-strategy-examples/references/recipes.md": "806aae0b4842408f6e6ed3e108293073f4f31d1c27fa419ad57df9e1134f23e7",
|
|
21
|
-
"battlegrid-strategy-examples/references/tradingview-ports.md": "
|
|
21
|
+
"battlegrid-strategy-examples/references/tradingview-ports.md": "c62ce72b741abe5a92e1dec6615f26b19a54959aa1d29542518cd9a189390b98",
|
|
22
22
|
"battlegrid-trade-analysis/SKILL.md": "3f927b4f9796178526fa1673a2043c5f14358c39aa955de33192ea58641f18fb",
|
|
23
23
|
"battlegrid-trade-proposal/SKILL.md": "e761fc6bddc0d1db79fcb90046d03a18439562c8658de726ec286830edd6369b"
|
|
24
24
|
}
|
|
@@ -32,7 +32,7 @@ Before committing a CREATE, every "no" here is a decision to state, not an omiss
|
|
|
32
32
|
|
|
33
33
|
| Transform | Header | Validated example |
|
|
34
34
|
|---|---|---|
|
|
35
|
-
| `value` | `<code>` | `bbWidthPct`, `
|
|
35
|
+
| `value` | `<code>` | `bbWidthPct`, `RVOL20`, `rate` |
|
|
36
36
|
| `trajectory` w4 | `<code>_t3…_t1`, `_now`, `_trend` (rising/falling/flat) | `RSI14_now`, `RSI14_trend` |
|
|
37
37
|
| `distance` | `dist_<code>` (signed % from price) | `dist_SMA50` |
|
|
38
38
|
| `spread` | `<base>_<operand>_spread` | `mark_oracle_spread` |
|
|
@@ -40,8 +40,8 @@ Before committing a CREATE, every "no" here is a decision to state, not an omiss
|
|
|
40
40
|
| `rank` | `<code>_rank_<hi\|lo\|far\|near>` — ordinal, `lte N` = top-N | `bbWidthPct_rank_lo` |
|
|
41
41
|
| `efficiency` wN | `<code>_er` (1 straight, ~0 chop) | `close_ltf_er` |
|
|
42
42
|
| `maxShare` wN | `<code>_maxShare` | `volBase_ltf_maxShare` |
|
|
43
|
-
| `classifyZone` / `classifyState` | `<code>_zone` / `<code>_state` | `RSI14_zone`, `
|
|
44
|
-
| `levelInteraction` | `<code>_lvl`, `<code>_lvl_b<N>` at a buffer of N bps | `
|
|
43
|
+
| `classifyZone` / `classifyState` | `<code>_zone` / `<code>_state` | `RSI14_zone`, `ADX14_state` |
|
|
44
|
+
| `levelInteraction` | `<code>_lvl`, `<code>_lvl_b<N>` at a buffer of N bps | `bbUpper20_lvl`, `bbUpper20_lvl_b25` |
|
|
45
45
|
| any, stored `"bars": "all"` | `<code>_dev`, then the transform's suffix | `RSI14_dev_t1` |
|
|
46
46
|
| `value` at `offset` N > 0 | `<code>_o<N>` | `RSI14_1d_o1` |
|
|
47
47
|
|
|
@@ -151,16 +151,16 @@ Recipes — each one clause:
|
|
|
151
151
|
`dist_SMA20` is the close's distance from the average, so crossing 0 is the close crossing it.
|
|
152
152
|
- **RSI entered overbought** — `RSI14_zone enters "overbought"` (the RSI module).
|
|
153
153
|
- **Squeeze released** — `kcSqueeze exits "on"`, over a `KC_SQUEEZE` value column.
|
|
154
|
-
- **New 20-bar high** — `
|
|
154
|
+
- **New 20-bar high** — `donchianHi20 increased`, over a `DONCHIAN_UPPER` value column: the 20-bar
|
|
155
155
|
upper channel rises exactly when the decided bar's high exceeds every high of the 20 bars before it.
|
|
156
|
-
- **Close above the prior 20-bar high (Donchian / Turtle breakout)** — `
|
|
156
|
+
- **Close above the prior 20-bar high (Donchian / Turtle breakout)** — `dist_donchianHiPrior20 crossesAbove 0`,
|
|
157
157
|
over a `DONCHIAN_UPPER_PRIOR × distance` column: Pine's `ta.crossover(close, ta.highest(high, 20)[1])`,
|
|
158
|
-
TRUE on the first close of a breakout run. `
|
|
158
|
+
TRUE on the first close of a breakout run. `dist_donchianHiPrior20 gt 0` is Pine's
|
|
159
159
|
`close > ta.highest(high, 20)[1]` and holds on every close of the run. Mirror with
|
|
160
|
-
`
|
|
161
|
-
- **Failed breakout of the prior 20-bar high** — `
|
|
160
|
+
`dist_donchianLoPrior20 crossesBelow 0` / `lt 0` over `DONCHIAN_LOWER_PRIOR`.
|
|
161
|
+
- **Failed breakout of the prior 20-bar high** — `donchianHiPrior20_lvl is "reject_from_below"`, over a
|
|
162
162
|
`DONCHIAN_UPPER_PRIOR × levelInteraction` column: the high pierced the prior 20-bar high and the close
|
|
163
|
-
came back under it. `
|
|
163
|
+
came back under it. `dist_donchianHiPrior20 crossesBelow 0` is NOT "fell back into the channel": the rail
|
|
164
164
|
rises to each breakout bar's high, so it fires once on the first bar of a breakout run that does not
|
|
165
165
|
close above the raised rail — the run of new closing highs ended.
|
|
166
166
|
- **RSI crossed above 50 within the last 3 closes** — `RSI14_now crossesAbove 50` holding
|
|
@@ -218,8 +218,8 @@ Inline it when:
|
|
|
218
218
|
`conditionRef`, so "together on 3 closes in a row" over a sub-rule needs its clauses inline.
|
|
219
219
|
|
|
220
220
|
Inlining a `NOT` is not always a flipped operator. A clause whose read ran and found no value reads
|
|
221
|
-
FALSE, so `NOT [
|
|
222
|
-
`
|
|
221
|
+
FALSE, so `NOT [dist_donchianHi20 gt -0.52]` reads TRUE on a coin with no channel value while
|
|
222
|
+
`dist_donchianHi20 lte -0.52` reads FALSE; `NOT [CVD_state is "bearish"]` and
|
|
223
223
|
`CVD_state in ["bullish", "neutral"]` part the same way. Flip only a header that always carries a
|
|
224
224
|
value, or choose deliberately which answer a missing value gives.
|
|
225
225
|
|
|
@@ -370,7 +370,7 @@ refuses a commit or a rebind.
|
|
|
370
370
|
reads the same on every bar of a trend, so `ST_DIR is "bullish"` is a regime filter and never an
|
|
371
371
|
entry signal. The flip is `ST_DIR enters "bullish"`: TRUE on the one completed bar the direction
|
|
372
372
|
turned, with no event column needed. The same distinction applies to every persisting
|
|
373
|
-
classification: `MAalign`, `
|
|
373
|
+
classification: `MAalign`, `ADX14_state`, `zone` — `is` for the regime, `enters` / `exits` for the
|
|
374
374
|
bar it changed.
|
|
375
375
|
|
|
376
376
|
**Name the anchor when a metric is calibrated for one.** `KC_SQUEEZE is "on"` reads *on* about 57% of
|
|
@@ -397,12 +397,12 @@ reached the level and whose close came back under it is `reject_from_below`; a b
|
|
|
397
397
|
within the buffer of the level is `touch_from_below`. From above is the mirror on the low.
|
|
398
398
|
|
|
399
399
|
- **A reject bar reports reject.** It is never also reported as a touch, so "touched or rejected" is
|
|
400
|
-
an `in` clause over both labels of a side: `
|
|
400
|
+
an `in` clause over both labels of a side: `bbUpper20_lvl in ["touch_from_below","reject_from_below"]`.
|
|
401
401
|
- **`buffer`** is whole basis points of the level, 0–500, default 0 — `buffer: 25` is 0.25%. Only the
|
|
402
|
-
touch reads it: a reject must pierce the level itself. Two buffers are two columns (`
|
|
403
|
-
`
|
|
402
|
+
touch reads it: a reject must pierce the level itself. Two buffers are two columns (`bbUpper20_lvl`,
|
|
403
|
+
`bbUpper20_lvl_b25`).
|
|
404
404
|
- **A break is a crossing, never a label.** The break of a level is `distance` over the same level
|
|
405
|
-
crossing 0: `
|
|
405
|
+
crossing 0: `dist_bbUpper20 crossesAbove 0` from below, `crossesBelow 0` from above. On the strategy's
|
|
406
406
|
own rung a `…_from_below` label never holds on a bar where `crossesAbove 0` holds.
|
|
407
407
|
- **Which levels take it.**
|
|
408
408
|
- **Offered:** a candle-homed level — a band (`BB_UPPER`, `BB_LOWER`, `KC_UPPER`, `KC_LOWER`),
|
|
@@ -420,9 +420,9 @@ within the buffer of the level is `touch_from_below`. From above is the mirror o
|
|
|
420
420
|
(`DONCHIAN_UPPER`, `DONCHIAN_LOWER`), each the extreme of the window ending at the very bar it would
|
|
421
421
|
classify, which a close can never pass — read `DONCHIAN_UPPER_PRIOR` / `DONCHIAN_LOWER_PRIOR`. A
|
|
422
422
|
price fact (`CLOSE`, `HLC3`, `MARK`) is refused: it is the price, not a level.
|
|
423
|
-
- **A higher-rung level reads that rung's completed bar.** `
|
|
423
|
+
- **A higher-rung level reads that rung's completed bar.** `bbUpper20_htf_lvl` classifies the last
|
|
424
424
|
completed regime-rung bar from its own high, low and close, never the strategy bar's price — so
|
|
425
|
-
compose its break on the same rung, `
|
|
425
|
+
compose its break on the same rung, `dist_bbUpper20_htf crossesAbove 0`.
|
|
426
426
|
|
|
427
427
|
**`BB_TOUCH` and `BB_UPPER × levelInteraction` answer different questions.** `BB_TOUCH` (`BBtouch`)
|
|
428
428
|
says where the CLOSE sits in %B — inside the band edge or not. `BB_UPPER × levelInteraction` says what
|
|
@@ -464,7 +464,7 @@ within N reads each of the N bars against its own predecessor.
|
|
|
464
464
|
Recipes — the level recipes on candle-homed levels:
|
|
465
465
|
|
|
466
466
|
- **Price rejected the upper Bollinger band** — a `BB_UPPER × levelInteraction` column, header
|
|
467
|
-
`
|
|
467
|
+
`bbUpper20_lvl`; the clause `bbUpper20_lvl is "reject_from_below"`.
|
|
468
468
|
- **Price touched or rejected VWAP from above** — `VWAP_lvl in ["touch_from_above","reject_from_above"]`,
|
|
469
469
|
with `buffer: 10` for a touch within 0.1% (header `VWAP_lvl_b10`).
|
|
470
470
|
- **Price broke the 20 EMA** — `dist_EMA20 crossesAbove 0`, the `distance` column over the same level.
|
|
@@ -25,8 +25,8 @@ Custom `Squeeze Scan` (no `sectionKey` — this is a CREATE): `BB_WIDTH_PCT valu
|
|
|
25
25
|
`bbWidthPct_rank_lo` a second time, which makes both of `SQUEEZE_ON`'s clauses ambiguous, and the
|
|
26
26
|
custom section already carries them.) Conditions: `LIQUID_FLOOR` (required —
|
|
27
27
|
`vol24hUsd gte 25000000`); `SQUEEZE_ON` building block (`bbWidthPct_rank_lo lte 10` AND
|
|
28
|
-
`
|
|
29
|
-
`
|
|
28
|
+
`ADX14 lt 20`); `BREAK_UP` verdict UP (ref SQUEEZE_ON + `closeChg gt 0` + `RVOL20 gte 1.5` +
|
|
29
|
+
`pctB20 gte 0.85`); `BREAK_DOWN` verdict DOWN (mirror with `pctB20 lte 0.15`). Levels 0.75–1.75 ATR,
|
|
30
30
|
RR 2, ATR floor 0.8% — a compression play needs the expansion to be worth the stop. PM: BE 1R; trail 1.2R giveback 35 buffer 0.3.
|
|
31
31
|
Market Read: "Trade only expansions out of compression: {SQUEEZE_ON} must read TRUE… skip
|
|
32
32
|
anything failing {LIQUID_FLOOR}."
|
|
@@ -54,8 +54,8 @@ refused, and its legal `far` ordering sorts by |change| — which would put the
|
|
|
54
54
|
losers in a "leaders" cohort. Custom `BTC Regime` with
|
|
55
55
|
`"benchmarkTicker": "BTC"`: `MA_ALIGN value`→`MAalign`, `ADX value`, `REGIME_TREND value` —
|
|
56
56
|
every row reads BTC. Conditions: `BTC_RISK_ON` building block (`MAalign is "bullish"` +
|
|
57
|
-
`
|
|
58
|
-
(`MAalign is "bearish"`); `LEADER` (`roc12_rank_hi lte 5` + `
|
|
57
|
+
`ADX14 gte 20` — both read bare, since only this section renders them); `BTC_RISK_OFF`
|
|
58
|
+
(`MAalign is "bearish"`); `LEADER` (`roc12_rank_hi lte 5` + `RVOL20_rank_hi lte 10` +
|
|
59
59
|
`vol24hUsd gte 100000000`); `ROTATE_IN` verdict UP (`ALL[ref BTC_RISK_ON, ref LEADER]`);
|
|
60
60
|
`ROTATE_OUT` verdict DOWN (`ALL[ref BTC_RISK_OFF, roc12_rank_lo lte 5]`). Levels 1–2 ATR, RR 1.8,
|
|
61
61
|
ATR floor 0.6%. PM: BE 1R; trail 1R giveback 45. Ranked cohort ≥ 40 —
|
|
@@ -63,12 +63,12 @@ rank conditions need a cohort wider than their thresholds.
|
|
|
63
63
|
|
|
64
64
|
### 4 · HTF Trend Pullback — 1h, multi-timeframe confluence
|
|
65
65
|
|
|
66
|
-
Platform `includeMtfConfluence` (`MAalign_ltf/…/_htf`, `RSI14_*_zone`, `
|
|
66
|
+
Platform `includeMtfConfluence` (`MAalign_ltf/…/_htf`, `RSI14_*_zone`, `ADX14_*_state` vocab
|
|
67
67
|
weak/developing/trending/extreme), `includeMovingAverages` (`dist_EMA20`, …), `includeRsi`
|
|
68
68
|
(`RSI14_now`, `RSI14_zone`). These three render `MAalign` and `RSI14_zone` twice between them, so
|
|
69
69
|
every condition below reads a suffixed form — never bare `MAalign` or bare `RSI14_zone`, which
|
|
70
70
|
would be ambiguous. Conditions: `HTF_UP` building block (`MAalign_htf is "bullish"` +
|
|
71
|
-
`
|
|
71
|
+
`ADX14_htf_state in ["trending","extreme"]`); `HTF_DOWN` mirror; `TREND_PRESENT` **required**
|
|
72
72
|
`ANY[ref HTF_UP, ref HTF_DOWN]` — chop blocks compose-trade entirely, before billing;
|
|
73
73
|
`PULLBACK_LONG` verdict UP (ref HTF_UP + `RSI14_now between 35 55` +
|
|
74
74
|
`dist_EMA20 between -3 0.5`); `PULLBACK_SHORT` verdict DOWN (mirror, 45–65 / −0.5–3). Levels
|
|
@@ -82,9 +82,9 @@ perp_led_fragile / spot_led_accumulation / neutral), `includeStructureZones`
|
|
|
82
82
|
(`BUY_PRESSURE value`→`buyPres` 0–1, `RVOL value`, `CLOSE_CHANGE value`). Conditions:
|
|
83
83
|
`SPOT_ACCUM` (`perpSpotFlow is "spot_led_accumulation"`); `NEAR_SUPPORT`
|
|
84
84
|
(`zones_htf_support_dist between 0 2` + `zones_htf_support_age_h gte 12`); `DIP_BID` verdict
|
|
85
|
-
UP — refs plus an `N_OF(2)` quorum over `buyPres gte 0.55` / `
|
|
85
|
+
UP — refs plus an `N_OF(2)` quorum over `buyPres gte 0.55` / `RVOL20 gte 1.2` / `closeChg gt 0`
|
|
86
86
|
(quorum beats a brittle ALL); `FRAGILE_POP` verdict DOWN (`perp_led_fragile` +
|
|
87
|
-
`zones_htf_resist_dist between -2 0` + `
|
|
87
|
+
`zones_htf_resist_dist between -2 0` + `RVOL20 gte 1.3`). Levels 0.5–1.2 ATR, RR 1.5, ATR floor
|
|
88
88
|
0.4%. PM: BE 0.7R; trail 0.9R
|
|
89
89
|
giveback 30 buffer 0.15; timeDecay ON aggressive (45/15, 15→60, stale 30).
|
|
90
90
|
|
|
@@ -13,6 +13,7 @@ engine) and names its substitutions where the catalog lacks a primitive.
|
|
|
13
13
|
- Donchian / Turtle breakout
|
|
14
14
|
- ICT / SMC (FVG + order blocks)
|
|
15
15
|
- Pivot highs / lows, HH-HL structure and break of structure
|
|
16
|
+
- Chaikin Money Flow
|
|
16
17
|
- Now native, formerly substituted
|
|
17
18
|
|
|
18
19
|
## TradingView ports — familiar processes, studio vocabulary
|
|
@@ -34,7 +35,7 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
34
35
|
`BB_LOWER spread KC_LOWER` still express the same reading at a threshold you choose. Release
|
|
35
36
|
direction from the MACD trajectory, `bollinger_squeeze` Critical-required.
|
|
36
37
|
Its selectivity is anchor-dependent — see `## Conditions` in the skill body before gating on it.
|
|
37
|
-
(`bbWidthPct_rank_lo` + `
|
|
38
|
+
(`bbWidthPct_rank_lo` + `ADX14 lt 20` remains a serviceable board-relative compression proxy, but it
|
|
38
39
|
is no longer the only option.)
|
|
39
40
|
- **Supertrend / UT Bot / Chandelier Exit** → `ST_LINE` and `ST_DIR` are native, so the regime
|
|
40
41
|
half is a direct port: `ST_DIR is "bullish"` as the persistent state, `dist_ST_LINE` for
|
|
@@ -58,16 +59,16 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
58
59
|
reading while the literal is a threshold the author can see and tune. Use the zone as a report
|
|
59
60
|
column, never as the substitute for the gate.
|
|
60
61
|
- **VWAP reversion** → `dist_VWAP` band + `dist_VWAP_rank_far lte 5` for board-relative
|
|
61
|
-
stretch; required `NOT [
|
|
62
|
+
stretch; required `NOT [ADX14_state in ["trending","extreme"]]` veto; scalp geometry +
|
|
62
63
|
aggressive timeDecay (VWAP anchors daily at 00:00 UTC).
|
|
63
|
-
- **Donchian / Turtle breakout** (`close > ta.highest(high, 20)[1]`) → `
|
|
64
|
-
over `DONCHIAN_UPPER_PRIOR × distance` (the breakout bar; `
|
|
65
|
-
close of the run) + `
|
|
66
|
-
`
|
|
64
|
+
- **Donchian / Turtle breakout** (`close > ta.highest(high, 20)[1]`) → `dist_donchianHiPrior20 crossesAbove 0`
|
|
65
|
+
over `DONCHIAN_UPPER_PRIOR × distance` (the breakout bar; `dist_donchianHiPrior20 gt 0` holds on every
|
|
66
|
+
close of the run) + `RVOL20 gte 1.5`; turtle exits = trend preset (trail from 1R, giveback 50). Mirror with
|
|
67
|
+
`dist_donchianLoPrior20 crossesBelow 0`. The same-bar rail (`dist_donchianHi20`) includes the breakout bar's
|
|
67
68
|
own high, so it can never confirm a close-based break — do not AND it in. `zone is "breakout high"` reads
|
|
68
|
-
the prior-bar rail: it is the label form of the `
|
|
69
|
+
the prior-bar rail: it is the label form of the `dist_donchianHiPrior20 gt 0` state, holds on every close of
|
|
69
70
|
the run, and adds nothing to the crossing; `"breakdown low"` mirrors it.
|
|
70
|
-
**Daily-breakout variant on any anchor:** pin the rail at 1d — `
|
|
71
|
+
**Daily-breakout variant on any anchor:** pin the rail at 1d — `dist_donchianHiPrior20_1d crossesAbove 0`
|
|
71
72
|
(`gt 0` for the held state).
|
|
72
73
|
**Literal previous-day levels are native**: `dist_PDH gte 0` composes directly. They are TIMELESS
|
|
73
74
|
and take `{rel: 'anchor'}` — see `## Header grammar` in the skill body.
|
|
@@ -85,8 +86,20 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
85
86
|
not), and each level is the stepped, non-repainting series — held from the bar that completes the
|
|
86
87
|
pivot's right side, not plotted back on the pivot bar. Break of structure is
|
|
87
88
|
`dist_fractalHi crossesAbove 0` / `dist_fractalLo crossesBelow 0`; "structure is bullish" is
|
|
88
|
-
`fractalLabel in ["HH","HL"]`.
|
|
89
|
-
|
|
89
|
+
`fractalLabel in ["HH","HL"]`. The pivot each latest one is compared with is native too:
|
|
90
|
+
`PRIOR_SWING_FRACTAL_HIGH` / `PRIOR_SWING_FRACTAL_LOW` (`pFractalHi` / `pFractalLo`, Pine's
|
|
91
|
+
`ta.valuewhen(not na(ph), ph, 1)`) are price levels held while the prior pivot is in reach, so
|
|
92
|
+
`dist_pFractalHi crossesAbove 0` is a close through the swing high before the last one. A pivot's age is
|
|
93
|
+
`SWING_FRACTAL_HIGH_BARS_AGO` / `SWING_FRACTAL_LOW_BARS_AGO` (`fractalHiAgo` / `fractalLoAgo`), counted
|
|
94
|
+
from the pivot's own bar: 2 on the bar that completes it, so it reads Pine's
|
|
95
|
+
`ta.barssince(not na(ph)) + 2`, and `fractalLoAgo lte 5` keeps a setup to a fresh swing low. Only two
|
|
96
|
+
bars each side is native: a `ta.pivothigh(5, 5)` or longer script ports onto the two-bar swing, and the
|
|
97
|
+
spec-lock question says so.
|
|
98
|
+
- **Chaikin Money Flow** (TradingView's built-in "Chaikin Money Flow" script, length 20; Pine has no
|
|
99
|
+
`ta.cmf`) → `CMF20` is native: each bar's volume weighted by where it closed in its range, summed over
|
|
100
|
+
20 closed bars and divided by their volume, −1 to +1. `CMF20 gt 0` is the accumulation state and
|
|
101
|
+
`CMF20_cross is "Bullish"` the zero-line cross; a flat bar weighs 0, and a window that traded no volume
|
|
102
|
+
reads no value, so a clause over it reads FALSE. It reads closed bars only, never the forming one.
|
|
90
103
|
- **Now native, formerly substituted** — WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`),
|
|
91
104
|
Hull (`HMA20`), Ichimoku (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`),
|
|
92
105
|
Parabolic SAR (`PSAR`), Keltner (`KC_UPPER`/`KC_LOWER`, its basis is `EMA20`), the MACD and PPO lines
|
|
@@ -95,7 +108,10 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
95
108
|
line minus signal), the 15-bar rate of change (`ROC15`), the Donchian break memory
|
|
96
109
|
(`DONCHIAN_BROKEN_HI`/`DONCHIAN_BROKEN_LO`), daily pivots
|
|
97
110
|
(`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`), Williams %R (`WILLR14`), Stochastic RSI
|
|
98
|
-
(`STOCH_RSI14`), the TTM squeeze (`KC_SQUEEZE`), Connors RSI-2 (`RSI2`), the 9/21/50 EMAs
|
|
99
|
-
(`EMA9`/`EMA21`/`EMA50`
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
(`STOCH_RSI14`), the TTM squeeze (`KC_SQUEEZE`), Connors RSI-2 (`RSI2`), the 8/9/21/50 EMAs
|
|
112
|
+
(`EMA8`/`EMA9`/`EMA21`/`EMA50` — `ta.ema(close, 8)` is `EMA8`, its distance `dist_EMA8`), literal
|
|
113
|
+
previous-session levels (`PDH`/`PDL`/`PDO`), fractal swing structure
|
|
114
|
+
(`SWING_FRACTAL_HIGH`/`SWING_FRACTAL_LOW`/`SWING_FRACTAL_LABEL`, the prior pivots
|
|
115
|
+
`PRIOR_SWING_FRACTAL_HIGH`/`PRIOR_SWING_FRACTAL_LOW` and their ages
|
|
116
|
+
`SWING_FRACTAL_HIGH_BARS_AGO`/`SWING_FRACTAL_LOW_BARS_AGO`), and Chaikin Money Flow (`CMF20`). Port
|
|
117
|
+
these directly — do not offer a substitute for a primitive the catalog serves.
|