@battlegrid/mcp-server 31.2.87 → 31.2.88
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +13 -9
- package/skills/battlegrid-agent-management/SKILL.md +9 -5
- package/skills/battlegrid-agent-management/references/agent-stage-examples.md +64 -0
- package/skills/battlegrid-arena-play/SKILL.md +7 -4
- package/skills/battlegrid-radar-deployment/SKILL.md +29 -0
- package/skills/battlegrid-radar-deployment/references/arena-stage-examples.md +140 -0
- package/skills/battlegrid-radar-deployment/references/radar-stage-examples.md +155 -0
- package/skills/battlegrid-strategy-authoring/SKILL.md +21 -3
- package/skills/battlegrid-strategy-authoring/references/strategy-stage-examples.md +50 -0
- package/skills/battlegrid-strategy-examples/SKILL.md +199 -10
- package/skills/battlegrid-strategy-examples/references/playbooks.md +3 -3
- package/skills/battlegrid-strategy-examples/references/tradingview-ports.md +19 -6
- package/skills/battlegrid-trade-analysis/SKILL.md +4 -4
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.88";
|
|
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.88';
|
|
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,18 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "105.0.0",
|
|
4
4
|
"files": {
|
|
5
|
-
"battlegrid-agent-management/SKILL.md": "
|
|
6
|
-
"battlegrid-
|
|
5
|
+
"battlegrid-agent-management/SKILL.md": "8ebb7d4fcc5d6a9ce463a762bf58d04a7f641369f7feaf64d2489727531e6f00",
|
|
6
|
+
"battlegrid-agent-management/references/agent-stage-examples.md": "d47555906ce8dedf4c17422f8f5d60187f2d6b3c0af5d9545617a10a5bdb59a1",
|
|
7
|
+
"battlegrid-arena-play/SKILL.md": "25f078eb31db5cd3d78ce0bc251802c32f94234fd8a8c6467c5b68abc41706f2",
|
|
7
8
|
"battlegrid-market-analysis/SKILL.md": "875bda71dd766ad06ac8e508679bcd3400a600cf74d2eac6c922dbe23175f325",
|
|
8
|
-
"battlegrid-radar-deployment/SKILL.md": "
|
|
9
|
-
"battlegrid-
|
|
9
|
+
"battlegrid-radar-deployment/SKILL.md": "093ac358cd4f1a67a7dd467b0b4161c9a055cdd516d49ebb23bd4e9ef44aa200",
|
|
10
|
+
"battlegrid-radar-deployment/references/arena-stage-examples.md": "f924b70801a4b72bbde5a80823408939903cba6b294d8554fc4afc11d80b3333",
|
|
11
|
+
"battlegrid-radar-deployment/references/radar-stage-examples.md": "1ddad4a11f021dd65234ee65cc12d7d1561fe9acfd61cb021f4352080d4cecd5",
|
|
12
|
+
"battlegrid-strategy-authoring/SKILL.md": "fcc4ab6ae7e75e0949cef057134288e5e6e67ff96d3238bb551775ba5ce22f95",
|
|
13
|
+
"battlegrid-strategy-authoring/references/strategy-stage-examples.md": "1c35353c7280934486b65d5defe80f87278e77955b6041db677238940e589b50",
|
|
10
14
|
"battlegrid-strategy-doctor/SKILL.md": "14f20bff9e2b5a59d11634fe3615b1298d672d5e2eba06d3dc8698b571b3bee3",
|
|
11
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
12
|
-
"battlegrid-strategy-examples/references/playbooks.md": "
|
|
15
|
+
"battlegrid-strategy-examples/SKILL.md": "528049af46a0a829628681d16076129eb57e898445c803e4ebafae98699a3b65",
|
|
16
|
+
"battlegrid-strategy-examples/references/playbooks.md": "9e4038df885bb840d6ee58ba0475a9214dc58d12bf355c91c8aae625d58e3096",
|
|
13
17
|
"battlegrid-strategy-examples/references/recipes.md": "61382acdd295fa159f055b787ba501f1c015f383f59a6714a973057f10de611f",
|
|
14
|
-
"battlegrid-strategy-examples/references/tradingview-ports.md": "
|
|
15
|
-
"battlegrid-trade-analysis/SKILL.md": "
|
|
18
|
+
"battlegrid-strategy-examples/references/tradingview-ports.md": "706a7dd3a120b2f3d5fa3190924f493cf4ebaf5df895bbeb155c2db73f8f5ece",
|
|
19
|
+
"battlegrid-trade-analysis/SKILL.md": "f545b916dc2a37c8fc35e16b27fa6f78ead4302e67f65ac584765c1e069b4ab2",
|
|
16
20
|
"battlegrid-trade-proposal/SKILL.md": "d207a30065c17e13efaf5391805e5603e6956633cba5d0a1ff60e276518d9696"
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -125,7 +125,8 @@ mid-edit in their form**, and every change you make joins it. Then:
|
|
|
125
125
|
read you proposed against returned (0 when it found no draft): it lands in their open form,
|
|
126
126
|
labelled as yours. Each axis you send is written WHOLE — BEHAVIOR carries all three of risk,
|
|
127
127
|
outlook and conviction, TRADING_CONFIG the complete configuration (step 6) — and the axes you
|
|
128
|
-
omit keep their values.
|
|
128
|
+
omit keep their values. Complete stage payloads — a create, a behavior change, a rebind — are in
|
|
129
|
+
`references/agent-stage-examples.md`.
|
|
129
130
|
- **Read the stage's diagnostics.** Its `errors` are the refusals the commit would raise, each
|
|
130
131
|
naming its field: fix the axis and stage again before you ask for anything.
|
|
131
132
|
- A staging call refused **`DRAFT_AXIS_CONTESTED`** names an axis the player changed after the
|
|
@@ -193,10 +194,13 @@ is voting on a state you have not seen. A commit refused with `nextAct: stage` n
|
|
|
193
194
|
diagnostics named: fix it in a stage, read, and confirm again.
|
|
194
195
|
|
|
195
196
|
**A refused archive names its own blockers.** The refusal carries typed `archiveBlockers[]` —
|
|
196
|
-
DEPLOYED / OPEN_TRADES / ACTIVE_SESSION, each with a count. Report each blocker and
|
|
197
|
-
the reason, and name what would clear it (un-deploy via the
|
|
198
|
-
the positions resolve, wait for the session to settle).
|
|
199
|
-
|
|
197
|
+
DEPLOYED / OPEN_TRADES / ACTIVE_SESSION / RADAR_DEPLOYED, each with a count. Report each blocker and
|
|
198
|
+
its count as the reason, and name what would clear it (un-deploy via the deployment tools, close or
|
|
199
|
+
let the positions resolve, wait for the session to settle). RADAR_DEPLOYED counts paused radars too:
|
|
200
|
+
a paused radar still holds the slot, so pausing does not clear it. Clearing it is a radar-deployment
|
|
201
|
+
write, not this arc's: activate `radar-deployment` and let its arc find the coins whose radar holds
|
|
202
|
+
the agent and un-deploy it there — a radar draft committed without the agent, or the coin's
|
|
203
|
+
deployment deleted. Never paraphrase the refusal into "it didn't work", and never retry it unchanged.
|
|
200
204
|
|
|
201
205
|
### 5. Halt recovery: offer exactly the served verdict's exits
|
|
202
206
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Agent stage examples
|
|
2
|
+
|
|
3
|
+
Complete `stage_agent_draft` inputs. Each is checked in CI against the live stage schema; a create example is also
|
|
4
|
+
checked for every axis a new agent needs. Its shape is current.
|
|
5
|
+
|
|
6
|
+
Every `<UPPER_SNAKE>` value is a placeholder. Replace it with the id a read returned:
|
|
7
|
+
|
|
8
|
+
- `<AGENT_ID>` comes from the draft or the agent you are editing.
|
|
9
|
+
- `<MODEL_ID>` is the row's `modelId` from `list_approved_models`.
|
|
10
|
+
- `<STRATEGY_ID>` comes from the player's strategies.
|
|
11
|
+
|
|
12
|
+
Never send a placeholder as written, and never invent an id. `draftVersion` is the version `get_agent_draft`
|
|
13
|
+
returned: 0 when there was no draft, and for a new create draft. Each axis you send is written whole, and an axis
|
|
14
|
+
you omit keeps the value the draft already has.
|
|
15
|
+
|
|
16
|
+
## Create a new agent
|
|
17
|
+
|
|
18
|
+
Omit `agentId` to open a create draft. The response carries the minted id, which every later call names. A new agent
|
|
19
|
+
needs all four of IDENTITY, BEHAVIOR, MODEL and STRATEGY_BINDING. TRADING_CONFIG is optional: omit it to take the
|
|
20
|
+
platform seed.
|
|
21
|
+
|
|
22
|
+
```json stage_agent_draft
|
|
23
|
+
{
|
|
24
|
+
"request": {
|
|
25
|
+
"draftVersion": 0,
|
|
26
|
+
"axes": {
|
|
27
|
+
"IDENTITY": { "displayName": "Breakout Scout" },
|
|
28
|
+
"BEHAVIOR": { "behavior": { "risk": "MODERATE", "outlook": "REALIST", "conviction": "MEASURED" } },
|
|
29
|
+
"MODEL": { "modelId": "<MODEL_ID>" },
|
|
30
|
+
"STRATEGY_BINDING": { "strategyId": "<STRATEGY_ID>" }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Change an existing agent's behavior
|
|
37
|
+
|
|
38
|
+
BEHAVIOR is sent whole: all three of risk, outlook and conviction, even when only one changes.
|
|
39
|
+
|
|
40
|
+
```json stage_agent_draft
|
|
41
|
+
{
|
|
42
|
+
"request": {
|
|
43
|
+
"agentId": "<AGENT_ID>",
|
|
44
|
+
"draftVersion": 4,
|
|
45
|
+
"axes": {
|
|
46
|
+
"BEHAVIOR": { "behavior": { "risk": "CONSERVATIVE", "outlook": "REALIST", "conviction": "CAUTIOUS" } }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Rebind an existing agent to another strategy
|
|
53
|
+
|
|
54
|
+
```json stage_agent_draft
|
|
55
|
+
{
|
|
56
|
+
"request": {
|
|
57
|
+
"agentId": "<AGENT_ID>",
|
|
58
|
+
"draftVersion": 4,
|
|
59
|
+
"axes": {
|
|
60
|
+
"STRATEGY_BINDING": { "strategyId": "<STRATEGY_ID>" }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: battlegrid-arena-play
|
|
3
|
-
description: Enter the player into Market Grid sessions — find an open session, read its coin pool and live market context, compose a grid with real per-coin reasoning or have one of their agents generate it, submit it against the session's entry fee, then read the results and the
|
|
3
|
+
description: Enter the player into Market Grid sessions — find an open session, read its coin pool and live market context, compose a grid with real per-coin reasoning or have one of their agents generate it, submit it against the session's entry fee, then read the results and the player's own entry record after settlement. Activate whenever the player wants to play, enter, join or submit to a session, wants their agent to enter one, asks what is open to play, or asks how a session they entered turned out.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Arena Play
|
|
@@ -108,9 +108,12 @@ reasoning as the player's, and the attribution is part of what was scored.
|
|
|
108
108
|
- `get_market_grid_results` — available only once the session is SETTLED. Before that it returns a
|
|
109
109
|
typed CONFLICT naming the current status: **report it and stop**. Do not poll it in a loop; the
|
|
110
110
|
op budget is finite and a settling session is not a failure.
|
|
111
|
-
- `
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
- `get_my_market_grid_entry` on request — the player's own entry in that session: its picks, who
|
|
112
|
+
authored it, whether another author has rewritten it (`edited`), and the reasoning and confidence
|
|
113
|
+
recorded at submit. Compare the outcome against what was actually reasoned, and say where the
|
|
114
|
+
reasoning was wrong. `reasoning` is null once the entry was edited — the record no longer describes
|
|
115
|
+
the picks, so say that rather than reconstructing one. `list_my_market_grid_entries` is the history
|
|
116
|
+
across sessions. An agent's decisions are a different record, `get_agent_journal`.
|
|
114
117
|
|
|
115
118
|
## Refusals, and what each one means
|
|
116
119
|
|
|
@@ -113,6 +113,20 @@ is test-routing a regime. Never carry a fact from a radar conversation into it,
|
|
|
113
113
|
To try a slot set without touching the draft, preview `{ kind: "SLOTS", slots }` — it resolves
|
|
114
114
|
explicit slots and nothing can commit it.
|
|
115
115
|
|
|
116
|
+
**Shaping a Radar rule.** A rule carries one or two conditions — never none. Pick the construction
|
|
117
|
+
from what the player asked for:
|
|
118
|
+
|
|
119
|
+
| The player wants the agent on duty… | Construction |
|
|
120
|
+
|---|---|
|
|
121
|
+
| whenever no rule matches | `DEFAULT_SLOT` — on duty only while no rule matches, so on a coin with no rules it is on duty at every instant |
|
|
122
|
+
| only in named regimes | a rule with a regime condition |
|
|
123
|
+
| only in named hours and days | a rule with an hours condition |
|
|
124
|
+
| in named regimes during named hours | a rule with one hours condition and one regime condition |
|
|
125
|
+
|
|
126
|
+
When the ask maps to no single row, or a refusal rules out the construction the player asked for,
|
|
127
|
+
ask the player which construction they mean, naming the rows that fit — never stage one they did not
|
|
128
|
+
choose. Complete stage payloads for every row are in `references/radar-stage-examples.md`.
|
|
129
|
+
|
|
116
130
|
**Arena** — the same shape, keyed on `presetId`:
|
|
117
131
|
|
|
118
132
|
1. `stage_deployment_policy_draft` with `draftVersion` = the `version` you read (0 when there was no
|
|
@@ -127,6 +141,21 @@ explicit slots and nothing can commit it.
|
|
|
127
141
|
A draft that would hold no rule and no catch-all is a withdrawal, which a commit never does — that is
|
|
128
142
|
`delete_deployment_policy` (step 4).
|
|
129
143
|
|
|
144
|
+
**Shaping an Arena rule.** A rule needs at least one condition by the time it is committed — a draft
|
|
145
|
+
may hold a rule with none while the player is still choosing, and the draft read's diagnostics flag
|
|
146
|
+
it. Pick the construction from what the player asked for:
|
|
147
|
+
|
|
148
|
+
| The player wants the agent to play… | Construction |
|
|
149
|
+
|---|---|
|
|
150
|
+
| whenever no rule matches | `DEFAULT_SLOT` — plays only when no rule wins the session |
|
|
151
|
+
| only in named regimes | a rule with a regime condition |
|
|
152
|
+
| only at named session starts, or one scheduled occurrence | a rule with one session condition — a start or an occurrence |
|
|
153
|
+
| in named regimes at those sessions | a rule with a regime condition and one session condition |
|
|
154
|
+
|
|
155
|
+
When the ask maps to no single row, or a refusal rules out the construction the player asked for,
|
|
156
|
+
ask the player which construction they mean, naming the rows that fit — never stage one they did not
|
|
157
|
+
choose. Complete stage payloads for every row are in `references/arena-stage-examples.md`.
|
|
158
|
+
|
|
130
159
|
Both previews run the **same resolver the live sweep runs**, so the preview is the real outcome, not
|
|
131
160
|
an estimate. Neither writes anything and neither costs the player an LLM call — so previewing
|
|
132
161
|
repeatedly while iterating is free and correct.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Arena stage examples
|
|
2
|
+
|
|
3
|
+
Complete `stage_deployment_policy_draft` inputs, one per construction in **Shaping an Arena rule**. Each is checked in
|
|
4
|
+
CI against the live stage schema and composed into a deployment the aggregate accepts, so its shape is current.
|
|
5
|
+
|
|
6
|
+
Every `<UPPER_SNAKE>` value is a placeholder. Replace it with the id a read returned:
|
|
7
|
+
|
|
8
|
+
- `<PRESET_ID>` comes from `list_game_presets`.
|
|
9
|
+
- `<AGENT_ID>` comes from your agent list.
|
|
10
|
+
- `<COIN_ID>` comes from the arena's coin pool.
|
|
11
|
+
|
|
12
|
+
Never send a placeholder as written, and never invent an id. `draftVersion` is the version
|
|
13
|
+
`get_deployment_policy_draft` returned: 0 when the arena has no draft. Each axis you send is written whole, and an
|
|
14
|
+
axis you omit keeps the value the draft already has.
|
|
15
|
+
|
|
16
|
+
## Play whenever no rule matches — the default slot
|
|
17
|
+
|
|
18
|
+
```json stage_deployment_policy_draft
|
|
19
|
+
{
|
|
20
|
+
"request": {
|
|
21
|
+
"presetId": "<PRESET_ID>",
|
|
22
|
+
"draftVersion": 0,
|
|
23
|
+
"axes": {
|
|
24
|
+
"DEFAULT_SLOT": {
|
|
25
|
+
"defaultSlot": { "agentId": "<AGENT_ID>", "minConfidence": null, "entryStrategy": "STANDARD" }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Play only in named regimes
|
|
33
|
+
|
|
34
|
+
```json stage_deployment_policy_draft
|
|
35
|
+
{
|
|
36
|
+
"request": {
|
|
37
|
+
"presetId": "<PRESET_ID>",
|
|
38
|
+
"draftVersion": 0,
|
|
39
|
+
"axes": {
|
|
40
|
+
"RULES": {
|
|
41
|
+
"rules": [
|
|
42
|
+
{
|
|
43
|
+
"agentId": "<AGENT_ID>",
|
|
44
|
+
"minConfidence": null,
|
|
45
|
+
"entryStrategy": "STANDARD",
|
|
46
|
+
"conditions": [{ "kind": "regime", "regimes": ["bull_expansion", "bull_ranging"] }]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Play only at named session starts
|
|
56
|
+
|
|
57
|
+
Start times are UTC `HH:MM:SS`. Days run 0 (Sunday) to 6. Each is listed once. A rule holds a session start or a
|
|
58
|
+
session occurrence, never both.
|
|
59
|
+
|
|
60
|
+
```json stage_deployment_policy_draft
|
|
61
|
+
{
|
|
62
|
+
"request": {
|
|
63
|
+
"presetId": "<PRESET_ID>",
|
|
64
|
+
"draftVersion": 0,
|
|
65
|
+
"axes": {
|
|
66
|
+
"RULES": {
|
|
67
|
+
"rules": [
|
|
68
|
+
{
|
|
69
|
+
"agentId": "<AGENT_ID>",
|
|
70
|
+
"minConfidence": 0.6,
|
|
71
|
+
"entryStrategy": "TWO_LOOK",
|
|
72
|
+
"conditions": [
|
|
73
|
+
{ "kind": "session_start", "startTimes": ["14:00:00", "15:00:00"], "days": [1, 2, 3, 4, 5] }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## In named regimes at those sessions — a regime condition and one session condition
|
|
84
|
+
|
|
85
|
+
```json stage_deployment_policy_draft
|
|
86
|
+
{
|
|
87
|
+
"request": {
|
|
88
|
+
"presetId": "<PRESET_ID>",
|
|
89
|
+
"draftVersion": 0,
|
|
90
|
+
"axes": {
|
|
91
|
+
"RULES": {
|
|
92
|
+
"rules": [
|
|
93
|
+
{
|
|
94
|
+
"agentId": "<AGENT_ID>",
|
|
95
|
+
"minConfidence": null,
|
|
96
|
+
"entryStrategy": "STANDARD",
|
|
97
|
+
"conditions": [
|
|
98
|
+
{ "kind": "regime", "regimes": ["volatile"] },
|
|
99
|
+
{ "kind": "session_start", "startTimes": ["14:00:00"], "days": [1, 2, 3, 4, 5] }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Override the regime anchor on a deployed arena
|
|
110
|
+
|
|
111
|
+
This stages only `REGIME_ANCHOR`, so the deployed rules and default slot stay as they are. The coin and the timeframe
|
|
112
|
+
come together, or not at all.
|
|
113
|
+
|
|
114
|
+
```json stage_deployment_policy_draft
|
|
115
|
+
{
|
|
116
|
+
"request": {
|
|
117
|
+
"presetId": "<PRESET_ID>",
|
|
118
|
+
"draftVersion": 2,
|
|
119
|
+
"axes": {
|
|
120
|
+
"REGIME_ANCHOR": { "regimeAnchor": { "regimeReferenceCoinId": "<COIN_ID>", "regimeTimeframe": "4h" } }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Clear the regime anchor override
|
|
127
|
+
|
|
128
|
+
The arena's own anchor applies again.
|
|
129
|
+
|
|
130
|
+
```json stage_deployment_policy_draft
|
|
131
|
+
{
|
|
132
|
+
"request": {
|
|
133
|
+
"presetId": "<PRESET_ID>",
|
|
134
|
+
"draftVersion": 2,
|
|
135
|
+
"axes": {
|
|
136
|
+
"REGIME_ANCHOR": { "regimeAnchor": null }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Radar stage examples
|
|
2
|
+
|
|
3
|
+
Complete `stage_radar_deployment_draft` inputs, one per construction in **Shaping a Radar rule**. Each is
|
|
4
|
+
checked in CI against the live stage schema and composed into a policy the aggregate accepts, so its
|
|
5
|
+
shape is current.
|
|
6
|
+
|
|
7
|
+
Every `<UPPER_SNAKE>` value is a placeholder. Replace it with the id a read returned:
|
|
8
|
+
|
|
9
|
+
- `<COIN_ID>` comes from `get_coin_metadata`.
|
|
10
|
+
- `<AGENT_ID>` comes from your agent list.
|
|
11
|
+
- `<OTHER_AGENT_ID>` is a second agent from your agent list, where an example deploys two.
|
|
12
|
+
|
|
13
|
+
Never send a placeholder as written, and never invent an id. `draftVersion` is the version
|
|
14
|
+
`get_radar_deployment_draft` returned: 0 when the coin has no draft. Each axis you send is written whole, and an axis
|
|
15
|
+
you omit keeps the value the draft already has.
|
|
16
|
+
|
|
17
|
+
## On duty whenever no rule matches — the default slot
|
|
18
|
+
|
|
19
|
+
On a coin with no rules, the default slot is on duty at every instant. This is the construction for "put this agent
|
|
20
|
+
on this coin" with no condition named.
|
|
21
|
+
|
|
22
|
+
```json stage_radar_deployment_draft
|
|
23
|
+
{
|
|
24
|
+
"request": {
|
|
25
|
+
"coinId": "<COIN_ID>",
|
|
26
|
+
"draftVersion": 0,
|
|
27
|
+
"axes": {
|
|
28
|
+
"DEFAULT_SLOT": { "defaultSlot": { "agentId": "<AGENT_ID>", "minConviction": null } }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## On duty only in named regimes
|
|
35
|
+
|
|
36
|
+
`minConviction` on the rule is the trade bar (0–1, or null to inherit the agent's). `minConviction` inside the
|
|
37
|
+
regime condition is the regime-reading floor, and must be a level one of the selected regimes can emit.
|
|
38
|
+
|
|
39
|
+
```json stage_radar_deployment_draft
|
|
40
|
+
{
|
|
41
|
+
"request": {
|
|
42
|
+
"coinId": "<COIN_ID>",
|
|
43
|
+
"draftVersion": 0,
|
|
44
|
+
"axes": {
|
|
45
|
+
"RULES": {
|
|
46
|
+
"rules": [
|
|
47
|
+
{
|
|
48
|
+
"agentId": "<AGENT_ID>",
|
|
49
|
+
"minConviction": null,
|
|
50
|
+
"conditions": [
|
|
51
|
+
{ "kind": "regime", "regimes": ["bull_expansion", "bull_ranging"], "minConviction": "high" }
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## On duty only in named hours and days
|
|
62
|
+
|
|
63
|
+
Hours are UTC, 0–23. Days run 0 (Sunday) to 6. Each is listed once.
|
|
64
|
+
|
|
65
|
+
```json stage_radar_deployment_draft
|
|
66
|
+
{
|
|
67
|
+
"request": {
|
|
68
|
+
"coinId": "<COIN_ID>",
|
|
69
|
+
"draftVersion": 0,
|
|
70
|
+
"axes": {
|
|
71
|
+
"RULES": {
|
|
72
|
+
"rules": [
|
|
73
|
+
{
|
|
74
|
+
"agentId": "<AGENT_ID>",
|
|
75
|
+
"minConviction": null,
|
|
76
|
+
"conditions": [
|
|
77
|
+
{ "kind": "hours", "hours": [13, 14, 15, 16, 17, 18, 19, 20], "days": [1, 2, 3, 4, 5] }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## In named regimes during named hours — one condition of each kind
|
|
88
|
+
|
|
89
|
+
```json stage_radar_deployment_draft
|
|
90
|
+
{
|
|
91
|
+
"request": {
|
|
92
|
+
"coinId": "<COIN_ID>",
|
|
93
|
+
"draftVersion": 0,
|
|
94
|
+
"axes": {
|
|
95
|
+
"RULES": {
|
|
96
|
+
"rules": [
|
|
97
|
+
{
|
|
98
|
+
"agentId": "<AGENT_ID>",
|
|
99
|
+
"minConviction": 0.6,
|
|
100
|
+
"conditions": [
|
|
101
|
+
{ "kind": "hours", "hours": [13, 14, 15, 16], "days": [1, 2, 3, 4, 5] },
|
|
102
|
+
{ "kind": "regime", "regimes": ["volatile"] }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Rules plus a default slot
|
|
113
|
+
|
|
114
|
+
Every rule that matches is on duty, and the first rule listed is offered the coin's fire first. The default slot is
|
|
115
|
+
on duty only while no rule matches, so here the second agent is on duty whenever the coin is in neither of the
|
|
116
|
+
first agent's regimes.
|
|
117
|
+
|
|
118
|
+
```json stage_radar_deployment_draft
|
|
119
|
+
{
|
|
120
|
+
"request": {
|
|
121
|
+
"coinId": "<COIN_ID>",
|
|
122
|
+
"draftVersion": 0,
|
|
123
|
+
"axes": {
|
|
124
|
+
"RULES": {
|
|
125
|
+
"rules": [
|
|
126
|
+
{
|
|
127
|
+
"agentId": "<AGENT_ID>",
|
|
128
|
+
"minConviction": null,
|
|
129
|
+
"conditions": [{ "kind": "regime", "regimes": ["bear_expansion", "bear_ranging"] }]
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"DEFAULT_SLOT": { "defaultSlot": { "agentId": "<OTHER_AGENT_ID>", "minConviction": null } }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Clear the default slot on a deployed coin
|
|
140
|
+
|
|
141
|
+
This stages only `DEFAULT_SLOT`, so the deployed rules stay as they are. Send `RULES` only when the rules change, and
|
|
142
|
+
then send the complete list: a rule you do not resend is deleted. `draftVersion` here is the version your read
|
|
143
|
+
returned.
|
|
144
|
+
|
|
145
|
+
```json stage_radar_deployment_draft
|
|
146
|
+
{
|
|
147
|
+
"request": {
|
|
148
|
+
"coinId": "<COIN_ID>",
|
|
149
|
+
"draftVersion": 3,
|
|
150
|
+
"axes": {
|
|
151
|
+
"DEFAULT_SLOT": { "defaultSlot": null }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
@@ -129,12 +129,29 @@ question you would otherwise guess — and a refusal you would otherwise earn:
|
|
|
129
129
|
it — its own `hold: { atLeast, of }`, which the editor calls **Hold** (Once, In a row, Within, At
|
|
130
130
|
least), counted in completed bars of the column's own timeframe — not to this axis. Every clause
|
|
131
131
|
and group carries `hold`; `{ "atLeast": 1, "of": 1 }` is Once. The `strategy-examples` skill
|
|
132
|
-
carries the vocabulary, the served hold domain (a crossover
|
|
133
|
-
|
|
132
|
+
carries the vocabulary, the served hold domain (a crossover or an edge operator — `crossesAbove`,
|
|
133
|
+
`crossesBelow`, `enters`, `exits` — is held "within" only; a Developing column cannot be held) and
|
|
134
|
+
the per-column Confirmed / Developing read.
|
|
135
|
+
- **What a bar did is its own vocabulary.** A touch or a rejection of a level, a candle pattern, a
|
|
136
|
+
gap and a run of bars are composed from the `strategy-examples` skill's **Bar geometry** section —
|
|
137
|
+
`levelInteraction` over a level with its `buffer` knob, and the pattern, gap, run and price-source
|
|
138
|
+
metrics. A level BREAK is a `distance` crossing over the same level, never a `levelInteraction`
|
|
139
|
+
label.
|
|
140
|
+
- **Swing structure is its own section.** Swing highs and lows, the `HH`/`HL`/`LH`/`LL` label and the
|
|
141
|
+
break-of-structure recipes are in the `strategy-examples` skill's **Swing structure** section.
|
|
134
142
|
- `get_strategy_column_contract` → `outputs[].conditionOperators`. An empty array means that
|
|
135
143
|
rendered header has no comparison semantics and cannot appear in a condition clause at all.
|
|
136
144
|
Legality is per rendered header, not per column: a trajectory's slot header and its `_trend`
|
|
137
145
|
header answer differently.
|
|
146
|
+
- `get_strategy_column_contract` → `lookback`, `measurement` and `seriesHome`. `lookback.used` is
|
|
147
|
+
the bars the column reads against `lookback.cap`, the column-lookback budget a save holds it to —
|
|
148
|
+
a column past the cap is refused with `REPORT_COLUMN_LOOKBACK_EXCEEDED` by this tool exactly as by
|
|
149
|
+
the save, so shrink its window or offset rather than staging it. `measurement` names how the
|
|
150
|
+
transform (and a chained transform) reads its series: the read shape, the window's unit, the price
|
|
151
|
+
basis and what an absent bar does. `seriesHome` says where the metric's series is kept — `candle`,
|
|
152
|
+
`history`, or `null` when the metric has no series, which is why a series transform does not
|
|
153
|
+
compose on it. Window and offset carry a declared `minimum` and no fixed maximum; the lookback
|
|
154
|
+
cap is the bound.
|
|
138
155
|
- `get_metric_construction_hints` → `rankOrderings`. Present only when rank is composable on that
|
|
139
156
|
metric, already range-gated server-side — read the offered set rather than deriving one from
|
|
140
157
|
the metric's native output.
|
|
@@ -160,7 +177,8 @@ asked to approve.
|
|
|
160
177
|
the `axes` you propose. It writes those axes into the player's draft — attributed to you, landing in
|
|
161
178
|
their builder as unsaved changes — and commits nothing. Each stage returns the draft's new
|
|
162
179
|
`version`: that is the `draftVersion` your next stage names. Stage each change once, and never fire
|
|
163
|
-
two stages in parallel against one draft.
|
|
180
|
+
two stages in parallel against one draft. Complete stage payloads showing the envelope are in
|
|
181
|
+
`references/strategy-stage-examples.md`.
|
|
164
182
|
|
|
165
183
|
**A new strategy is a create draft.** Omit `strategyId` and send `draftVersion: 0` on the first
|
|
166
184
|
stage; the response carries the id minted for it, which every later call names. A create needs
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Strategy stage examples
|
|
2
|
+
|
|
3
|
+
`strategyId` and `draftVersion` sit beside `axes`, inside `request` — never inside `axes`. Every example below is
|
|
4
|
+
checked in CI against the live `stage_strategy_draft` schema; a create example is also checked for the axes a new
|
|
5
|
+
strategy needs. Its shape is current.
|
|
6
|
+
|
|
7
|
+
These examples show the envelope and the small axes. A full composition (report sections, conditions, signal
|
|
8
|
+
rules) is in `strategy-examples`' playbooks, which have their own compile gate.
|
|
9
|
+
|
|
10
|
+
Every `<UPPER_SNAKE>` value is a placeholder: `<STRATEGY_ID>` is the id a read returned. Never send a placeholder as
|
|
11
|
+
written, and never invent an id. `draftVersion` is the version `get_strategy_draft` returned: 0 when there was no
|
|
12
|
+
draft, and for a new create draft. Each axis you send is written whole, and an axis you omit keeps the value the draft
|
|
13
|
+
already has.
|
|
14
|
+
|
|
15
|
+
## Open a create draft
|
|
16
|
+
|
|
17
|
+
Omit `strategyId`. The response carries the minted id, which every later call names. A new strategy needs IDENTITY
|
|
18
|
+
and TIMEFRAME_PROFILE before it can be created.
|
|
19
|
+
|
|
20
|
+
```json stage_strategy_draft
|
|
21
|
+
{
|
|
22
|
+
"request": {
|
|
23
|
+
"draftVersion": 0,
|
|
24
|
+
"axes": {
|
|
25
|
+
"IDENTITY": {
|
|
26
|
+
"name": "Squeeze Breakout",
|
|
27
|
+
"description": "Enters when a volatility squeeze resolves upward on rising volume.",
|
|
28
|
+
"tagline": "Buys the release of a tight range"
|
|
29
|
+
},
|
|
30
|
+
"TIMEFRAME_PROFILE": { "timeframe": "1h" }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Change an existing strategy's setup gates
|
|
37
|
+
|
|
38
|
+
SETUP_GATES is sent whole: all three gates, even when only one changes.
|
|
39
|
+
|
|
40
|
+
```json stage_strategy_draft
|
|
41
|
+
{
|
|
42
|
+
"request": {
|
|
43
|
+
"strategyId": "<STRATEGY_ID>",
|
|
44
|
+
"draftVersion": 2,
|
|
45
|
+
"axes": {
|
|
46
|
+
"SETUP_GATES": { "minAggregateScore": 60, "minRequiredCount": 1, "minAtrPct": 0.5 }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
@@ -44,10 +44,11 @@ Before committing a CREATE, every "no" here is a decision to state, not an omiss
|
|
|
44
44
|
| `efficiency` wN | `<code>_er` (1 straight, ~0 chop) | `close_ltf_er` |
|
|
45
45
|
| `maxShare` wN | `<code>_maxShare` | `volBase_ltf_maxShare` |
|
|
46
46
|
| `classifyZone` / `classifyState` | `<code>_zone` / `<code>_state` | `RSI14_zone`, `ADX_state` |
|
|
47
|
+
| `levelInteraction` | `<code>_lvl`, `<code>_lvl_b<N>` at a buffer of N bps | `bbUpper_lvl`, `bbUpper_lvl_b25` |
|
|
47
48
|
|
|
48
49
|
Non-anchor rungs affix `_ltf` (lower) / `_htf` (regime): `MAalign_htf`,
|
|
49
50
|
`zones_htf_support_dist`. Chains are bounded at two: inner `distance`/`spread` → outer
|
|
50
|
-
`trajectory`/`aggregate`/`
|
|
51
|
+
`trajectory`/`aggregate`/`rank` (`EMA5 spread EMA13 × trajectory` →
|
|
51
52
|
`EMA5_EMA13_spread_now` + `_trend`).
|
|
52
53
|
|
|
53
54
|
**Timeframe references are two families.** *Relative* (`anchor`/`lower`/`regime`) re-resolve
|
|
@@ -84,7 +85,11 @@ no timeframe reference at all: bind them `{rel: 'anchor'}` — the default — a
|
|
|
84
85
|
previous UTC session from any anchor. An absolute reference is REFUSED at save, including `1d`,
|
|
85
86
|
because there is no rung for one to select. They are catalogued price levels, so `dist_PDH gte 0`
|
|
86
87
|
composes directly. (`distance` still rejects an `offset`, and a clause still compares a column
|
|
87
|
-
|
|
88
|
+
with a literal or with its own previous completed bar, never with another column; neither of those
|
|
89
|
+
shapes is what a previous-session level needed.) The same holds
|
|
90
|
+
for every timeless metric — these levels, funding, the regime and the other bundle
|
|
91
|
+
reads: a `value` column over one accepts only `offset: 0`, and a larger offset is REFUSED at save,
|
|
92
|
+
because there is no earlier bar to cut back to.
|
|
88
93
|
|
|
89
94
|
## Conditions
|
|
90
95
|
|
|
@@ -92,8 +97,11 @@ against a literal; neither of those shapes is what a previous-session level need
|
|
|
92
97
|
required, no defaults. A `clock` key is REFUSED (the per-condition evidence clock was retired in
|
|
93
98
|
contract `61.0.0`), so is a condition-level `closes` key — the hold lives on each clause and each
|
|
94
99
|
group (contract `84.0.0`) — and so is a boolean `exit` key: the side an exit closes is its own
|
|
95
|
-
`exitSide` (contract `88.0.0`). Clauses
|
|
96
|
-
|
|
100
|
+
`exitSide` (contract `88.0.0`). Clauses — the operator a header takes is served per header
|
|
101
|
+
(`conditionOperators`), by its output kind: numeric headers take the comparisons
|
|
102
|
+
`lt|lte|gte|gt|between` and the prior-bar operators `crossesAbove|crossesBelow|increased|decreased`;
|
|
103
|
+
rank headers take the comparisons only; classification, direction and boolean headers take
|
|
104
|
+
`is|in|enters|exits` with the served vocabulary; event headers take `is|in`. Groups:
|
|
97
105
|
`ALL | ANY | NOT | N_OF` (with `n`), depth ≤ 2. Every clause and every group carries a required
|
|
98
106
|
`hold: { atLeast, of }` — `{ "atLeast": 1, "of": 1 }` is a single read; a clause or group without
|
|
99
107
|
`hold` is refused (`CONDITION_HOLD_INVALID`). `conditionRef` composes named conditions (no
|
|
@@ -104,6 +112,46 @@ decider — so order breaks ties between carriers that AGREE. Carriers that DISA
|
|
|
104
112
|
`NEITHER` and stand the coin aside; declaration order never picks a side between them. Building
|
|
105
113
|
blocks carry `null`.
|
|
106
114
|
|
|
115
|
+
**Prior-bar operators — a column against its own previous completed bar.** A clause compares its
|
|
116
|
+
column with a literal, or with the same column's reading on the completed bar before the one it
|
|
117
|
+
decides — never with another column. Six operators read that prior bar:
|
|
118
|
+
|
|
119
|
+
| Operator | Literal | TRUE when |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `crossesAbove` | a number `L` | `x > L` on the decided bar and `x ≤ L` on the bar before |
|
|
122
|
+
| `crossesBelow` | a number `L` | `x < L` on the decided bar and `x ≥ L` on the bar before |
|
|
123
|
+
| `increased` | none | `x` is strictly above its prior-bar reading |
|
|
124
|
+
| `decreased` | none | `x` is strictly below its prior-bar reading |
|
|
125
|
+
| `enters` | one label | the label is read now and was not on the bar before |
|
|
126
|
+
| `exits` | one label | the label was read on the bar before and is not now |
|
|
127
|
+
|
|
128
|
+
The crossing boundary is Pine's (`ta.crossover`): a bar sitting exactly on `L` has not crossed until a
|
|
129
|
+
later bar closes above it. A change takes no literal at all — `{ "op": "increased" }` with no `value`.
|
|
130
|
+
A prior-bar clause reads COMPLETED bars only, on every surface: the decided bar and the one before
|
|
131
|
+
it, never the forming bar, so it is never provisional. A failed read on either bar is UNRESOLVED; a
|
|
132
|
+
bar with no value (a null) reads FALSE. A header admits them only where its stored history keeps the
|
|
133
|
+
bar before: a header whose `conditionHold.maxWindow` is 1 is served none of the six, so
|
|
134
|
+
`vol24hUsd increased` is refused (`CONDITION_OPERATOR_UNSUPPORTED`, naming the operators it does
|
|
135
|
+
admit), and on a header that keeps them each reads one bar less than its window.
|
|
136
|
+
|
|
137
|
+
**An edge is held "within n closes" only.** `crossesAbove`, `crossesBelow`, `enters` and `exits` mark
|
|
138
|
+
the bar a state changed, so like a crossover event their `maxAtLeast` is 1 — hold them "within n
|
|
139
|
+
closes", never "in a row". `increased` and `decreased` are no edge: "3 closes in a row" of
|
|
140
|
+
`increased` is three rising closes. A group whose members must all read TRUE together (`ALL`) over an
|
|
141
|
+
edge admits "within" only too. Every prior-bar read counts toward the frame-read budget: a clause
|
|
142
|
+
held "within 3 closes" reads four bars.
|
|
143
|
+
|
|
144
|
+
Recipes — each one clause:
|
|
145
|
+
|
|
146
|
+
- **Close crossed above the 20 SMA** — `dist_SMA20 crossesAbove 0` (the Moving Averages module).
|
|
147
|
+
`dist_SMA20` is the close's distance from the average, so crossing 0 is the close crossing it.
|
|
148
|
+
- **RSI entered overbought** — `RSI14_zone enters "overbought"` (the RSI module).
|
|
149
|
+
- **Squeeze released** — `kcSqueeze exits "on"`, over a `KC_SQUEEZE` value column.
|
|
150
|
+
- **New 20-bar high** — `donchianHi increased`, over a `DONCHIAN_UPPER` value column: the 20-bar
|
|
151
|
+
upper channel rises exactly when the decided bar's high exceeds every high of the 20 bars before it.
|
|
152
|
+
- **RSI crossed above 50 within the last 3 closes** — `RSI14_now crossesAbove 50` holding
|
|
153
|
+
`{ "atLeast": 1, "of": 3 }`.
|
|
154
|
+
|
|
107
155
|
**Two things block a trade, not one.** `required: true` = a FALSE reading blocks compose-trade
|
|
108
156
|
before billing. And the RESOLVED verdict binds entry DIRECTION: `UP` admits long setups only,
|
|
109
157
|
`DOWN` short only, and `NEITHER`/`UNRESOLVED` admit none — the refused side is absent from the
|
|
@@ -247,10 +295,12 @@ it opened under, with the report and timeframes they read: an exit-rule edit app
|
|
|
247
295
|
opened after it, and an open position keeps the exit rules it opened with, so an open position never
|
|
248
296
|
refuses a commit or a rebind.
|
|
249
297
|
|
|
250
|
-
**A state column is not a flip event
|
|
251
|
-
`ST_DIR is "bullish"` is a regime filter and never an
|
|
252
|
-
|
|
253
|
-
applies to every persisting
|
|
298
|
+
**A state column is not a flip event — `is` reads the state, `enters` reads the flip.** `ST_DIR`
|
|
299
|
+
reads the same on every bar of a trend, so `ST_DIR is "bullish"` is a regime filter and never an
|
|
300
|
+
entry signal. The flip is `ST_DIR enters "bullish"`: TRUE on the one completed bar the direction
|
|
301
|
+
turned, with no event column needed. The same distinction applies to every persisting
|
|
302
|
+
classification: `MAalign`, `ADX_state`, `zone` — `is` for the regime, `enters` / `exits` for the
|
|
303
|
+
bar it changed.
|
|
254
304
|
|
|
255
305
|
**Name the anchor when a metric is calibrated for one.** `KC_SQUEEZE is "on"` reads *on* about 57% of
|
|
256
306
|
1h crypto bars even at canonical parameters — Bollinger σ is close-to-close while ATR captures
|
|
@@ -258,6 +308,145 @@ intrabar range, so σ/ATR runs low here — against about 19% at 15m. It is a se
|
|
|
258
308
|
below and close to useless at 1h. A metric whose selectivity depends on the anchor is stated with the
|
|
259
309
|
anchor, or the author gates on something that admits most bars.
|
|
260
310
|
|
|
311
|
+
## Bar geometry
|
|
312
|
+
|
|
313
|
+
What one bar did — against a price level, in its own shape, against the bar before it, and how many
|
|
314
|
+
bars ran one way. Every value here reads the bar's own open, high, low and close, so it is a candle
|
|
315
|
+
column: it takes a timeframe reference and the Confirmed / Developing selector like any other.
|
|
316
|
+
|
|
317
|
+
**`levelInteraction` — what the bar did at a level.** It classifies the newest bar of the column as
|
|
318
|
+
one of five labels: `touch_from_below`, `touch_from_above`, `reject_from_below`, `reject_from_above`,
|
|
319
|
+
`none`. The side is where the PRIOR close stood against the PRIOR bar's level — from below when it
|
|
320
|
+
closed at or under it — so a level that moved between the two bars is judged on the bar it stood at.
|
|
321
|
+
From below: a bar that closes through the level is a break and reads `none`; a bar whose high
|
|
322
|
+
reached the level and whose close came back under it is `reject_from_below`; a bar whose high came
|
|
323
|
+
within the buffer of the level is `touch_from_below`. From above is the mirror on the low.
|
|
324
|
+
|
|
325
|
+
- **A reject bar reports reject.** It is never also reported as a touch, so "touched or rejected" is
|
|
326
|
+
an `in` clause over both labels of a side: `bbUpper_lvl in ["touch_from_below","reject_from_below"]`.
|
|
327
|
+
- **`buffer`** is whole basis points of the level, 0–500, default 0 — `buffer: 25` is 0.25%. Only the
|
|
328
|
+
touch reads it: a reject must pierce the level itself. Two buffers are two columns (`bbUpper_lvl`,
|
|
329
|
+
`bbUpper_lvl_b25`).
|
|
330
|
+
- **A break is a crossing, never a label.** The break of a level is `distance` over the same level
|
|
331
|
+
crossing 0: `dist_bbUpper crossesAbove 0` from below, `crossesBelow 0` from above. On the strategy's
|
|
332
|
+
own rung a `…_from_below` label never holds on a bar where `crossesAbove 0` holds.
|
|
333
|
+
- **Which levels take it.** A candle-homed level — a band (`BB_UPPER`, `BB_LOWER`, `KC_UPPER`,
|
|
334
|
+
`KC_MID`, `KC_LOWER`), `VWAP`, a moving average (`SMA20`, `EMA20`, …), `ST_LINE`, `PSAR`, the
|
|
335
|
+
Ichimoku lines, the swing levels (`SWING_FRACTAL_HIGH`, `SWING_FRACTAL_LOW`) — reads its own value at
|
|
336
|
+
each of the two bars. A prior-session level or pivot (`PDH`,
|
|
337
|
+
`PDL`, `PDO`, `PIVOT_P`, `PRIOR_TPO_POC`, …) has one reading for the session, used as both bars' level;
|
|
338
|
+
a clause over it is a single read with no hold, because the session profile keeps no earlier
|
|
339
|
+
reading. A developing-session level (`TPO_POC`, `TPO_VAH`, …) and the naked points of control move
|
|
340
|
+
between bars with no earlier reading kept, so they are not offered it; neither are the Donchian
|
|
341
|
+
rails (`DONCHIAN_UPPER`, `DONCHIAN_LOWER`), each the extreme of the window ending at the very bar it
|
|
342
|
+
would classify, which a close can never pass. A price fact (`CLOSE`, `HLC3`, `MARK`) is refused: it
|
|
343
|
+
is the price, not a level.
|
|
344
|
+
- **A higher-rung level reads that rung's completed bar.** `bbUpper_htf_lvl` classifies the last
|
|
345
|
+
completed regime-rung bar from its own high, low and close, never the strategy bar's price — so
|
|
346
|
+
compose its break on the same rung, `dist_bbUpper_htf crossesAbove 0`.
|
|
347
|
+
|
|
348
|
+
**`BB_TOUCH` and `BB_UPPER × levelInteraction` answer different questions.** `BB_TOUCH` (`BBtouch`)
|
|
349
|
+
says where the CLOSE sits in %B — inside the band edge or not. `BB_UPPER × levelInteraction` says what
|
|
350
|
+
the bar's RANGE did at the edge — touched it, or pierced it and closed back.
|
|
351
|
+
|
|
352
|
+
**Candle patterns** — the bar's shape against the ten bars before it (TA-Lib's default settings). A
|
|
353
|
+
pattern carries no trend context: "a hammer in a downtrend" is `pin is "lower_wick"` plus a separate
|
|
354
|
+
trend clause.
|
|
355
|
+
|
|
356
|
+
| Key | Header | Labels |
|
|
357
|
+
|---|---|---|
|
|
358
|
+
| `CANDLE_DOJI` | `doji` | boolean: the body is at most a tenth of the average range |
|
|
359
|
+
| `CANDLE_PIN` | `pin` | `lower_wick`, `upper_wick`, `none` — a short body with one long wick |
|
|
360
|
+
| `CANDLE_ENGULFING` | `engulf` | `bullish`, `bearish`, `none` — the body engulfs the prior opposite body |
|
|
361
|
+
| `BAR_CONTAINMENT` | `barContain` | `inside`, `outside`, `none` — the range inside, or around, the prior range |
|
|
362
|
+
|
|
363
|
+
**Gaps** — the open against the bar before it, as three separate keys.
|
|
364
|
+
|
|
365
|
+
| Key | Header | Reads |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| `GAP` | `gap` | `up`, `down`, `none` — the open above the prior high, or below the prior low |
|
|
368
|
+
| `GAP_RANGE_CLEAR` | `gapClear` | `up`, `down`, `none` — the whole range clear of the prior range (stricter) |
|
|
369
|
+
| `GAP_PCT` | `gapPct` | the signed % from the prior close to the open |
|
|
370
|
+
|
|
371
|
+
**Runs** — report columns counting consecutive bars, capped at ±20. Gating "three closes in a row" is
|
|
372
|
+
the hold's job; a run column shows the streak.
|
|
373
|
+
|
|
374
|
+
| Key | Header | Counts |
|
|
375
|
+
|---|---|---|
|
|
376
|
+
| `CLOSE_RUN` | `closeRun` | consecutive closes above (+) or below (−) the close before |
|
|
377
|
+
| `CANDLE_RUN` | `candleRun` | consecutive candles of the newest bar's colour, white (+) or black (−) |
|
|
378
|
+
|
|
379
|
+
**Price sources** — per-bar price facts that take every transform a close takes: `HL2` (`hl2`,
|
|
380
|
+
median), `HLC3` (`hlc3`, typical), `OHLC4` (`ohlc4`, average).
|
|
381
|
+
|
|
382
|
+
**"Within the last N closed bars" is the hold `{ "atLeast": 1, "of": N }`.** A pattern or a gap held
|
|
383
|
+
within N reads each of the N bars against its own predecessor.
|
|
384
|
+
|
|
385
|
+
Recipes — the level recipes on candle-homed levels:
|
|
386
|
+
|
|
387
|
+
- **Price rejected the upper Bollinger band** — a `BB_UPPER × levelInteraction` column, header
|
|
388
|
+
`bbUpper_lvl`; the clause `bbUpper_lvl is "reject_from_below"`.
|
|
389
|
+
- **Price touched or rejected VWAP from above** — `VWAP_lvl in ["touch_from_above","reject_from_above"]`,
|
|
390
|
+
with `buffer: 10` for a touch within 0.1% (header `VWAP_lvl_b10`).
|
|
391
|
+
- **Price broke the 20 EMA** — `dist_EMA20 crossesAbove 0`, the `distance` column over the same level.
|
|
392
|
+
- **A bullish engulfing within the last 3 bars** — `engulf is "bullish"` holding
|
|
393
|
+
`{ "atLeast": 1, "of": 3 }`.
|
|
394
|
+
- **An inside bar** — `barContain is "inside"`.
|
|
395
|
+
- **A gap up** — `gap is "up"`; `gapClear is "up"` when the whole range must clear.
|
|
396
|
+
- **A run of candles as a report column** — a `CANDLE_RUN` value column (`candleRun`) beside the
|
|
397
|
+
thesis columns; no clause needed.
|
|
398
|
+
- **The 20-bar simple moving average of typical price** — `HLC3 × aggregate(20)` with
|
|
399
|
+
`aggregate: "mean"`, header `hlc3_mean20`.
|
|
400
|
+
|
|
401
|
+
## Swing structure
|
|
402
|
+
|
|
403
|
+
Swing highs, swing lows and the structure they spell, from the market-data swing engine. Every value
|
|
404
|
+
is stored under the bar it describes, so a swing column holds and takes the prior-bar operators like
|
|
405
|
+
any candle column.
|
|
406
|
+
|
|
407
|
+
| Key | Header | Reads |
|
|
408
|
+
|---|---|---|
|
|
409
|
+
| `SWING_FRACTAL_HIGH` | `fractalHi` | the most recent swing high — a price level |
|
|
410
|
+
| `SWING_FRACTAL_LOW` | `fractalLo` | the most recent swing low — a price level |
|
|
411
|
+
| `SWING_FRACTAL_LABEL` | `fractalLabel` | `HH`, `HL`, `LH`, `LL`, `none` — the most recent swing against the one before it on its side |
|
|
412
|
+
|
|
413
|
+
**The rule.** A swing high is a bar whose high is at or above the highs of the two bars before it and
|
|
414
|
+
strictly above the highs of the two bars after it; a swing low mirrors it on the lows. An equal extreme
|
|
415
|
+
is allowed on the left and not on the right, so of a run of equal highs the newest is the swing (Pine's
|
|
416
|
+
`ta.pivothigh(2, 2)`). A swing is published from the bar that completes its right side — two bars
|
|
417
|
+
after its own — never earlier and never from a forming bar. Each level is held until the next swing on
|
|
418
|
+
its side replaces it.
|
|
419
|
+
|
|
420
|
+
- **The label is a held state, not an event.** It reads the most recent swing: `HH` / `LH` for a high
|
|
421
|
+
(an equal high is `LH` — it failed to make a higher high), `HL` / `LL` for a low (an equal low is
|
|
422
|
+
`HL`), `none` when no comparable pair is in reach. On too short a history it reads nothing, and a
|
|
423
|
+
clause over it reads FALSE.
|
|
424
|
+
- **A swing level does not remember a broken level.** Two to four bars after a break the level steps
|
|
425
|
+
to the next swing, so a retest of the BROKEN level is not a swing-level read.
|
|
426
|
+
- **The two levels take what every candle level takes** — `distance`, `spread`, `aggregate`,
|
|
427
|
+
`trajectory`, `efficiency` and `levelInteraction` — and both rank across the board
|
|
428
|
+
(`dist_fractalHi_rank_near`: the coins closest to their own last swing high).
|
|
429
|
+
- **The break is the crossing toward the level.** `dist_fractalHi crossesAbove 0` is a close through the
|
|
430
|
+
last swing high and never fires when the level steps onto a new swing, because the bar that confirms
|
|
431
|
+
a swing high sits under it. The opposite crossing (`dist_fractalHi crossesBelow 0`, or
|
|
432
|
+
`dist_fractalLo crossesAbove 0`) CAN fire on the bar the level steps past the close — it is not a
|
|
433
|
+
break.
|
|
434
|
+
- **A swing confirmed on the break bar can already have moved the label**, so a structure condition
|
|
435
|
+
paired with a break is held over the bars before it.
|
|
436
|
+
|
|
437
|
+
Recipes:
|
|
438
|
+
|
|
439
|
+
- **The swing structure is bullish** — `fractalLabel in ["HH","HL"]`.
|
|
440
|
+
- **A higher high was confirmed on this bar** — `fractalHi increased` (it also catches a second
|
|
441
|
+
higher high in a row, which `fractalLabel enters "HH"` cannot); `fractalHi decreased` is a lower
|
|
442
|
+
high.
|
|
443
|
+
- **Bullish break of structure** — a `SWING_FRACTAL_HIGH × distance` column, header `dist_fractalHi`;
|
|
444
|
+
the clause `dist_fractalHi crossesAbove 0`.
|
|
445
|
+
- **Bearish break of structure** — `dist_fractalLo crossesBelow 0`.
|
|
446
|
+
- **A break of the last swing low while the structure was bullish** — `dist_fractalLo crossesBelow 0`
|
|
447
|
+
together with `fractalLabel in ["HH","HL"]` holding `{ "atLeast": 1, "of": 3 }`, so the bullish
|
|
448
|
+
read may sit on the bars before the break.
|
|
449
|
+
|
|
261
450
|
## Entry
|
|
262
451
|
|
|
263
452
|
`{ trigger, levelOffsetAtrMultiple, validForBars }` — the axis's keys, and the trigger decides
|
|
@@ -385,8 +574,8 @@ RR 2, BE 1R, trail 1.2R/giveback 35 · swing trend (4h) 1.0–2.5 ATR, RR 2, BE
|
|
|
385
574
|
|
|
386
575
|
The one place a claim that the catalog LACKS something may live, and every row names the key it
|
|
387
576
|
denies so the claim can be checked. A claim about the grammar's shape (a clause compares one
|
|
388
|
-
column
|
|
389
|
-
permanent. A claim that a metric is absent belongs here, or nowhere.
|
|
577
|
+
column with a literal or with its own previous completed bar, never with another column; `distance`
|
|
578
|
+
rejects an `offset`) belongs in prose above — those are permanent. A claim that a metric is absent belongs here, or nowhere.
|
|
390
579
|
|
|
391
580
|
| Script / primitive | Key the catalog would need | Nearest expressible neighbour |
|
|
392
581
|
|---|---|---|
|
|
@@ -87,13 +87,13 @@ would be ambiguous. Conditions: `HTF_UP` building block (`MAalign_htf is "bullis
|
|
|
87
87
|
|
|
88
88
|
Platform `includePerpSpotFlow` (`perpSpotFlow` vocab: confirmed_bull / confirmed_bear /
|
|
89
89
|
perp_led_fragile / spot_led_accumulation / neutral), `includeStructureZones`
|
|
90
|
-
(`zones_htf_support_dist` signed
|
|
90
|
+
(`zones_htf_support_dist` signed % of price from the zone midpoint, positive above a support zone; `_age_h`), custom `Tape`
|
|
91
91
|
(`BUY_PRESSURE value`→`buyPres` 0–1, `RVOL value`, `CLOSE_CHANGE value`). Conditions:
|
|
92
92
|
`SPOT_ACCUM` (`perpSpotFlow is "spot_led_accumulation"`); `NEAR_SUPPORT`
|
|
93
|
-
(`zones_htf_support_dist between
|
|
93
|
+
(`zones_htf_support_dist between 0 2` + `zones_htf_support_age_h gte 12`); `DIP_BID` verdict
|
|
94
94
|
UP — refs plus an `N_OF(2)` quorum over `buyPres gte 0.55` / `RVOL gte 1.2` / `closeChg gt 0`
|
|
95
95
|
(quorum beats a brittle ALL); `FRAGILE_POP` verdict DOWN (`perp_led_fragile` +
|
|
96
|
-
`zones_htf_resist_dist between 0
|
|
96
|
+
`zones_htf_resist_dist between -2 0` + `RVOL gte 1.3`). Rules:
|
|
97
97
|
`flow_perp_spot_bull_divergence`/`_bear_divergence` 3 required · `cvd_bull_divergence`/
|
|
98
98
|
`_bear_divergence` 2 · `sr_at_support`/`_at_resistance` 2 · `structure_ob_approach` 1 ·
|
|
99
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
|
|
@@ -12,6 +12,7 @@ engine) and names its substitutions where the catalog lacks a primitive.
|
|
|
12
12
|
- VWAP reversion
|
|
13
13
|
- Donchian / Turtle breakout
|
|
14
14
|
- ICT / SMC (FVG + order blocks)
|
|
15
|
+
- Pivot highs / lows, HH-HL structure and break of structure
|
|
15
16
|
- Now native, formerly substituted
|
|
16
17
|
|
|
17
18
|
## TradingView ports — familiar processes, studio vocabulary
|
|
@@ -40,8 +41,9 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
40
41
|
distance to the plotted stop. The plotted trailing line is still best executed by the studio's
|
|
41
42
|
own stop engine — `trailingTriggerR: 0` (trail from entry), giveback ~30–40 (tight factor) or
|
|
42
43
|
45–55 (loose/chandelier) — because a trailing stop is a position-management mechanism, not a
|
|
43
|
-
column. `ST_DIR` is a persisting state
|
|
44
|
-
|
|
44
|
+
column. `ST_DIR` is a persisting state, so its FLIP is read with `enters`, natively:
|
|
45
|
+
`ST_DIR enters "bullish"` is TRUE on the one completed bar the direction turned, and
|
|
46
|
+
`ST_DIR exits "bullish"` on the bar it turned back — see `## Conditions` in the skill body.
|
|
45
47
|
- **MACD + 200 MA filter** → `ABOVE_200` building block (`dist_SMA200 gt 0`) referenced by a
|
|
46
48
|
carrier with `MACD_cross is "Bullish"`; rules `macd_bull/bear_cross` 3 required +
|
|
47
49
|
`ma_sma200_above/below` 2 required; swing-trend geometry.
|
|
@@ -68,16 +70,27 @@ rules about the columns themselves, not about porting — they live in the skill
|
|
|
68
70
|
and take `{rel: 'anchor'}` — see `## Header grammar` in the skill body.
|
|
69
71
|
- **ICT / SMC (FVG + order blocks)** → `STRUCT_ZONES` is the native zone engine:
|
|
70
72
|
`zones_htf_support_type` (`bullish FVG`/`bullish order block`), `zones_htf_support_dist
|
|
71
|
-
between
|
|
73
|
+
between 0 1.5`, `_age_h gte 12`, HTF bias required via `MAalign_htf`; rules
|
|
72
74
|
`structure_fvg_approach`/`structure_ob_approach` required (their `proximityPct` is the
|
|
73
75
|
in-zone dial). Liquidity sweeps, displacement, killzones and event *sequencing* are shapes the
|
|
74
|
-
grammar does not have — a clause compares one column
|
|
75
|
-
|
|
76
|
+
grammar does not have — a clause compares one column with a literal or with its own previous
|
|
77
|
+
completed bar, never with another column or an earlier event, so an ordered sequence of events
|
|
78
|
+
cannot be stated at all. That is a grammar limit, not a missing metric; name it as
|
|
76
79
|
one.
|
|
80
|
+
- **Pivot highs / lows, HH-HL structure and break of structure** (`ta.pivothigh(2, 2)` /
|
|
81
|
+
`ta.pivotlow(2, 2)`) → `SWING_FRACTAL_HIGH` / `SWING_FRACTAL_LOW` are the native swing levels and
|
|
82
|
+
`SWING_FRACTAL_LABEL` the structure label (`HH`/`HL`/`LH`/`LL`/`none`) — see `## Swing structure` in
|
|
83
|
+
the skill body. Pine's tie rule holds (an equal extreme on the left is allowed, on the right it is
|
|
84
|
+
not), and each level is the stepped, non-repainting series — held from the bar that completes the
|
|
85
|
+
pivot's right side, not plotted back on the pivot bar. Break of structure is
|
|
86
|
+
`dist_fractalHi crossesAbove 0` / `dist_fractalLo crossesBelow 0`; "structure is bullish" is
|
|
87
|
+
`fractalLabel in ["HH","HL"]`. Only two bars each side is native: a `ta.pivothigh(5, 5)` or longer
|
|
88
|
+
script ports onto the two-bar swing, and the spec-lock question says so.
|
|
77
89
|
- **Now native, formerly substituted** — WaveTrend (`WT1`/`WT2`), QQE (`QQE_RSI_MA`/`QQE_STOP`),
|
|
78
90
|
Hull (`HMA20`), Ichimoku (`ICHI_CONV`/`ICHI_BASE`/`ICHI_SPAN_A`/`ICHI_SPAN_B`/`ICHI_LAG`),
|
|
79
91
|
Parabolic SAR (`PSAR`), Keltner (`KC_UPPER`/`KC_MID`/`KC_LOWER`), daily pivots
|
|
80
92
|
(`PIVOT_P`/`PIVOT_R1`–`R3`/`PIVOT_S1`–`S3`), Williams %R (`WILLR14`), Stochastic RSI
|
|
81
93
|
(`STOCH_RSI14`), the TTM squeeze (`KC_SQUEEZE`), Connors RSI-2 (`RSI2`), the 9/21/50 EMAs
|
|
82
|
-
(`EMA9`/`EMA21`/`EMA50`),
|
|
94
|
+
(`EMA9`/`EMA21`/`EMA50`), literal previous-session levels (`PDH`/`PDL`/`PDO`), and fractal swing
|
|
95
|
+
structure (`SWING_FRACTAL_HIGH`/`SWING_FRACTAL_LOW`/`SWING_FRACTAL_LABEL`). Port these directly —
|
|
83
96
|
do not offer a substitute for a primitive the catalog serves.
|
|
@@ -38,10 +38,10 @@ Not "what were its returns" — whether it did *the thing it was told to do*.
|
|
|
38
38
|
`exitR`, and `giveBackR` (what it surrendered between its peak and the close). `exitEfficiency` is
|
|
39
39
|
a union — branch on its `state` before reading `efficiencyPercent`. All of them are gross of
|
|
40
40
|
costs; `netPnl` is the after-fee figure.
|
|
41
|
-
- The record around the trades: `
|
|
42
|
-
|
|
43
|
-
reasoning), `get_agent_activity_feed` and `get_user_activity_feed` (what
|
|
44
|
-
`get_agent_explorer` for where the agent stands among public agents.
|
|
41
|
+
- The record around the trades: `list_my_market_grid_entries` (the player's Market Grid entries —
|
|
42
|
+
`agentId` for one agent's, none for every entry), `get_agent_thought_log` and
|
|
43
|
+
`get_user_thought_log` (the reasoning), `get_agent_activity_feed` and `get_user_activity_feed` (what
|
|
44
|
+
happened), and `get_agent_explorer` for where the agent stands among public agents.
|
|
45
45
|
- `get_signal_performance` when the question is whether the agent's signals are working, as
|
|
46
46
|
distinct from whether its trades made money.
|
|
47
47
|
|