@battlegrid/mcp-server 32.0.11 → 32.0.16
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 +24 -2
- package/SKILL.md +8 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +11 -7
- package/skills/battlegrid-agent-management/SKILL.md +35 -1
- package/skills/battlegrid-agent-management/references/officer-stage-examples.md +77 -0
- package/skills/battlegrid-radar-deployment/SKILL.md +22 -3
- package/skills/battlegrid-radar-deployment/references/radar-stage-examples.md +4 -1
- package/skills/battlegrid-strategy-examples/SKILL.md +17 -11
- package/skills/battlegrid-strategy-examples/references/playbooks.md +6 -3
- package/skills/battlegrid-strategy-examples/references/recipes.md +1 -1
- package/skills/battlegrid-strategy-officer/SKILL.md +226 -0
- package/skills/battlegrid-strategy-officer/references/figures.md +105 -0
- package/skills/battlegrid-strategy-officer/references/proposal-shapes.md +174 -0
package/README.md
CHANGED
|
@@ -38,6 +38,27 @@ 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 — v122 (only an officer's own turn records a review)
|
|
42
|
+
|
|
43
|
+
**Breaking at v122: two officer review tools leave the catalog you discover.** v122 is the contract on top of v121.
|
|
44
|
+
Reconnect the proxy, or restart it, so discovery reads the new catalog.
|
|
45
|
+
|
|
46
|
+
### Removed tools — something you called is gone
|
|
47
|
+
|
|
48
|
+
- **`get_officer_review_context` and `submit_officer_review`** are published only to a Strategy Officer's own turn,
|
|
49
|
+
which runs on BattleGrid's server. A call naming either through this proxy is answered as an unknown tool. A second
|
|
50
|
+
opinion on an officer's review is your own reading, never a record.
|
|
51
|
+
|
|
52
|
+
### Added tool and fields
|
|
53
|
+
|
|
54
|
+
- **`run_officer({ officerId })`** runs one of your officers now, as its schedule would, and answers before any
|
|
55
|
+
review starts: `QUEUED` with `agentCount`, `NOTHING_TO_REVIEW` (nothing queued or billed) or `ALREADY_QUEUED`. It
|
|
56
|
+
follows `commanderAccess` from `get_account_state`, not `llmAccess`.
|
|
57
|
+
- **`pendingRun`** on each officer `list_officers` and `get_officer` serve: `{ state, queuedAt }` while a run is
|
|
58
|
+
waiting or running, otherwise null. **`presets`** on `list_officers`: the starting points a new officer is
|
|
59
|
+
staged from.
|
|
60
|
+
- The **Officers** workflow routes from `list_officers` to the `battlegrid-officers` skill.
|
|
61
|
+
|
|
41
62
|
## Contract history — v120 (the readiness ladder is three milestones)
|
|
42
63
|
|
|
43
64
|
**Breaking at v120: `get_onboarding_requirements` reshapes its rungs and re-specifies its counts.** v120 is the
|
|
@@ -3903,12 +3924,12 @@ Install the BattleGrid skills for AI agent instructions:
|
|
|
3903
3924
|
npx skills add playbattlegrid/battlegrid-mcp
|
|
3904
3925
|
```
|
|
3905
3926
|
|
|
3906
|
-
|
|
3927
|
+
The skills ship from this repo, all inside the npm tarball (`SKILL.md`, `skills/`).
|
|
3907
3928
|
|
|
3908
3929
|
**`battlegrid`** (repo root) is the connection skill and is authored here: how to connect, the
|
|
3909
3930
|
`{ account, request }` envelope, the two scopes, and where to go for everything else.
|
|
3910
3931
|
|
|
3911
|
-
The
|
|
3932
|
+
The `skills/battlegrid-*` are **exported from BattleGrid's server repository** — they are the
|
|
3912
3933
|
same instructions BattleGrid's own in-app Commander runs on, which is why they name the same tools
|
|
3913
3934
|
you reach over MCP:
|
|
3914
3935
|
|
|
@@ -3916,6 +3937,7 @@ you reach over MCP:
|
|
|
3916
3937
|
|---|---|
|
|
3917
3938
|
| `battlegrid-agent-management` | Commission and govern intelligence agents: interview and create one against a committed strategy and an approved model, change configuration and risk limits, rebind, halt, resume, archive, and act on live positions |
|
|
3918
3939
|
| `battlegrid-arena-play` | Enter 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 an agent generate it, submit, then read results and the reasoning journal |
|
|
3940
|
+
| `battlegrid-officers` | Set up and govern Strategy Officers: create or change one through its draft, run one now, read when each runs and what is waiting, and apply, push or dismiss the proposals its reviews carry — never writing a review |
|
|
3919
3941
|
| `battlegrid-market-analysis` | Read the current crypto market — regime, funding and open interest, leaders and laggards, a deep-dive on any named coin — and close with the levels worth watching |
|
|
3920
3942
|
| `battlegrid-radar-deployment` | Put agents on standing duty: per-coin Radar policies that fire on confirmed regime flips, and per-preset Arena deployment policies, previewed before they are written and un-deployed with the blast radius stated |
|
|
3921
3943
|
| `battlegrid-strategy-authoring` | Build a strategy from a plain-English idea: gather evidence, lock the spec, stage it into the strategy's draft, review the diff, diagnostics and impact, commit only on confirmation. Also fork, tune, restore, archive, preview |
|
package/SKILL.md
CHANGED
|
@@ -7,10 +7,10 @@ description: MCP skill for BattleGrid — play crypto prediction games (Market G
|
|
|
7
7
|
|
|
8
8
|
BattleGrid is a real-time cryptocurrency prediction gaming and trading platform. This MCP server gives AI agents access to play games, author trading strategies, and run strategy-bound intelligence agents.
|
|
9
9
|
|
|
10
|
-
## The other
|
|
10
|
+
## The other skills
|
|
11
11
|
|
|
12
12
|
This skill covers **connection**: how to reach BattleGrid, the `account` argument, and what each scope
|
|
13
|
-
grants. Everything about *using* the platform lives in the
|
|
13
|
+
grants. Everything about *using* the platform lives in the skills installed beside it, exported
|
|
14
14
|
from BattleGrid's own server so they name exactly the tools you reach here:
|
|
15
15
|
|
|
16
16
|
| For | Activate |
|
|
@@ -24,8 +24,13 @@ from BattleGrid's own server so they name exactly the tools you reach here:
|
|
|
24
24
|
| Reading your own position, agents, and open trades | `battlegrid-trade-analysis` |
|
|
25
25
|
| Finding and staging a trade for one of your agents — scan its coins, propose on one, approve or decline on your word | `battlegrid-trade-proposal` |
|
|
26
26
|
| Working out why an agent has not traded or has stopped | `battlegrid-strategy-doctor` |
|
|
27
|
+
| Setting up a Strategy Officer, running it now, and applying, pushing or dismissing what it proposes | `battlegrid-officers` |
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
`battlegrid-strategy-officer` is not one to activate: it is the review procedure a Strategy Officer runs on
|
|
30
|
+
BattleGrid's own server, whose review tools no external client is published, and the next skills export removes it.
|
|
31
|
+
To work with officers, activate `battlegrid-officers`.
|
|
32
|
+
|
|
33
|
+
Those skills carry the working arcs and the exact contracts. What follows here is the minimum needed
|
|
29
34
|
to connect and to know which one to open.
|
|
30
35
|
|
|
31
36
|
## Discover the live surface first
|
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.16";
|
|
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.16';
|
|
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,22 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "121.2.0",
|
|
4
4
|
"files": {
|
|
5
|
-
"battlegrid-agent-management/SKILL.md": "
|
|
5
|
+
"battlegrid-agent-management/SKILL.md": "09e5cbf241e2ba8f672e8292d40b3a50ee875f9b838003b95ee51928c5dbdf2e",
|
|
6
6
|
"battlegrid-agent-management/references/agent-stage-examples.md": "dffcc353d61dfc7b5bb6a2b3077fc6bf8c760a34c287cafb3b59b99306f24195",
|
|
7
|
+
"battlegrid-agent-management/references/officer-stage-examples.md": "a5a42792a8aa8f4af8cbf424d7c9aadbaa23df382ce17691a40a8d0e5447db63",
|
|
7
8
|
"battlegrid-arena-play/SKILL.md": "2e37970a66b8738ffbc445a23da90651c1f41b56ce4eef551582ab123517ef74",
|
|
8
9
|
"battlegrid-arena-play/references/entry-stage-examples.md": "0223f3743171ff213e943789d27590b2f65648398e85e10cd24938d5eb6af665",
|
|
9
10
|
"battlegrid-market-analysis/SKILL.md": "fcbe98e30e1edce39bb170c224cdf20181934f62b5f3a26b293787c70eeb6229",
|
|
10
|
-
"battlegrid-radar-deployment/SKILL.md": "
|
|
11
|
+
"battlegrid-radar-deployment/SKILL.md": "cf6664f51f92d40acce21a46f4fb078d9afe46c99c4f4b603bf002e73d4c5a23",
|
|
11
12
|
"battlegrid-radar-deployment/references/arena-stage-examples.md": "d27cfed1f4663f267c4a7ecebe4a6e65a450fc74bd14ce8987fb61af5bbf2ca5",
|
|
12
|
-
"battlegrid-radar-deployment/references/radar-stage-examples.md": "
|
|
13
|
+
"battlegrid-radar-deployment/references/radar-stage-examples.md": "13fc3f55d91d3da9754e1ed7fb27493b7b7ba4d40a6a7e8d1ee0365f90ea10de",
|
|
13
14
|
"battlegrid-strategy-authoring/SKILL.md": "39cfa46b1f640e6800edb7e85a8f2c4b6b8786cddf71bb8244d172a44fc11d75",
|
|
14
15
|
"battlegrid-strategy-authoring/references/strategy-stage-examples.md": "f10ecdc91f3660a708eee7a7bacefce2acbb46c9cf7e0ac1454bd54f839f39fd",
|
|
15
16
|
"battlegrid-strategy-doctor/SKILL.md": "e34e055b0cc9b355bc1adeb5d49d464eaa0756ca3f48dc89ca2ccdda94e1311f",
|
|
16
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
17
|
-
"battlegrid-strategy-examples/references/playbooks.md": "
|
|
18
|
-
"battlegrid-strategy-examples/references/recipes.md": "
|
|
17
|
+
"battlegrid-strategy-examples/SKILL.md": "5e0d3244ed0592b8af933c0925346b6bd3bbaeef6c7acb35329925a026e7cd05",
|
|
18
|
+
"battlegrid-strategy-examples/references/playbooks.md": "275352be43652ca0fd36a35981ea7ffcfabdc4f9b33293826656ee500d3795e9",
|
|
19
|
+
"battlegrid-strategy-examples/references/recipes.md": "806aae0b4842408f6e6ed3e108293073f4f31d1c27fa419ad57df9e1134f23e7",
|
|
19
20
|
"battlegrid-strategy-examples/references/tradingview-ports.md": "9eaa926cbe8606c6a09f91007a0606042afbfdc50173a5b13ae9336d2e9e9b08",
|
|
21
|
+
"battlegrid-strategy-officer/SKILL.md": "2c28ec4afa632b0f69b84b6d309f77d811d0d7452c1dbebce011b22044563780",
|
|
22
|
+
"battlegrid-strategy-officer/references/figures.md": "e9591e4f4edb9fc54066527ecd862aeef18badc96eaa924f14bd7da326eab541",
|
|
23
|
+
"battlegrid-strategy-officer/references/proposal-shapes.md": "20c9acd2b20e7920ed7ac9aa096604d91625b1d5b6438aa1ded7f6b63ecc9a22",
|
|
20
24
|
"battlegrid-trade-analysis/SKILL.md": "3f927b4f9796178526fa1673a2043c5f14358c39aa955de33192ea58641f18fb",
|
|
21
25
|
"battlegrid-trade-proposal/SKILL.md": "e761fc6bddc0d1db79fcb90046d03a18439562c8658de726ec286830edd6369b"
|
|
22
26
|
}
|
|
@@ -37,7 +37,7 @@ step named beside it before doing anything else.
|
|
|
37
37
|
which strategy an agent is bound to is the agent's own STRATEGY_BINDING axis (step 4).
|
|
38
38
|
`stage_strategy_draft`, `commit_strategy_draft`, `discard_strategy_draft`, `fork_strategy`,
|
|
39
39
|
`archive_strategy`, `restore_strategy`, `save_report_section` and `remove_saved_report_section` are
|
|
40
|
-
**not**, even though
|
|
40
|
+
**not**, even though this skill's tools put them in view in the conversation.
|
|
41
41
|
|
|
42
42
|
When the work is a strategy change, activate `strategy-authoring` and let its arc run — its
|
|
43
43
|
evidence, spec-lock, stage-review and confirm rules bind from that point. Do not drive a strategy
|
|
@@ -266,6 +266,40 @@ These tools carry no consent gate on either door: they are ownership-gated and b
|
|
|
266
266
|
in chat and through an external MCP client. Your confirm is interaction, never authorization —
|
|
267
267
|
never describe it as a permission check, and never add one of your own.
|
|
268
268
|
|
|
269
|
+
### 8. Strategy Officers: configure one like an agent
|
|
270
|
+
|
|
271
|
+
A Strategy Officer is a platform review the player schedules: on its hour it reads each in-scope agent's
|
|
272
|
+
record and records at most one proposal per agent for the player to apply or dismiss. It writes no draft
|
|
273
|
+
and commits nothing. Setting one up is configuration, and every run it schedules is billed to the
|
|
274
|
+
player's LLM credit — so it goes through its own draft, exactly as an agent does.
|
|
275
|
+
|
|
276
|
+
- **Read first.** `list_officers` for the roster (each officer's schedule, scope, last and next run),
|
|
277
|
+
`list_officer_drafts` for setups part-way through, `get_officer_draft` for one draft.
|
|
278
|
+
- **Create.** `stage_officer_draft` with **no** `officerId` and `draftVersion: 0` mints a new create
|
|
279
|
+
draft; every later call names the minted id. Stage only what the player asked for — every axis the
|
|
280
|
+
draft does not hold starts at its default (name "Strategy Officer", the catalog's default model, no
|
|
281
|
+
instructions, `DAILY` at 03:00 UTC, all active agents, all three levers). "Add an officer that
|
|
282
|
+
reviews my agents every morning at six and focuses on stops" is `NAME`, `SCHEDULE`
|
|
283
|
+
`{ cadence: 'DAILY', hourUtc: 6, weekdayUtc: null }` and `INSTRUCTIONS` `{ focusText }` in the
|
|
284
|
+
player's words — and ask which timezone "six" means before converting it to a UTC hour. Worked
|
|
285
|
+
inputs: `references/officer-stage-examples.md`.
|
|
286
|
+
- **Presets are starting points, staged as ordinary axes.** When the player names one — an exits
|
|
287
|
+
officer (levers `strategy`), a regime officer (`radar-deployment`), a rails officer (`agent`) or a
|
|
288
|
+
general one (all three) — stage its `NAME`, `INSTRUCTIONS` and `SCOPE`; the officer remembers no
|
|
289
|
+
preset.
|
|
290
|
+
- **Axes are whole.** `SCHEDULE` carries `cadence`, `hourUtc` and `weekdayUtc` (`0` Sunday … `6`
|
|
291
|
+
Saturday, for `WEEKLY` only, otherwise null); `SCOPE` carries both `agents` (`'ALL'` or agent ids
|
|
292
|
+
from `list_intelligence_agents`) and `levers`. `INSTRUCTIONS` are the player's brief in their own
|
|
293
|
+
words: at most 2,000 characters, naming no tool and no id — the commit refuses either.
|
|
294
|
+
- **Show, then commit on the player's word.** Read `get_officer_draft` and show the diff, the
|
|
295
|
+
diagnostics and the impact — `nextRunAt` is when it first runs, and a create's `admission` says
|
|
296
|
+
how many officers the player holds against the limit and how many more it admits (`officersRemaining`). Commit with `commit_officer_draft` naming
|
|
297
|
+
exactly the `draftVersion` and `committedRevision` the read returned, and only when the player says
|
|
298
|
+
so. A cadence of `OFF` keeps the setup and runs nothing.
|
|
299
|
+
- An officer's reviews and their Apply, Push and Dismiss live on the Officers page's inbox; they are not
|
|
300
|
+
this flow's. In the app, the player sets an officer up in its own console, where this conversation may be
|
|
301
|
+
docked beside the form — stage into the officer it is scoped to.
|
|
302
|
+
|
|
269
303
|
## When a write's outcome is unknown
|
|
270
304
|
|
|
271
305
|
An interrupted or timed-out write may have landed.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Strategy Officer stage examples
|
|
2
|
+
|
|
3
|
+
Complete `stage_officer_draft` inputs. Each is checked in CI against the live stage schema and the setup rules a
|
|
4
|
+
commit applies without a repository read. Its shape is current.
|
|
5
|
+
|
|
6
|
+
Every `<UPPER_SNAKE>` value is a placeholder. Replace it with the id a read returned:
|
|
7
|
+
|
|
8
|
+
- `<OFFICER_ID>` comes from `list_officers` or the draft you are editing.
|
|
9
|
+
- `<AGENT_ID>` and `<OTHER_AGENT_ID>` come from `list_intelligence_agents`.
|
|
10
|
+
- `<MODEL_ID>` is the row's `modelId` from `list_approved_models`.
|
|
11
|
+
|
|
12
|
+
Never send a placeholder as written, and never invent an id. `draftVersion` is the version `get_officer_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, the officer's current value, or for a new officer its default.
|
|
15
|
+
|
|
16
|
+
## Add an officer that reviews every morning at six and focuses on stops
|
|
17
|
+
|
|
18
|
+
Omit `officerId` to open a create draft. The response carries the minted id, which every later call names. Convert
|
|
19
|
+
the player's "six" to a UTC hour only after asking which timezone they mean; this example is 06:00 UTC.
|
|
20
|
+
|
|
21
|
+
```json stage_officer_draft
|
|
22
|
+
{
|
|
23
|
+
"draftVersion": 0,
|
|
24
|
+
"axes": {
|
|
25
|
+
"NAME": { "name": "Morning Stops Officer" },
|
|
26
|
+
"SCHEDULE": { "cadence": "DAILY", "hourUtc": 6, "weekdayUtc": null },
|
|
27
|
+
"INSTRUCTIONS": { "focusText": "Focus on stops: premature stop-outs and what price did after each exit." }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Start from the regime preset, weekly on Monday
|
|
33
|
+
|
|
34
|
+
A preset is staged as ordinary axes: its name, its brief and its scope.
|
|
35
|
+
|
|
36
|
+
```json stage_officer_draft
|
|
37
|
+
{
|
|
38
|
+
"draftVersion": 0,
|
|
39
|
+
"axes": {
|
|
40
|
+
"NAME": { "name": "Regime Officer" },
|
|
41
|
+
"INSTRUCTIONS": {
|
|
42
|
+
"focusText": "Read the record by the regime each decision was scored against. Where a regime with a ready sample loses and the others win, propose a regime condition on the Radar rule that routes the agent."
|
|
43
|
+
},
|
|
44
|
+
"SCOPE": { "agents": "ALL", "levers": ["radar-deployment"] },
|
|
45
|
+
"SCHEDULE": { "cadence": "WEEKLY", "hourUtc": 7, "weekdayUtc": 1 }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Narrow an existing officer to two agents and the rails lever
|
|
51
|
+
|
|
52
|
+
SCOPE is sent whole: both the agents and the levers, even when only one changes.
|
|
53
|
+
|
|
54
|
+
```json stage_officer_draft
|
|
55
|
+
{
|
|
56
|
+
"officerId": "<OFFICER_ID>",
|
|
57
|
+
"draftVersion": 0,
|
|
58
|
+
"axes": {
|
|
59
|
+
"SCOPE": { "agents": ["<AGENT_ID>", "<OTHER_AGENT_ID>"], "levers": ["agent"] }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Change the model and pause the officer
|
|
65
|
+
|
|
66
|
+
A cadence of OFF keeps the setup and runs nothing.
|
|
67
|
+
|
|
68
|
+
```json stage_officer_draft
|
|
69
|
+
{
|
|
70
|
+
"officerId": "<OFFICER_ID>",
|
|
71
|
+
"draftVersion": 0,
|
|
72
|
+
"axes": {
|
|
73
|
+
"MODEL": { "modelId": "<MODEL_ID>" },
|
|
74
|
+
"SCHEDULE": { "cadence": "OFF", "hourUtc": 3, "weekdayUtc": null }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
@@ -107,7 +107,12 @@ is test-routing a regime. Never carry a fact from a radar conversation into it,
|
|
|
107
107
|
Do not confirm a draft whose diagnostics carry an error: stage a fix.
|
|
108
108
|
3. `preview_radar_resolution({ coinId, request: { kind: "DRAFT", draftVersion } })` at the version that
|
|
109
109
|
read returned. It composes the draft over the deployed policy **exactly as the commit will write it**,
|
|
110
|
-
and refuses a draft no longer at that version with `DRAFT_VERSION_MOVED`.
|
|
110
|
+
and refuses a draft no longer at that version with `DRAFT_VERSION_MOVED`. Its `ruleCloseCoverage`
|
|
111
|
+
counts, for each hours rule, the closes of its agent's `strategyTimeframe` bar the rule's hours let
|
|
112
|
+
it decide through `windowEndAt`. Before asking the player to commit, name every rule whose
|
|
113
|
+
`decidableCloses` is 0: its agent's timeframe, the served `windowEndAt`, and that it is this rule's
|
|
114
|
+
hours alone that hold no close — not the agent, which still decides closes it is on duty for through
|
|
115
|
+
another rule or the default. Never tell the player the agent will not trade.
|
|
111
116
|
|
|
112
117
|
To try a slot set without touching the draft, preview `{ kind: "SLOTS", slots }` — it resolves
|
|
113
118
|
explicit slots and nothing can commit it.
|
|
@@ -122,6 +127,19 @@ from what the player asked for:
|
|
|
122
127
|
| only in named hours and days | a rule with an hours condition |
|
|
123
128
|
| in named regimes during named hours | a rule with one hours condition and one regime condition |
|
|
124
129
|
|
|
130
|
+
When the player names an exchange session — New York, the New York open, London, the London open,
|
|
131
|
+
the London–New York overlap, Tokyo, the Tokyo open — call `get_radar_market_hours` and stage that session's `hours`
|
|
132
|
+
exactly as served, merged with any other session they name; never work session hours out yourself,
|
|
133
|
+
because New York and London shift with daylight saving on their own dates and the overlap changes
|
|
134
|
+
shape on each of those dates. The three opens are one hour wide, so they suit agents on 1h bars or
|
|
135
|
+
faster. The days stay the player's choice. Tell the player the staged hours are UTC, that an hour is
|
|
136
|
+
on duty from :00 through :59, and, when the session carries a `nextShift`, the date its hours change
|
|
137
|
+
and that the rule keeps the hours it was given.
|
|
138
|
+
|
|
139
|
+
Session closes are not presets. Asked for "the New York close" or any other session close, stage only
|
|
140
|
+
the hours the player names, or raise flattening before the session ends as a separate idea — never
|
|
141
|
+
work a close window out yourself.
|
|
142
|
+
|
|
125
143
|
When the ask maps to no single row, or a refusal rules out the construction the player asked for,
|
|
126
144
|
ask the player which construction they mean, naming the rows that fit — never stage one they did not
|
|
127
145
|
choose. Complete stage payloads for every row are in `references/radar-stage-examples.md`.
|
|
@@ -352,8 +370,9 @@ Four reads, cheapest first. Stop as soon as the player's question is answered.
|
|
|
352
370
|
refused it and nothing was traded. It is the only history of not-qualifying closes: the journal
|
|
353
371
|
writes no row for one, and `resolvesNow` keeps only each agent's last decision — the one
|
|
354
372
|
`reading.lastClose` (step 1) carries; this read holds every decision before it. Narrow with
|
|
355
|
-
`agentId
|
|
356
|
-
`
|
|
373
|
+
`agentId`. Each record carries its whole evidence, so keep `limit` small; page back by passing
|
|
374
|
+
the response's `nextCursor` as `cursor`, and stop when `nextCursor` is null — that is the end of
|
|
375
|
+
the history. A bar still
|
|
357
376
|
waiting inside its window has no record yet (`resolvesNow` shows its `closeDecision.state` as
|
|
358
377
|
`DEFERRED`), and records are kept 90 days.
|
|
359
378
|
4. **`get_radar_activity`** only for what steps 2 and 3 cannot do: more rows than its ten, a
|
|
@@ -56,7 +56,10 @@ regime condition is the regime-reading floor, and must be a level one of the sel
|
|
|
56
56
|
|
|
57
57
|
## On duty only in named hours and days
|
|
58
58
|
|
|
59
|
-
Hours are UTC, 0–23. Days run 0 (Sunday) to 6. Each is listed once.
|
|
59
|
+
Hours are UTC, 0–23. Days run 0 (Sunday) to 6. Each is listed once. For a named exchange session —
|
|
60
|
+
including the London and Tokyo opens and the London–New York overlap — stage the `hours` `get_radar_market_hours`
|
|
61
|
+
serves for it rather than a set of your own; the example below is a shape, not a session. No session
|
|
62
|
+
close is served: a close is staged only as hours the player names.
|
|
60
63
|
|
|
61
64
|
```json stage_radar_deployment_draft
|
|
62
65
|
{
|
|
@@ -78,10 +78,10 @@ add. Either way you never invent one. To section-qualify a duplicated header, re
|
|
|
78
78
|
`CONDITION_COLUMN_AMBIGUOUS` refusal offers.
|
|
79
79
|
|
|
80
80
|
**Event columns print only on their event.** `MACDhist_cross` and `EMA5_13` (Bullish/Bearish) carry a
|
|
81
|
-
value on the crossing bar and are null on every other one,
|
|
81
|
+
value on the crossing bar and are null on every other one, and a null reads FALSE. Use an event
|
|
82
82
|
column as a TRIGGER inside a carrier, and pair it with a persistent state — a spread sign,
|
|
83
|
-
`MAalign` — for regime. A condition that treats an event column as a standing state
|
|
84
|
-
nearly every bar,
|
|
83
|
+
`MAalign` — for regime. A condition that treats an event column as a standing state reads FALSE on
|
|
84
|
+
nearly every bar, so it is TRUE only on the crossing bar itself.
|
|
85
85
|
|
|
86
86
|
**The previous-session levels are TIMELESS.** `PDH`, `PDL`, `PDO` and the seven floor pivots
|
|
87
87
|
(`pivotP`, `pivotR1`–`R3`, `pivotS1`–`S3`) read the session profile, not a candle rung, so they take
|
|
@@ -228,9 +228,12 @@ column selecting Developing above the strategy timeframe (`CONDITION_VERDICT_REA
|
|
|
228
228
|
closure is what the condition reads directly plus everything reached through `conditionRef`,
|
|
229
229
|
transitively: a referenced condition contributes what IT reads, so moving the clause into a building
|
|
230
230
|
block does not launder it. A closure whose candle columns all read Confirmed — or whose operands no
|
|
231
|
-
bar moves at all, such as a published regime label
|
|
232
|
-
|
|
233
|
-
a
|
|
231
|
+
bar moves at all, such as a published regime label or a funding rate — admits a verdict at every
|
|
232
|
+
decision, because a decision reads the completed strategy bar and a level is compared against that
|
|
233
|
+
bar's close. A change over a horizon above the strategy timeframe — `chg4h` or `chg24h` on a `1h`
|
|
234
|
+
strategy, and `oiRegime`, which reads the `1h` change, below `1h` — is a developing read whatever its
|
|
235
|
+
selector: its bar is still in progress at the close, so it carries no verdict. Read a horizon at or
|
|
236
|
+
below the strategy timeframe instead, or a Confirmed `CLOSE_CHANGE` on the higher timeframe.
|
|
234
237
|
|
|
235
238
|
Evaluation is three-valued: UNRESOLVED never collapses to FALSE; a developing read is provisional,
|
|
236
239
|
and so is any reading taken on the display lane's forming bar.
|
|
@@ -266,10 +269,11 @@ the served domain is refused (`CONDITION_HOLD_ILLEGAL`, the domain in `allowedDo
|
|
|
266
269
|
a header admits only Once:
|
|
267
270
|
|
|
268
271
|
- `NOT_REWINDABLE` — the value is stored for the current bar only (session and universe scalars,
|
|
269
|
-
ranks, zone entities, enrichment metrics,
|
|
272
|
+
ranks, zone entities, enrichment metrics, a change at or below the strategy timeframe). **Worked liquidity floor:**
|
|
270
273
|
`LIQUID_FLOOR` holds Once because `vol24hUsd` is a bundle scalar.
|
|
271
|
-
- `DEVELOPING` — the column reads
|
|
272
|
-
|
|
274
|
+
- `DEVELOPING` — the column reads a bar still forming: a Developing candle column above the strategy
|
|
275
|
+
timeframe, or a change over a horizon above it. **Developing columns cannot be held**; set a candle
|
|
276
|
+
column Confirmed to count closes, or read a change at or below the strategy timeframe.
|
|
273
277
|
- `OFFSET` — the column already reads an earlier bar; remove the offset to count closes.
|
|
274
278
|
- `BAR_STATE` — the header describes only the bar being decided.
|
|
275
279
|
- `HISTORY` — not enough history is stored on that timeframe to count more than one close.
|
|
@@ -300,8 +304,10 @@ anchor's own close — so Developing there is legal and changes no decision. A c
|
|
|
300
304
|
developing bar is single-frame: its clause holds Once (a Developing column cannot be held), it
|
|
301
305
|
carries no verdict and it cannot be an exit rule. One exception overrides both the stored value and the default: a column reading a metric with
|
|
302
306
|
no live value (itself, or a `spread` operand) reads completed bars at every rung, because the forming
|
|
303
|
-
bar publishes nothing for it.
|
|
304
|
-
|
|
307
|
+
bar publishes nothing for it. A change (`chg5m` … `chg24h`, and `oiRegime`'s `1h` change) carries its
|
|
308
|
+
horizon in the metric, so above the strategy timeframe it is Developing at every rung and under every
|
|
309
|
+
selector. The resolved answer for a column is served on `effectiveParameters.bars`, and it states both
|
|
310
|
+
exceptions.
|
|
305
311
|
|
|
306
312
|
**A higher-timeframe level is measured from the current strategy-bar price.** The distance to a `4h`
|
|
307
313
|
Donchian band, and every candle label classifier (`MA_ALIGN`, `PRICE_ZONE`, `BB_TOUCH`), compares
|
|
@@ -36,11 +36,14 @@ anything failing {LIQUID_FLOOR}."
|
|
|
36
36
|
Custom `Positioning`: `FUNDING_RATE value`→`rate`, `aggregate w24`→`rate_mean24`,
|
|
37
37
|
`FUNDING_ANN value`→`ann`, `OI_CHG value`→`oiChg`, `OI_PX_REGIME value`→`oiRegime` (vocab:
|
|
38
38
|
new longs / new shorts / short covering / long liquidation), `MARK spread ORACLE`→
|
|
39
|
-
`mark_oracle_spread`, `RSI14 value`, `
|
|
39
|
+
`mark_oracle_spread`, `RSI14 value`, `CLOSE_CHANGE value @ {abs: 1d}`→`closeChg_1d` (Confirmed, the
|
|
40
|
+
default above the strategy timeframe); plus `includeCvd`. Conditions:
|
|
40
41
|
`NO_LIQUIDATION_TAPE` required NOT-veto (`NOT [oiRegime is "long liquidation"]`);
|
|
41
42
|
`CROWDED_LONGS` building block (`ann gte 25` + `oiChg gte 3` + `oiRegime is "new longs"`);
|
|
42
|
-
`FADE_SHORT` verdict DOWN (ref + `RSI14 gte 65` + `
|
|
43
|
-
/ `SQUEEZE_LONG` verdict UP
|
|
43
|
+
`FADE_SHORT` verdict DOWN (ref + `RSI14 gte 65` + `closeChg_1d gte 5`); mirrored `SQUEEZED_SHORTS`
|
|
44
|
+
/ `SQUEEZE_LONG` verdict UP (`closeChg_1d lte -5`). The confirmation is the prior completed UTC day's
|
|
45
|
+
move, not today's: today's (`chg24h`) is a 1d bar still in progress at every 1h close, a developing
|
|
46
|
+
read no verdict may carry. Levels 1.0–2.5 ATR, RR 1.5, ATR floor 0.5%. PM: BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25).
|
|
44
47
|
|
|
45
48
|
### 3 · Relative-Strength Rotation with a Benchmark Gate — 4h cross-sectional
|
|
46
49
|
|
|
@@ -46,7 +46,7 @@ and is null otherwise:
|
|
|
46
46
|
|
|
47
47
|
An event column is a trigger inside a carrier, never a standing state; pair it with a persistent
|
|
48
48
|
state for regime. The skill body's `## Header grammar` states that rule and is the authority on it —
|
|
49
|
-
a condition that reads a `crossDetect` column as a state
|
|
49
|
+
a condition that reads a `crossDetect` column as a state reads FALSE on nearly every bar, because a null reads FALSE.
|
|
50
50
|
|
|
51
51
|
## Board-relative rank recipes
|
|
52
52
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: battlegrid-strategy-officer
|
|
3
|
+
description: Run one Strategy Officer's review of one agent — read the officer's own setup, the agent's served review figures and the current market read, cite every finding to a named figure with its readiness and population, choose at most one lever inside the officer's scope, and record the review with a complete proposal for the owner to apply, push or dismiss, or with no lever and the reason. It never writes a draft. Activate whenever a turn opens with a Strategy Officer brief naming an officer and an agent, or when the player asks one of their Strategy Officers to review an agent now.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Strategy Officer
|
|
7
|
+
|
|
8
|
+
You are one of the owner's Strategy Officers, reviewing one of their agents against its own record. The
|
|
9
|
+
owner set you up with a name, a brief, a schedule and a scope; the brief opening this turn names you and the
|
|
10
|
+
agent. Your job is to say, from the figures the platform serves, where this playbook wins and where it loses,
|
|
11
|
+
and — when a ready figure supports it — to propose the one change that would move that figure most.
|
|
12
|
+
|
|
13
|
+
Everything you do here is a **read**, and one record. You read the officer setup, the agent's record and the
|
|
14
|
+
market; you write exactly one thing, the review, through `submit_officer_review`. You have no draft tool and
|
|
15
|
+
you do not need one: a proposal lives on the review record, and the owner's working copy changes only when
|
|
16
|
+
the owner presses Push or Apply on the card. Nothing you submit reaches a live strategy, agent or Radar
|
|
17
|
+
deployment on its own.
|
|
18
|
+
|
|
19
|
+
Most runs are scheduled. **No one is reading this turn as it happens**, and no one will answer a question:
|
|
20
|
+
the review record is your whole output.
|
|
21
|
+
|
|
22
|
+
## The six failures this flow exists to prevent
|
|
23
|
+
|
|
24
|
+
1. **Diagnosing from prose.** *Cue: reaching for a journal entry, a thought log line or the agent's overlay
|
|
25
|
+
text to explain a result.* The figures exist; cite them. → step 3.
|
|
26
|
+
2. **A rate from a handful.** *Cue: a percentage over three trades.* A figure that reads `INSUFFICIENT_DATA`
|
|
27
|
+
has no rate, mean or median to state, and no lever rests on it. → step 3.
|
|
28
|
+
3. **A lever from the market alone.** *Cue: "the market is volatile now, so…".* The market read places a
|
|
29
|
+
finding in context; every lever rests on a figure of the agent's own record. → step 4.
|
|
30
|
+
4. **Two levers, or half an axis.** *Cue: two good ideas, or one changed field sent alone.* One lever per run,
|
|
31
|
+
one axis, sent whole. → step 5.
|
|
32
|
+
5. **A private write path.** *Cue: wanting to "just stage it".* You submit a proposal; the owner pushes or
|
|
33
|
+
applies it. → the negatives.
|
|
34
|
+
6. **Ending on a question.** *Cue: "shall I…?".* A scheduled run has no one to answer. → step 7.
|
|
35
|
+
|
|
36
|
+
## Sequence
|
|
37
|
+
|
|
38
|
+
### 0. When the player asks in the conversation
|
|
39
|
+
|
|
40
|
+
A scheduled brief names the officer and the agent. When the player asks instead ("have my officer review my
|
|
41
|
+
agent"), resolve both first: `list_officers` for the officer — none means telling them to add one from the
|
|
42
|
+
Officers page and stopping there; one is the officer; several, take the one the ask names, else the one
|
|
43
|
+
whose `scope.agents` covers the agent, else ask which. The agent is the one the ask names, read with
|
|
44
|
+
`list_intelligence_agents`. Then run the sequence below exactly as a scheduled run would.
|
|
45
|
+
|
|
46
|
+
### 1. Read your own setup first
|
|
47
|
+
|
|
48
|
+
`get_officer` with the officer id the brief names. Its `focusText` is the owner's standing brief for every
|
|
49
|
+
review you run — "weigh exits over entries" decides which figures you lead with and which lever you prefer
|
|
50
|
+
when two are supported. Its `scope` bounds you: `scope.levers` is the set of draft kinds you may propose for
|
|
51
|
+
(`strategy`, `agent`, `radar-deployment`), and `scope.agents` is `ALL` or the agent ids you review. An agent
|
|
52
|
+
outside `scope.agents` gets a review with lever `NONE`, reason `AGENT_OUT_OF_SCOPE`, and nothing else.
|
|
53
|
+
|
|
54
|
+
The brief carries the same instructions verbatim; where the two differ, the setup `get_officer` returns is
|
|
55
|
+
the current one.
|
|
56
|
+
|
|
57
|
+
### 2. Read the review context — the guards' inputs
|
|
58
|
+
|
|
59
|
+
`get_officer_review_context` with the officer id and the agent id. It serves:
|
|
60
|
+
|
|
61
|
+
- `lastReviewAt` — your latest review of this agent, or null when you never reviewed it;
|
|
62
|
+
- `closedDecisionsSince` — the agent's closed decisions since then. **Zero means nothing new to review**:
|
|
63
|
+
submit lever `NONE`, reason `NO_NEW_DECISIONS`, with one finding that says so, and stop;
|
|
64
|
+
- `strategyId` and `strategyOwnedByOwner` — **false means the owner did not author the strategy the agent
|
|
65
|
+
runs** (a public or library strategy). There is no strategy lever for this agent: see step 5;
|
|
66
|
+
- `targets` — the lever targets your scope allows, each with its `kind`, its `targetId` (the strategy id,
|
|
67
|
+
the agent id, or the coin id a Radar deployment is keyed by) and the `committedRevision` a proposal names
|
|
68
|
+
as its `baseRevision`.
|
|
69
|
+
|
|
70
|
+
### 3. Read the record, and state findings as figures
|
|
71
|
+
|
|
72
|
+
Five reads, every one keyed by the agent id:
|
|
73
|
+
|
|
74
|
+
- **`get_agent_review_analytics`** — the review read: the `overall` figure set, and the same figures sliced
|
|
75
|
+
by the regime recorded at decision (`regime`), by UTC hour (`hourOfDay`) and weekday (`weekday`), by coin
|
|
76
|
+
(`coin`, with fees), by cited condition (`condition`), LLM spend against result (`llm`), the rail refusals
|
|
77
|
+
(`railRefusals`) and the current halt (`halt`).
|
|
78
|
+
- **`get_agent_decision_quality`** — entries versus skips at each horizon, exit quality (premature stops,
|
|
79
|
+
what price did after the exit), the verdict mix and exit efficiency.
|
|
80
|
+
- **`get_agent_cost_economics`** — realized gross, fees and net per close outcome, and the winners costs
|
|
81
|
+
turned into losers.
|
|
82
|
+
- **`get_agent_entry_discipline`** — what confirmation cost (the confirmation tax), fast stop-outs and
|
|
83
|
+
adverse-first excursions. It takes a window: pass the review read's `windowStartAt` and `windowEndAt`.
|
|
84
|
+
- **`get_agent_conviction_calibration`** — win rate by conviction band, so a band that wins no more often than
|
|
85
|
+
a lower one reads as one.
|
|
86
|
+
|
|
87
|
+
Then state each finding as a **named figure** from the closed list in `references/figures.md`: its figure id,
|
|
88
|
+
the slice it was read over (a regime, an hour, a weekday, a coin, a cited condition, or the whole window), the
|
|
89
|
+
value exactly as served with its readiness and population, and one sentence in the figure's display
|
|
90
|
+
vocabulary. "Stopped too early in 9 of 20 forced exits (45%)", not "the stops are too tight". A regime is named
|
|
91
|
+
by its display name ("Bear Ranging"), never its stored value; a verdict, a close outcome, a rail refusal and a
|
|
92
|
+
halt reason the same way.
|
|
93
|
+
|
|
94
|
+
- **Readiness is the gate.** A figure reading `INSUFFICIENT_DATA` is reported as its counts and "too few to
|
|
95
|
+
report a rate", with its sample size — never as a rate, a mean, a median or a trend, and never as the
|
|
96
|
+
ground for a lever.
|
|
97
|
+
- **Counts and sums are observations.** Decisions, closed trades, realized net, fees and LLM spend are
|
|
98
|
+
reportable at any sample; a rate over the same rows is not.
|
|
99
|
+
- **A mark is quoted as a mark.** A `STOPPED_EARLY` verdict means the original target printed within 24 hours
|
|
100
|
+
of the stop. Say that; never state a profit the trade "would have made". The skip side of entries-versus-skips
|
|
101
|
+
is a recorded forward return, not money the agent left on the table.
|
|
102
|
+
- **Never compute a figure the reads do not serve.** Quote served values; compare two served values only by
|
|
103
|
+
naming both.
|
|
104
|
+
|
|
105
|
+
If a read fails or returns nothing for what you need, that figure is **UNDETERMINED**: name the read you could
|
|
106
|
+
not use, and lean on nothing from it. Never turn an absent read into a clean finding.
|
|
107
|
+
|
|
108
|
+
### 4. Place the record against the current market
|
|
109
|
+
|
|
110
|
+
Read the market for the agent's main coins — the coin rows of the review read with the most decisions:
|
|
111
|
+
`get_regime_snapshot` for each, at the agent's regime timeframe (on `get_intelligence_agent`), and
|
|
112
|
+
`get_market_context` for the context the agent itself reads. Use it to place a finding: "this playbook's
|
|
113
|
+
Bear Ranging record is the losing one, and BTC reads Bear Ranging now". **A market read is context, never a
|
|
114
|
+
lever**: with no ready figure of the agent's own record behind it, the review records the market context and
|
|
115
|
+
lever `NONE`, reason `INSUFFICIENT_DATA`.
|
|
116
|
+
|
|
117
|
+
### 5. Choose at most one lever, inside your scope
|
|
118
|
+
|
|
119
|
+
Rank what would change the observed figures most, and choose **at most one** lever. Name it as the axis and
|
|
120
|
+
the draft kind that applies it:
|
|
121
|
+
|
|
122
|
+
| Finding | Kind | Axis |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| Stops too tight or too wide, a risk-reward floor, a volatility floor | `strategy` | `TRADE_LEVEL_POLICY` |
|
|
125
|
+
| Break-even, trailing, time decay, invalidation exits | `strategy` | `POSITION_MANAGEMENT` |
|
|
126
|
+
| A cited condition that confirms losers, a missing gate | `strategy` | `CONDITIONS` |
|
|
127
|
+
| When an entry may be taken (confirmation, trigger) | `strategy` | `ENTRY` |
|
|
128
|
+
| The losing pattern needs data no report section carries; a section no condition or decision uses | `strategy` | `REPORT` |
|
|
129
|
+
| Rail refusals, exposure, leverage, daily limits, drawdown | `agent` | `TRADING_CONFIG` |
|
|
130
|
+
| Win rate flat across conviction bands; a posture the drawdown record does not support | `agent` | `BEHAVIOR` |
|
|
131
|
+
| LLM spend outrunning realized net on the agent's model | `agent` | `MODEL` |
|
|
132
|
+
| Losses across regimes no condition change fixes; a strategy the owner did not author; another strategy that fits the market better | `agent` | `STRATEGY_BINDING` |
|
|
133
|
+
| A regime or hours slice that loses while the others win | `radar-deployment` | `RULES` |
|
|
134
|
+
| Losses in hours or regimes no rule covers, routed by the catch-all | `radar-deployment` | `DEFAULT_SLOT` |
|
|
135
|
+
|
|
136
|
+
A regime or hours finding routes to the agent's **Radar rule** — a `REGIME` condition excluding the losing
|
|
137
|
+
regime, or an `HOURS` condition — never to the strategy. The second-best lever is recorded as a finding
|
|
138
|
+
without a lever, never as a second proposal.
|
|
139
|
+
|
|
140
|
+
**A rail refusal is routed by the setting it names, never by its gate stage.** Each `railRefusals` row of the
|
|
141
|
+
review read carries `tradingConfigMember`: the agent setting the owner moves to clear it. A row with a member is
|
|
142
|
+
a `TRADING_CONFIG` lever on that member, inside the agent lever — `EXPOSURE_BUDGET_EXHAUSTED` names
|
|
143
|
+
`maxConcurrentExposureUsd`, `DAILY_TRADE_LIMIT_REACHED` names `maxDailyTrades`, `INSUFFICIENT_EQUITY` names
|
|
144
|
+
`balanceThresholdUsd`. Its `gateStage` reading `ACCOUNT` says where the gate runs, not whose setting it is: never
|
|
145
|
+
say an exposure limit is outside your scope while you hold the agent lever. Lowering `balanceThresholdUsd` lowers
|
|
146
|
+
a safety floor, so weigh it as a risk-increasing change and say so. A row with a null member is a halt: it ends on
|
|
147
|
+
the owner's resume, and the read's `halt` names the stop that tripped it — a finding naming the owner's step,
|
|
148
|
+
never a lever.
|
|
149
|
+
|
|
150
|
+
**Rebind or edit.** A rebind replaces everything the agent reads and trades by — report, conditions, entry, trade
|
|
151
|
+
levels, exit policy and timeframe — while each open position keeps the rules it opened under. Propose one only
|
|
152
|
+
when no single strategy field would change the losing figures, or when the agent runs a strategy the owner did not
|
|
153
|
+
author; otherwise edit the strategy the agent runs.
|
|
154
|
+
|
|
155
|
+
- **A kind outside `scope.levers` is not yours.** Record the finding; if no in-scope lever is supported, submit
|
|
156
|
+
lever `NONE`, reason `LEVER_NOT_ALLOWED`, and say which out-of-scope lever the figures pointed at.
|
|
157
|
+
- **A strategy the owner did not author is not a strategy lever.** When `strategyOwnedByOwner` is false, record
|
|
158
|
+
the finding, state that the strategy must be forked from the library before a strategy lever can be proposed —
|
|
159
|
+
forking is the owner's step, never yours — and consider a rebind and the other agent and Radar levers within
|
|
160
|
+
your scope instead. If none is supported, submit lever `NONE`, reason `TARGET_NOT_OWNED`.
|
|
161
|
+
- **No ready figure, no lever.** When every figure a lever would rest on reads `INSUFFICIENT_DATA`, submit lever
|
|
162
|
+
`NONE`, reason `INSUFFICIENT_DATA`, naming the figure and its sample.
|
|
163
|
+
- **Nothing to change is an answer.** When the ready figures read as a playbook doing what it should, submit
|
|
164
|
+
lever `NONE`, reason `NO_CHANGE_SUPPORTED`. Never manufacture a lever to have something to propose.
|
|
165
|
+
|
|
166
|
+
### 6. Read the target's current values, then write the proposal whole
|
|
167
|
+
|
|
168
|
+
A proposal carries **exactly one axis, whole**, in the kind's own vocabulary — the complete current value of
|
|
169
|
+
that axis with your one change in it. So read the current value first:
|
|
170
|
+
|
|
171
|
+
- a strategy axis → `get_strategy` on the agent's `strategyId`; for `REPORT`, `list_strategy_categories` for the
|
|
172
|
+
platform sections a report may carry — the whole section list is sent, with your change in it;
|
|
173
|
+
- `TRADING_CONFIG` → `get_intelligence_agent` for the agent's current `tradingConfig`, and
|
|
174
|
+
`get_trading_config_catalog` for the bounds every member must sit inside;
|
|
175
|
+
- `BEHAVIOR` → `get_intelligence_agent` for the current `behavior`; all three members are sent;
|
|
176
|
+
- `MODEL` → `list_approved_models` for the models the account may select now, by `modelId`;
|
|
177
|
+
- `STRATEGY_BINDING` → `list_strategies` (active strategies only) for the owner's own strategies and the
|
|
178
|
+
library's, by `strategyId`; a rebind carries no `TRADING_CONFIG`, since the timeframes follow the strategy;
|
|
179
|
+
- `RULES` and `DEFAULT_SLOT` → `get_radar_deployment` on the coin the target names (`list_radar_deployments`
|
|
180
|
+
shows which coins route this agent); every rule is sent, in priority order, with the one rule changed, and a
|
|
181
|
+
rule or the catch-all names only agents the coin already routes.
|
|
182
|
+
|
|
183
|
+
The complete shape of every axis — which members it must carry and where each comes from — is in
|
|
184
|
+
`references/proposal-shapes.md`. The proposal is `{ kind, targetId, axes, baseRevision }`, where `targetId` and
|
|
185
|
+
`baseRevision` are the target's `targetId` and `committedRevision` from step 2.
|
|
186
|
+
|
|
187
|
+
### 7. Submit the review — the close
|
|
188
|
+
|
|
189
|
+
`submit_officer_review` with the officer id, the agent id, your one-line `verdict` on the record (at most 500
|
|
190
|
+
characters), the `findings`, and either the `proposal` with `noLeverReason` null, or `proposal` null with the
|
|
191
|
+
`noLeverReason`. Mint one UUID as its `idempotencyKey` for each submission and repeat that key when you retry the
|
|
192
|
+
same submission after a failed call, so one submission is recorded once; a resubmission with a different lever
|
|
193
|
+
(below) is a new submission with a new key. The server judges the proposal with the target kind's own assessment — the same validation,
|
|
194
|
+
diff, diagnostics and impact a stage would show — and stores it on the record, so the card shows only what the
|
|
195
|
+
owner could commit.
|
|
196
|
+
|
|
197
|
+
**A refused proposal becomes a recorded reason.** When the submit refuses with `LEVER_NOT_ALLOWED`,
|
|
198
|
+
`AGENT_OUT_OF_SCOPE`, `TARGET_NOT_OWNED` or `PROPOSAL_INVALID`, submit again with `proposal` null and that code
|
|
199
|
+
as `noLeverReason`, quoting the refusal's message in a finding's statement. Do not retry a different value to
|
|
200
|
+
get past a `PROPOSAL_INVALID`: the record says what was refused, and the next review starts from it.
|
|
201
|
+
|
|
202
|
+
**The review is written in every case** — no new decisions, out of scope, not enough data, nothing to change.
|
|
203
|
+
|
|
204
|
+
Close with the review, not a question. In a scheduled run that is the whole close. When the player is in the
|
|
205
|
+
conversation, add one sentence: the change is proposed and waiting on its card on the Officers page, where Apply
|
|
206
|
+
publishes it, Push places it in the editor beside their own edits, and Dismiss closes it.
|
|
207
|
+
|
|
208
|
+
## Negatives
|
|
209
|
+
|
|
210
|
+
- **Never take a draft action.** You name no stage, commit or discard tool of any draft kind, because none is
|
|
211
|
+
yours to call: the proposal is the review record's, and Push and Apply are the owner's. Never fork, create,
|
|
212
|
+
archive, pause or resume anything.
|
|
213
|
+
- **Never describe a proposal as staged or applied.** It is proposed and waiting for the owner's Apply or Push.
|
|
214
|
+
- **Never say the agent "learns", "self-improves" or "optimizes itself".** Nothing reaches the agent's decision
|
|
215
|
+
prompt from a review; a change happens only when the owner commits it.
|
|
216
|
+
- **Never present a counterfactual as a result.** A forward mark, a skip's return or a premature-stop verdict is
|
|
217
|
+
quoted as the recorded mark it is, never as money that "would have" been made.
|
|
218
|
+
- **Never call `test_generate_deployment_grid`.** It runs a billed generation against the owner's credits and
|
|
219
|
+
writes records; it is not a review read.
|
|
220
|
+
- Never diagnose from the agent's prose where a figure exists, and never present a count as a rate.
|
|
221
|
+
|
|
222
|
+
## Reporting discipline
|
|
223
|
+
|
|
224
|
+
- Numbers, codes and slices exactly as served, through their display names.
|
|
225
|
+
- Worst news first: a halt or a standing rail refusal outranks a tuning finding.
|
|
226
|
+
- One verdict line, a handful of findings that carry it, and at most one lever. Concise beats complete.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Figures a finding may cite
|
|
2
|
+
|
|
3
|
+
A finding names exactly one figure id from this list — the submit refuses any other. Each row says which read
|
|
4
|
+
serves the figure, where in that read's output it sits, its unit, and how its value and population go into the
|
|
5
|
+
finding. The owner's card renders each id through its own label and unit, so your `statement` carries the
|
|
6
|
+
words and the figure carries the number.
|
|
7
|
+
|
|
8
|
+
## How a finding carries a figure
|
|
9
|
+
|
|
10
|
+
A finding is `{ figureId, slice, value, statement }`:
|
|
11
|
+
|
|
12
|
+
- **`slice`** — the slice the figure was read over, or null for the agent's whole window:
|
|
13
|
+
`{ kind: 'REGIME', regime }` (a regime row's key — a regime, `UNAVAILABLE` or `ABSENT`),
|
|
14
|
+
`{ kind: 'HOUR', hourUtc }`, `{ kind: 'WEEKDAY', weekday }`, `{ kind: 'COIN', coinTicker }`, or
|
|
15
|
+
`{ kind: 'CONDITION', conditionKey, conditionName }`, each copied from the row it was read from.
|
|
16
|
+
- **`value`** — `{ readiness: 'READY', sampleSize, value }`, or `{ readiness: 'INSUFFICIENT_DATA', sampleSize }`
|
|
17
|
+
copied from a figure that reads `INSUFFICIENT_DATA`. A count or a sum is an observation and always `READY`.
|
|
18
|
+
- **`statement`** — one sentence naming the value and its population in display words: "won 4 of 19 closed
|
|
19
|
+
trades in Bear Ranging (21.1%)", "too few closed trades on Saturdays to report a rate (3)".
|
|
20
|
+
|
|
21
|
+
The outputs are `{ analytics }` for `get_agent_review_analytics`, `{ quality }` for
|
|
22
|
+
`get_agent_decision_quality`, `{ economics }` for `get_agent_cost_economics`, `{ stats }` for
|
|
23
|
+
`get_agent_entry_discipline`, and `{ calibration }` for `get_agent_conviction_calibration`. In the review read,
|
|
24
|
+
`figures` below means `analytics.overall` for the whole window, or a slice row's `figures` — `regime[]`,
|
|
25
|
+
`hourOfDay[]`, `weekday[]` or `coin[]` — for that slice.
|
|
26
|
+
|
|
27
|
+
## The figures
|
|
28
|
+
|
|
29
|
+
### Volume
|
|
30
|
+
|
|
31
|
+
- **`DECISIONS_EVALUATED`** — `figures.decisions`. Count of decisions. `sampleSize` is the count itself.
|
|
32
|
+
- **`CLOSED_TRADES`** — `figures.closedTrades`. Count of closed trades. `sampleSize` is the count itself.
|
|
33
|
+
- **`FORCED_EXITS`** — `figures.forcedExitCount`. Count of closes the Decision Quality band counts as forced
|
|
34
|
+
exits. `sampleSize` is the slice's `closedTrades`.
|
|
35
|
+
|
|
36
|
+
### Result
|
|
37
|
+
|
|
38
|
+
- **`WIN_RATE`** — `figures.winRate`: `ratePercent` when `READY`, in percent; its `sampleSize` is the closed
|
|
39
|
+
trades. State it with `figures.winningTrades` of `figures.closedTrades`.
|
|
40
|
+
- **`REALIZED_NET_USD`** — `figures.realizedNetUsd`, USD, signed: the sum of the closed trades' recorded net.
|
|
41
|
+
`sampleSize` is `figures.closedTrades`.
|
|
42
|
+
- **`REALIZED_FEES_USD`** — a coin row's `feesUsd`, or `analytics.llm.feesUsd` for the whole window, USD.
|
|
43
|
+
`sampleSize` is that row's `closedTrades`.
|
|
44
|
+
|
|
45
|
+
### Exits
|
|
46
|
+
|
|
47
|
+
- **`PREMATURE_STOP_RATE`** — `figures.prematureStop`, or `quality.exitQuality.prematureStop` for the whole
|
|
48
|
+
window: `prematureStopRatePercent` when `READY`, in percent, with `prematureStopCount` of `sampleSize` stop-outs
|
|
49
|
+
that were `STOPPED_EARLY` ("Stopped Early" — the original target printed within 24h of the stop).
|
|
50
|
+
- **`POST_EXIT_MEAN_RETURN`** — `quality.exitQuality.postExit`: `meanSignedReturnPct` when `READY`, percent,
|
|
51
|
+
direction-signed — the mean move in the trade's favour over the 24 hours after the exit. Positive means price
|
|
52
|
+
kept going the trade's way after it closed.
|
|
53
|
+
- **`MEDIAN_EXIT_EFFICIENCY`** — `quality.exitEfficiency.medianEfficiencyPercent`: `median` when `READY`, in
|
|
54
|
+
percent of the in-trade favourable move the exit kept.
|
|
55
|
+
- **`MEDIAN_MAE_R`** — `quality.exitEfficiency.medianMaeR`: `median` when `READY`, in R — how far against the
|
|
56
|
+
trade price went before the exit, in units of the initial risk.
|
|
57
|
+
|
|
58
|
+
### Entries
|
|
59
|
+
|
|
60
|
+
- **`ENTER_MEAN_RETURN`** — the `enter` cell of one `filterValue[]` row (in `quality.filterValue`, or a slice's
|
|
61
|
+
`figures.filterValue`): `meanSignedReturnPct` when `READY`, percent, direction-signed, at that row's `horizon`.
|
|
62
|
+
The statement names the horizon.
|
|
63
|
+
- **`SKIP_MEAN_RETURN`** — the `skip` cell of the same row, the same way. A positive skip return is a recorded
|
|
64
|
+
forward move the agent did not take — a mark, never money it lost.
|
|
65
|
+
- **`CONFIRMATION_TAX_USD`** — `stats.confirmationTax.totalUsd`, USD, signed (positive is an adverse fill against
|
|
66
|
+
the decided price). `sampleSize` is `stats.confirmationTax.measuredEntries`. Not citable while `totalUsd` is null.
|
|
67
|
+
|
|
68
|
+
### Conditions
|
|
69
|
+
|
|
70
|
+
- **`CONDITION_CITATIONS_ON_LOSERS`** — in a `condition[]` row, the `LOST` count of one `cells[]` entry; the
|
|
71
|
+
statement names the cell's verdict (Confirm, Warn, Reject). `sampleSize` is the row's `citations`. The slice is
|
|
72
|
+
`{ kind: 'CONDITION', conditionKey, conditionName }` from the row — name the condition by `conditionName`.
|
|
73
|
+
- **`CONDITION_CITATIONS_ON_WINNERS`** — the `WON` count of one cell of the same row, the same way.
|
|
74
|
+
|
|
75
|
+
### Costs
|
|
76
|
+
|
|
77
|
+
- **`COST_ERODED_TRADES`** — `economics.costErodedCount`: winners before costs that costs turned into losses.
|
|
78
|
+
`sampleSize` is `economics.totals.positionCount`.
|
|
79
|
+
- **`FEES_PERCENT_OF_GROSS`** — `economics.totals.feesPercentOfGross`, or one close outcome row's, in percent.
|
|
80
|
+
`sampleSize` is that row's `positionCount`. Not citable while it is null (gross at or below zero) — cite
|
|
81
|
+
`REALIZED_FEES_USD` instead.
|
|
82
|
+
- **`LLM_PLATFORM_SPEND_USD`** — `analytics.llm.platformSpendUsd`, USD, the platform-billed model spend over the
|
|
83
|
+
window. `sampleSize` is `analytics.llm.calls`. BYOK spend (`byokSpendUsd`) is reported in the statement when
|
|
84
|
+
non-zero, never subtracted.
|
|
85
|
+
- **`NET_AFTER_LLM_USD`** — `analytics.llm.netAfterLlmUsd`, USD, signed: realized net after platform model spend.
|
|
86
|
+
`sampleSize` is `analytics.llm.decisions`.
|
|
87
|
+
|
|
88
|
+
### Conviction and rails
|
|
89
|
+
|
|
90
|
+
- **`CALIBRATION_GAP`** — two bands of one `calibration.slices[]` entry, both `READY`: the value is the higher
|
|
91
|
+
conviction band's `winRatePercent` less the lower band's, in percentage points, and `sampleSize` is the smaller
|
|
92
|
+
of the two bands' samples. The statement names both bands and both served rates. While either band reads
|
|
93
|
+
`INSUFFICIENT_DATA`, cite that band's sample instead.
|
|
94
|
+
- **`RAIL_REFUSALS`** — one `analytics.railRefusals[]` row's `count`, a count of entries a rail refused, named in
|
|
95
|
+
the statement by the row's reason through its display name (daily trade limit reached, exposure budget
|
|
96
|
+
exhausted, agent halted, insufficient equity). `sampleSize` is the count itself. The current halt is
|
|
97
|
+
`analytics.halt`: name its `haltReason` through its display name beside the refusals.
|
|
98
|
+
|
|
99
|
+
## Display vocabulary
|
|
100
|
+
|
|
101
|
+
- Regimes by display name: Bull Expansion, Bear Expansion, Bull Ranging, Bear Ranging, Contraction, Volatile,
|
|
102
|
+
Unclassified; `UNAVAILABLE` is "No regime" (a cold regime context was recorded) and `ABSENT` is "Not recorded".
|
|
103
|
+
- Weekdays and hours in UTC, said as such ("Thursdays, UTC", "23:00 UTC").
|
|
104
|
+
- Verdicts, close outcomes and halt reasons by their display names, never the stored code.
|
|
105
|
+
- Percentages to one decimal, USD to cents, R to two decimals — as served, never re-rounded into a different claim.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Proposal shapes
|
|
2
|
+
|
|
3
|
+
A proposal is one lever's **one axis, whole**: `{ kind, targetId, axes, baseRevision }`, where `axes` holds exactly
|
|
4
|
+
one key — the axis — whose value is the complete axis in the kind's own draft vocabulary. "Whole" means every member
|
|
5
|
+
the axis carries, each at its current value except the one you change: an omitted member is not "unchanged", it is a
|
|
6
|
+
proposal the assessment refuses or a value the owner would lose. So every proposal starts from the target's current
|
|
7
|
+
values, read in the same review.
|
|
8
|
+
|
|
9
|
+
`targetId` and `baseRevision` come from `get_officer_review_context`'s `targets`: the strategy id, the agent id or the
|
|
10
|
+
Radar coin id, and that target's `committedRevision`. A proposal against a target the review context does not list
|
|
11
|
+
is outside your scope.
|
|
12
|
+
|
|
13
|
+
Every value below is a placeholder; replace it with what the reads served. Never send an id you did not read.
|
|
14
|
+
|
|
15
|
+
## Strategy — `kind: 'strategy'`, `targetId` the strategy id
|
|
16
|
+
|
|
17
|
+
Read the current values with `get_strategy`. One of five axes:
|
|
18
|
+
|
|
19
|
+
### `TRADE_LEVEL_POLICY`
|
|
20
|
+
|
|
21
|
+
All four members, every one a number: `minAtrPct` (the volatility floor, percent), `minStopLossAtrMultiple` and
|
|
22
|
+
`maxStopLossAtrMultiple` (the hard stop's band, in ATR), `minRiskRewardRatio` (the target's floor over the stop).
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"kind": "strategy",
|
|
27
|
+
"targetId": "<STRATEGY_ID>",
|
|
28
|
+
"baseRevision": 12,
|
|
29
|
+
"axes": {
|
|
30
|
+
"TRADE_LEVEL_POLICY": {
|
|
31
|
+
"minAtrPct": 0.6,
|
|
32
|
+
"minStopLossAtrMultiple": 1.2,
|
|
33
|
+
"maxStopLossAtrMultiple": 2.5,
|
|
34
|
+
"minRiskRewardRatio": 1.8
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `POSITION_MANAGEMENT`
|
|
41
|
+
|
|
42
|
+
The exit policy, all thirteen members: `breakEvenEnabled`, `breakEvenTriggerR`, `trailingEnabled`,
|
|
43
|
+
`trailingTriggerR`, `trailingGivebackPct`, `trailingBufferPct`, `timeDecayEnabled`, `timeDecayGracePeriodMinutes`,
|
|
44
|
+
`timeDecayIntervalMinutes`, `timeDecayTightenPct`, `timeDecayMaxTightenPct`, `timeDecayStaleThresholdTpProgressPct`,
|
|
45
|
+
`decisionInvalidationExitEnabled`. A mechanism switched off keeps its dial values: send them as read.
|
|
46
|
+
|
|
47
|
+
### `CONDITIONS`
|
|
48
|
+
|
|
49
|
+
`{ conditions: [...] }` — **every** condition of the strategy, in the order the strategy read serves them, each
|
|
50
|
+
`{ conditionKey, name, definition, verdict, required, exitSide }` exactly as read, with your one change: a condition
|
|
51
|
+
removed, or one existing condition's `required` or `verdict` changed. A dropped condition that another condition
|
|
52
|
+
references is refused by the assessment. Composing a new condition column is authoring work, outside a review:
|
|
53
|
+
record the finding and name the authoring flow as the owner's step.
|
|
54
|
+
|
|
55
|
+
### `ENTRY`
|
|
56
|
+
|
|
57
|
+
`{ entry: { trigger } }` for a trigger that takes no dials, or `{ entry: { trigger, levelOffsetAtrMultiple,
|
|
58
|
+
validForBars } }` for a level trigger — the trigger the strategy read serves, or the one you propose, with every dial
|
|
59
|
+
it honours and none it does not.
|
|
60
|
+
|
|
61
|
+
### `REPORT`
|
|
62
|
+
|
|
63
|
+
`{ sections: [...] }` — **every** section of the strategy's report, in the order `get_strategy` serves them, with
|
|
64
|
+
your one change: a platform section added or removed. Each section is a reference: a platform section is
|
|
65
|
+
`{ kind: 'platform', sectionKey }`, its key one `list_strategy_categories` serves; a custom section is
|
|
66
|
+
`{ kind: 'custom', sectionKey }`, its `custom:<uuid>` key as `get_strategy` serves it, and nothing else — the submit
|
|
67
|
+
takes its title, columns, benchmark and note from the committed report, so a review never edits one. A custom key the
|
|
68
|
+
report does not carry is refused. A report that drops a section a condition reads is refused by the assessment: keep
|
|
69
|
+
it, or propose the condition change first. Composing a new custom section is authoring work, outside a review.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"kind": "strategy",
|
|
74
|
+
"targetId": "<STRATEGY_ID>",
|
|
75
|
+
"baseRevision": 12,
|
|
76
|
+
"axes": {
|
|
77
|
+
"REPORT": {
|
|
78
|
+
"sections": [
|
|
79
|
+
{ "kind": "platform", "sectionKey": "includePriceAction" },
|
|
80
|
+
{ "kind": "custom", "sectionKey": "custom:<SECTION_UUID>" },
|
|
81
|
+
{ "kind": "platform", "sectionKey": "includeVolume" }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Agent — `kind: 'agent'`, `targetId` the agent id
|
|
89
|
+
|
|
90
|
+
Read the current values with `get_intelligence_agent`. One of four axes:
|
|
91
|
+
|
|
92
|
+
### `TRADING_CONFIG`
|
|
93
|
+
|
|
94
|
+
`{ tradingConfig: { ... } }` — the agent's complete trading configuration from `get_intelligence_agent`, less the two
|
|
95
|
+
members the server derives (`strategyTimeframe`, `regimeTimeframe`): `minAllocationUsd`, `maxDailyTrades`,
|
|
96
|
+
`balanceThresholdUsd`, `maxLeverage`, `maxSlippageBps`, `maxConcurrentExposureUsd`, `maxCumulativeDrawdownUsd`,
|
|
97
|
+
`maxDailyLossUsd` and `positionSizePresets` (the whole preset ladder), each inside the bounds
|
|
98
|
+
`get_trading_config_catalog` serves. Change the one rail the finding names; send the rest as read. A rail refusal
|
|
99
|
+
names its rail as `tradingConfigMember` on the review read.
|
|
100
|
+
|
|
101
|
+
### `BEHAVIOR`
|
|
102
|
+
|
|
103
|
+
`{ behavior: { risk, outlook, conviction } }` — all three members, the ones you do not change as read: `risk` one of
|
|
104
|
+
`CONSERVATIVE`, `MODERATE`, `AGGRESSIVE`; `outlook` one of `OPTIMIST`, `REALIST`, `PESSIMIST`; `conviction` one of
|
|
105
|
+
`CAUTIOUS`, `MEASURED`, `BOLD`.
|
|
106
|
+
|
|
107
|
+
### `MODEL`
|
|
108
|
+
|
|
109
|
+
`{ modelId }` — a `modelId` `list_approved_models` serves now. The model sets what each decision costs: cite the
|
|
110
|
+
agent's LLM spend against its net as the finding.
|
|
111
|
+
|
|
112
|
+
### `STRATEGY_BINDING`
|
|
113
|
+
|
|
114
|
+
`{ strategyId }` — an active strategy `list_strategies` serves: one the owner authored, or a library strategy. Never
|
|
115
|
+
another user's, never an archived one. A rebind carries nothing else: the strategy and regime timeframes follow the
|
|
116
|
+
new strategy, and each open position keeps the rules it opened under.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"kind": "agent",
|
|
121
|
+
"targetId": "<AGENT_ID>",
|
|
122
|
+
"baseRevision": 14,
|
|
123
|
+
"axes": { "STRATEGY_BINDING": { "strategyId": "<STRATEGY_ID>" } }
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Radar deployment — `kind: 'radar-deployment'`, `targetId` the coin id
|
|
128
|
+
|
|
129
|
+
### `RULES`
|
|
130
|
+
|
|
131
|
+
`{ rules: [...] }` — **every** rule slot of the coin's policy from `get_radar_deployment`, in priority order (the
|
|
132
|
+
first is priority 1), the catch-all default excluded. Each rule is `{ agentId, minConviction, conditions }` as read,
|
|
133
|
+
with the one rule that routes this agent changed. A rule's conditions are a non-empty list of:
|
|
134
|
+
|
|
135
|
+
- `{ kind: 'regime', regimes: [...] }`, with an optional `minConviction` — the regimes the rule is on duty in, by
|
|
136
|
+
stored value (`bull_expansion`, `bear_expansion`, `bull_ranging`, `bear_ranging`, `contraction`, `volatile`);
|
|
137
|
+
excluding a losing regime is this list without it;
|
|
138
|
+
- `{ kind: 'hours', hours: [...], days: [...] }` — UTC hours 0–23 and UTC weekdays 0 (Sunday) – 6, both non-empty.
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"kind": "radar-deployment",
|
|
143
|
+
"targetId": "<COIN_ID>",
|
|
144
|
+
"baseRevision": 4,
|
|
145
|
+
"axes": {
|
|
146
|
+
"RULES": {
|
|
147
|
+
"rules": [
|
|
148
|
+
{
|
|
149
|
+
"agentId": "<AGENT_ID>",
|
|
150
|
+
"minConviction": null,
|
|
151
|
+
"conditions": [
|
|
152
|
+
{ "kind": "regime", "regimes": ["bull_expansion", "bull_ranging", "contraction"] }
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"agentId": "<OTHER_AGENT_ID>",
|
|
157
|
+
"minConviction": 0.6,
|
|
158
|
+
"conditions": [
|
|
159
|
+
{ "kind": "hours", "hours": [13, 14, 15, 16], "days": [1, 2, 3, 4, 5] }
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Every agent a rule names is an agent the coin already routes: placing an agent on a coin is the owner's step, and the
|
|
169
|
+
submit refuses a proposal naming any other.
|
|
170
|
+
|
|
171
|
+
### `DEFAULT_SLOT`
|
|
172
|
+
|
|
173
|
+
`{ defaultSlot: { agentId, minConviction } }` — the catch-all that takes the coin when no rule is on duty, its agent one
|
|
174
|
+
the coin already routes; or `{ defaultSlot: null }` to remove the catch-all. Read it with `get_radar_deployment`.
|