@battlegrid/mcp-server 32.0.16 → 32.0.18
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 +29 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +4 -6
- package/skills/battlegrid-agent-management/SKILL.md +4 -33
- package/skills/battlegrid-officers/SKILL.md +101 -0
- package/skills/{battlegrid-agent-management → battlegrid-officers}/references/officer-stage-examples.md +4 -16
- package/skills/battlegrid-strategy-officer/SKILL.md +0 -226
- package/skills/battlegrid-strategy-officer/references/figures.md +0 -105
- package/skills/battlegrid-strategy-officer/references/proposal-shapes.md +0 -174
package/README.md
CHANGED
|
@@ -59,6 +59,35 @@ Reconnect the proxy, or restart it, so discovery reads the new catalog.
|
|
|
59
59
|
staged from.
|
|
60
60
|
- The **Officers** workflow routes from `list_officers` to the `battlegrid-officers` skill.
|
|
61
61
|
|
|
62
|
+
## Contract history — v123 (a metric's code states its indicator's length)
|
|
63
|
+
|
|
64
|
+
**Breaking at v123: eighteen report header stems are renamed.** v123 is the contract on top of v122. Reconnect the
|
|
65
|
+
proxy, or restart it, so discovery reads the new schemas.
|
|
66
|
+
|
|
67
|
+
### Rejected input — something you send is no longer accepted
|
|
68
|
+
|
|
69
|
+
- **A condition column or a `{…}` Market Read marker naming a retired header is refused.** A metric's code now ends
|
|
70
|
+
with its indicator's single length: `CCI` → `CCI20`, `ADX` → `ADX14`, `ATR` → `ATR14`, `BBwidth` → `BBwidth20`,
|
|
71
|
+
`pctB` → `pctB20`, `donchianHi` / `donchianLo` → `donchianHi20` / `donchianLo20`, `donchianHiPrior` /
|
|
72
|
+
`donchianLoPrior` → `donchianHiPrior20` / `donchianLoPrior20`, `donchianBrkHi` / `donchianBrkLo` →
|
|
73
|
+
`donchianBrkHi20` / `donchianBrkLo20`, `bbUpper` / `bbLower` → `bbUpper20` / `bbLower20`, `willR` → `willR14`,
|
|
74
|
+
`stochRSI` → `stochRSI14`, `DIplus` / `DIminus` → `DIplus14` / `DIminus14`, `RVOL` → `RVOL20`. Every suffix and
|
|
75
|
+
prefix is kept (`ADX14_state`, `dist_donchianLo20`, `RVOL20_rank_hi`). Metric **keys** do not move (`ADX`, `RVOL`,
|
|
76
|
+
`DONCHIAN_UPPER`), so a column you author by key is unchanged. Stored strategies, agents, drafts and open
|
|
77
|
+
positions are rewritten on the server.
|
|
78
|
+
|
|
79
|
+
### Reshaped output — something you read moved
|
|
80
|
+
|
|
81
|
+
- **Rendered headers, condition columns, glossary entries and catalog `code` values serve the new stems.** A client
|
|
82
|
+
that matches a header by its old text no longer finds it.
|
|
83
|
+
- **`list_strategy_vocabulary`'s `budgets` gains required `marketReadChars`**, the Market Read bound. A strategy's
|
|
84
|
+
`marketReadText` now runs to 4,000 characters, where 2,000 was the bound.
|
|
85
|
+
|
|
86
|
+
### Widened input
|
|
87
|
+
|
|
88
|
+
- **Seven metric keys are added:** `CLOSE_LOCATION`, `PRIOR_SWING_FRACTAL_HIGH`, `PRIOR_SWING_FRACTAL_LOW`,
|
|
89
|
+
`SWING_FRACTAL_HIGH_BARS_AGO`, `SWING_FRACTAL_LOW_BARS_AGO`, `CMF20` and `EMA8`.
|
|
90
|
+
|
|
62
91
|
## Contract history — v120 (the readiness ladder is three milestones)
|
|
63
92
|
|
|
64
93
|
**Breaking at v120: `get_onboarding_requirements` reshapes its rungs and re-specifies its counts.** v120 is the
|
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.18";
|
|
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.18';
|
|
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,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "122.0.0",
|
|
4
4
|
"files": {
|
|
5
|
-
"battlegrid-agent-management/SKILL.md": "
|
|
5
|
+
"battlegrid-agent-management/SKILL.md": "4c2cb5cfec27cf11769d66763e024e6900c392e27a978de3080cf55b2ece7a68",
|
|
6
6
|
"battlegrid-agent-management/references/agent-stage-examples.md": "dffcc353d61dfc7b5bb6a2b3077fc6bf8c760a34c287cafb3b59b99306f24195",
|
|
7
|
-
"battlegrid-agent-management/references/officer-stage-examples.md": "a5a42792a8aa8f4af8cbf424d7c9aadbaa23df382ce17691a40a8d0e5447db63",
|
|
8
7
|
"battlegrid-arena-play/SKILL.md": "2e37970a66b8738ffbc445a23da90651c1f41b56ce4eef551582ab123517ef74",
|
|
9
8
|
"battlegrid-arena-play/references/entry-stage-examples.md": "0223f3743171ff213e943789d27590b2f65648398e85e10cd24938d5eb6af665",
|
|
10
9
|
"battlegrid-market-analysis/SKILL.md": "fcbe98e30e1edce39bb170c224cdf20181934f62b5f3a26b293787c70eeb6229",
|
|
10
|
+
"battlegrid-officers/SKILL.md": "3fd59ec301d1492c956fbdb4368fef61f29abd137f02f49a6172351c7a563309",
|
|
11
|
+
"battlegrid-officers/references/officer-stage-examples.md": "ef46ba26f4b36c9964b5bde95fdf98e5952c3786b77be5f136056cac792f0aec",
|
|
11
12
|
"battlegrid-radar-deployment/SKILL.md": "cf6664f51f92d40acce21a46f4fb078d9afe46c99c4f4b603bf002e73d4c5a23",
|
|
12
13
|
"battlegrid-radar-deployment/references/arena-stage-examples.md": "d27cfed1f4663f267c4a7ecebe4a6e65a450fc74bd14ce8987fb61af5bbf2ca5",
|
|
13
14
|
"battlegrid-radar-deployment/references/radar-stage-examples.md": "13fc3f55d91d3da9754e1ed7fb27493b7b7ba4d40a6a7e8d1ee0365f90ea10de",
|
|
@@ -18,9 +19,6 @@
|
|
|
18
19
|
"battlegrid-strategy-examples/references/playbooks.md": "275352be43652ca0fd36a35981ea7ffcfabdc4f9b33293826656ee500d3795e9",
|
|
19
20
|
"battlegrid-strategy-examples/references/recipes.md": "806aae0b4842408f6e6ed3e108293073f4f31d1c27fa419ad57df9e1134f23e7",
|
|
20
21
|
"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",
|
|
24
22
|
"battlegrid-trade-analysis/SKILL.md": "3f927b4f9796178526fa1673a2043c5f14358c39aa955de33192ea58641f18fb",
|
|
25
23
|
"battlegrid-trade-proposal/SKILL.md": "e761fc6bddc0d1db79fcb90046d03a18439562c8658de726ec286830edd6369b"
|
|
26
24
|
}
|
|
@@ -266,39 +266,10 @@ 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
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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.
|
|
269
|
+
### 8. Strategy Officers are the officers skill's
|
|
270
|
+
|
|
271
|
+
Setting up, running and reviewing a Strategy Officer, and acting on its proposals, is the `officers` skill's flow:
|
|
272
|
+
activate it for any officer ask.
|
|
302
273
|
|
|
303
274
|
## When a write's outcome is unknown
|
|
304
275
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: battlegrid-officers
|
|
3
|
+
description: Set up and govern the player's Strategy Officers and work their reviews — create or change an officer through its draft, run one now, read when each runs and what is waiting, and apply, push or dismiss the proposals its reviews carry. It never writes a review. Activate whenever the player wants a new Strategy Officer, wants to change, pause or archive one, wants one to run now, asks what their officers found or when they run, or wants to apply, push or dismiss an officer's proposal.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Strategy Officers
|
|
7
|
+
|
|
8
|
+
A Strategy Officer is a review the player schedules: on its hour, or when the player runs it now, it reads each
|
|
9
|
+
in-scope agent's record and records at most one proposal per agent for the player to apply, push or dismiss. The
|
|
10
|
+
officer writes its reviews on its own turn, on its own model and brief. This flow is the player's side of it:
|
|
11
|
+
setting an officer up, running it, reading where it stands, and acting on what it proposed.
|
|
12
|
+
|
|
13
|
+
Every officer run is billed to the player's LLM credit, and an applied proposal changes a live strategy, agent or
|
|
14
|
+
Radar deployment. So a setup goes through its own draft, a run is started only on the player's word, and Apply is
|
|
15
|
+
confirmed before it is called.
|
|
16
|
+
|
|
17
|
+
## The four failures this flow exists to prevent
|
|
18
|
+
|
|
19
|
+
1. **Writing a review yourself.** *Cue: "what would my officer say about…", wanting to record a finding.* You read
|
|
20
|
+
an officer's reviews and give your own reading in the conversation; only the officer records a review. → §2.
|
|
21
|
+
2. **Applying without confirmation.** *Cue: "apply it", "make it live".* Apply publishes the proposal to a live
|
|
22
|
+
target. Show the review, then confirm through `ask_user`. → §4.
|
|
23
|
+
3. **Computing what is served.** *Cue: working out the next run from the schedule, counting open reviews.* Every
|
|
24
|
+
status figure is on the read. → §3.
|
|
25
|
+
4. **Committing a setup unasked.** *Cue: a staged officer that looks right.* The commit is the player's word. → §1.
|
|
26
|
+
|
|
27
|
+
## 1. Set up and update an officer through its draft
|
|
28
|
+
|
|
29
|
+
- **Read first.** `list_officers` for the roster (each officer's schedule, scope, last and next run) and the
|
|
30
|
+
`presets`, `list_officer_drafts` for setups part-way through, `get_officer_draft` for one draft.
|
|
31
|
+
- **Create.** `stage_officer_draft` with **no** `officerId` and `draftVersion: 0` mints a new create draft; every
|
|
32
|
+
later call names the minted id. Stage only what the player asked for — every axis the draft does not hold starts
|
|
33
|
+
at its default (name "Strategy Officer", the catalog's default model, no instructions, `DAILY` at 03:00 UTC, all
|
|
34
|
+
active agents, all three levers). "Add an officer that reviews my agents every morning at six and focuses on
|
|
35
|
+
stops" is `NAME`, `SCHEDULE` `{ cadence: 'DAILY', hourUtc: 6, weekdayUtc: null }` and `INSTRUCTIONS`
|
|
36
|
+
`{ focusText }` in the player's words — and ask which timezone "six" means before converting it to a UTC hour.
|
|
37
|
+
Worked inputs: `references/officer-stage-examples.md`.
|
|
38
|
+
- **Presets are starting points, staged as ordinary axes.** When the player names one of the `presets`
|
|
39
|
+
`list_officers` serves, stage its `name`, `focusText` and `scope` as `NAME`, `INSTRUCTIONS` and `SCOPE`; the
|
|
40
|
+
officer remembers no preset.
|
|
41
|
+
- **Axes are whole.** `SCHEDULE` carries `cadence`, `hourUtc` and `weekdayUtc` (`0` Sunday … `6` Saturday, for
|
|
42
|
+
`WEEKLY` only, otherwise null); `SCOPE` carries both `agents` (`'ALL'` or agent ids from
|
|
43
|
+
`list_intelligence_agents`) and `levers`; `MODEL` names a `modelId` from `list_approved_models`. `INSTRUCTIONS`
|
|
44
|
+
are the player's brief in their own words: at most 2,000 characters, naming no tool and no id — the commit
|
|
45
|
+
refuses either.
|
|
46
|
+
- **Show, then commit on the player's word.** Read `get_officer_draft` and show the diff, the diagnostics and the
|
|
47
|
+
impact — `nextRunAt` is when it first runs, and a create's `admission` says how many officers the player holds
|
|
48
|
+
against the limit and how many more it admits (`officersRemaining`). Commit with `commit_officer_draft` naming
|
|
49
|
+
exactly the `draftVersion` and `committedRevision` the read returned, and only when the player says so. A cadence
|
|
50
|
+
of `OFF` keeps the setup and runs nothing on a schedule.
|
|
51
|
+
- **Archive** with `archive_officer` only on the player's word: an archived officer runs no more, and its open
|
|
52
|
+
reviews stay in the inbox.
|
|
53
|
+
- In the app, the player sets an officer up in its own console, where this conversation may be docked beside the
|
|
54
|
+
form — stage into the officer it is scoped to.
|
|
55
|
+
|
|
56
|
+
## 2. Run an officer now
|
|
57
|
+
|
|
58
|
+
`run_officer` with the officer id, on the player's word. It runs exactly what the schedule runs — the officer's
|
|
59
|
+
own model and brief, over the agents its scope covers that closed a decision or saw the officer's setup change
|
|
60
|
+
since its last review — and answers before any review starts:
|
|
61
|
+
|
|
62
|
+
- `QUEUED` with `agentCount`: the reviews arrive in `list_officer_reviews` as each finishes;
|
|
63
|
+
- `NOTHING_TO_REVIEW`: no agent is under review, and nothing is queued or billed;
|
|
64
|
+
- `ALREADY_QUEUED`: a run of this officer is waiting or running.
|
|
65
|
+
|
|
66
|
+
A run is per officer: to run two, call it for each. While one is waiting or running, `pendingRun` on `list_officers`
|
|
67
|
+
and `get_officer` names its state; read it again rather than calling `run_officer` twice. It follows
|
|
68
|
+
`commanderAccess` from `get_account_state`, not `llmAccess`: show `deniedMessage` when it is not allowed. An
|
|
69
|
+
archived officer is refused.
|
|
70
|
+
|
|
71
|
+
**Never write a review.** The officer records its own; a second opinion on one of its reviews is your reading in
|
|
72
|
+
the conversation, never a record.
|
|
73
|
+
|
|
74
|
+
## 3. Where each officer stands
|
|
75
|
+
|
|
76
|
+
Read it, never compute it. `list_officers` serves each officer's `lastRunAt`, `nextRunAt` and `pendingRun`, and
|
|
77
|
+
`list_officer_reviews` serves `openCountByOfficer`, each unarchived officer's open and pushed reviews. Quote them as
|
|
78
|
+
served, through their display names.
|
|
79
|
+
|
|
80
|
+
## 4. Work the inbox
|
|
81
|
+
|
|
82
|
+
- **Read.** `list_officer_reviews`, by officer or by agent, newest first; `includeClosed` adds the applied,
|
|
83
|
+
dismissed and superseded history. Show each review's verdict, findings, diff, impact and warnings exactly as
|
|
84
|
+
served; `applyAvailable` says Apply is offered, `heldAxes` names every axis the target's draft holds and who wrote
|
|
85
|
+
it, and `baseMoved` says the target was committed since the review.
|
|
86
|
+
- **Apply** with `apply_officer_proposal` only after an `ask_user` confirmation naming the review, its target and
|
|
87
|
+
its axis, and only on the confirming pick. A client with no `ask_user` asks in its reply and waits for the
|
|
88
|
+
player's word. Apply publishes the proposal to the live target; it needs `mcp:wager`.
|
|
89
|
+
- **Push** with `push_officer_proposal` and **Dismiss** with `dismiss_officer_review` on the player's explicit word,
|
|
90
|
+
naming the review. A push places the proposal in the target's draft for the player to save from the editor; a
|
|
91
|
+
dismissal closes the review and touches no draft.
|
|
92
|
+
- **A refusal names its recovery** through `details.nextAct`: `BASE_MOVED` — the target moved since the review,
|
|
93
|
+
dismiss it for a re-review; `AXIS_HELD` and `DRAFT_NOT_EMPTY` — the target's draft already holds work, so the
|
|
94
|
+
player saves or discards it, or pushes instead of applying; `TARGET_NOT_OWNED` and `PROPOSAL_INVALID` — the target
|
|
95
|
+
no longer accepts the proposal, so dismiss it. A dismissed review is not run again until the agent closes a new
|
|
96
|
+
decision or the officer's setup changes.
|
|
97
|
+
|
|
98
|
+
## Reporting discipline
|
|
99
|
+
|
|
100
|
+
- Numbers, codes and states exactly as served, through their display names.
|
|
101
|
+
- Never describe a proposal as applied before `apply_officer_proposal` answered, or a review as written by you.
|
|
@@ -29,23 +29,11 @@ the player's "six" to a UTC hour only after asking which timezone they mean; thi
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
## Start from
|
|
32
|
+
## Start from a preset
|
|
33
33
|
|
|
34
|
-
A preset is staged as ordinary axes: its name
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
```
|
|
34
|
+
A preset is a starting point, staged as ordinary axes: take its `name`, `focusText` and `scope` from the `presets`
|
|
35
|
+
`list_officers` serves, and stage them as `NAME`, `INSTRUCTIONS` and `SCOPE`, beside any `SCHEDULE` the player asks for.
|
|
36
|
+
The officer remembers no preset.
|
|
49
37
|
|
|
50
38
|
## Narrow an existing officer to two agents and the rails lever
|
|
51
39
|
|
|
@@ -1,226 +0,0 @@
|
|
|
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.
|
|
@@ -1,105 +0,0 @@
|
|
|
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.
|
|
@@ -1,174 +0,0 @@
|
|
|
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`.
|