@battlegrid/mcp-server 31.2.24 → 31.2.26
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 +98 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/EXPORT.json +4 -4
- package/skills/battlegrid-strategy-authoring/SKILL.md +7 -5
- package/skills/battlegrid-strategy-examples/SKILL.md +32 -35
- package/skills/battlegrid-trade-proposal/SKILL.md +38 -24
package/README.md
CHANGED
|
@@ -24,6 +24,104 @@ Seeing package `31.x` alongside handshake `battlegrid@33.x` — the package **be
|
|
|
24
24
|
|
|
25
25
|
**What this changes for you:** nothing about how you call anything. Upgrading the package no longer waits on a server deploy, and a server deploy no longer strands you on a package that names the wrong contract — reconnect and the announcement follows. **Contract breaking-change notes are no longer keyed to package versions**, since a contract move is no longer a release here; the v11-and-earlier notes below are kept as history, and the live vocabulary is always discovery.
|
|
26
26
|
|
|
27
|
+
## Contract history — v60
|
|
28
|
+
|
|
29
|
+
One release train, four numbers, and two parts to read first: **`propose_entry_decision` no longer
|
|
30
|
+
decides in the call**, and **a closed trade's `tradeStatus` now follows its net P&L**. v55 through v59 are not written up here; the canonical record for
|
|
31
|
+
every contract move is `docs/architecture/MCP_CONTRACT_HISTORY.md` in `battlegrid-app`, and the
|
|
32
|
+
served version is what the handshake announces.
|
|
33
|
+
|
|
34
|
+
### Rejected input — something you author is no longer accepted
|
|
35
|
+
|
|
36
|
+
- **The entry axis names no bar but the strategy's own, and three of its keys are gone** (60.0.0,
|
|
37
|
+
`decide-entry-on-strategy-close`). The authoring schemas are `.strict()`, so `compile_strategy_plan`,
|
|
38
|
+
`apply_strategy_plan`, `fork_strategy` and `restore_strategy` REFUSE a body carrying
|
|
39
|
+
`entry.confirmTf` (the deciding bar is the strategy's own timeframe, so a required input whose only
|
|
40
|
+
legal value was another field of the same strategy is absent rather than mirrored), `entry.closes`
|
|
41
|
+
and `entry.bandAtrMultiple` (a multi-bar hold is declared on the condition that needs it; the
|
|
42
|
+
displacement band is replaced by the platform's own entry-deviation gate, measured against the
|
|
43
|
+
decided close), and the whole `exit` object (the open-position exit lane judges one closed candle
|
|
44
|
+
of the position's strategy timeframe). Exactly one input hash moves, `compile_strategy_plan`'s.
|
|
45
|
+
|
|
46
|
+
- **`entry.trigger: AT_SIGNAL` is retired for authoring** (60.0.0, `retire-at-signal-trigger`, riding
|
|
47
|
+
the same number). Refused on every authoring surface and on a RESTORE, which rebuilds a stored
|
|
48
|
+
revision through the same value object. The enum member stays READABLE on every strategy read and
|
|
49
|
+
on a fired decision's provenance, so a pre-retirement revision is still legible — it just cannot be
|
|
50
|
+
re-authored. The three that remain are `ON_CANDLE_CLOSE`, `STOP_THROUGH_LEVEL` and `ON_RETEST`.
|
|
51
|
+
|
|
52
|
+
### Changed meaning, unchanged shape
|
|
53
|
+
|
|
54
|
+
- **`tradeStatus` follows NET P&L on every closed trade** (60.3.0, `label-trade-outcome-by-pnl`).
|
|
55
|
+
No schema hash moves for this and the values you receive change anyway. It used to map the close
|
|
56
|
+
REASON onto a verdict — every `TAKE_PROFIT` was `WON`, every `STOP_LOSS` was `LOST`, and a
|
|
57
|
+
`MARKET_CLOSE` of either sign was the neutral `CLOSED` — so a stop that filled after a break-even
|
|
58
|
+
reprice was reported as a loss and a take-profit eaten by fees as a win. `LIQUIDATED` still
|
|
59
|
+
outranks the number, because a force-close is not a verdict about the trade; `CLOSED` now means
|
|
60
|
+
only that there is no outcome row to judge. A client that counted `WON` rows was counting
|
|
61
|
+
take-profits.
|
|
62
|
+
|
|
63
|
+
- **A proposal is QUEUED, not decided** (60.2.0, `queue-manual-entry-for-close`). This is the entry in
|
|
64
|
+
this section to act on. `propose_entry_decision` registers a request against the agent's next
|
|
65
|
+
strategy-bar close and returns immediately: no model runs, nothing is spent, and the call carries
|
|
66
|
+
`type: "queued"` with `request` — `requestId`, the bar (`barStart`), when the answer is due
|
|
67
|
+
(`decidesBy`) and the last instant that bar may still be decided (`windowEndsAt`). The answer
|
|
68
|
+
arrives later, in the agent's conversation and, when it proposes a trade, in
|
|
69
|
+
`list_pending_approvals`. A close that does not qualify, a window that passes with no sweep, and a
|
|
70
|
+
bar the agent's own radar deployment decided first are each recorded in the conversation instead.
|
|
71
|
+
|
|
72
|
+
`recommendation` and `no_trade` REMAIN in the union: the idempotency registrar replays results
|
|
73
|
+
recorded before this release for their TTL, so a client that dropped those members would fail on
|
|
74
|
+
its own retry. **A client written against 60.2 handles `queued` and `error`**; one that must also
|
|
75
|
+
replay handles all four.
|
|
76
|
+
|
|
77
|
+
### Reshaped output — the same call returns a different shape
|
|
78
|
+
|
|
79
|
+
- **The four retired entry keys leave every strategy read** (60.0.0) — `get_strategy`,
|
|
80
|
+
`list_strategies`, `fork_strategy` and both plan envelopes — and `confirmTimeframesByMainCandle`
|
|
81
|
+
leaves `list_strategy_vocabulary`, because there is no confirm set left to publish.
|
|
82
|
+
|
|
83
|
+
- **`entryDiscipline.closes` and `.bandAtrMultiple` go `number` → `number | null`** (60.0.0) on
|
|
84
|
+
`get_trade_outcome_by_decision` and `list_trade_outcomes`. Null on every decision fired after this
|
|
85
|
+
release, which authors neither; a non-null pair dates the row to the arming era.
|
|
86
|
+
|
|
87
|
+
### Widened enum — new members your own copy rejects
|
|
88
|
+
|
|
89
|
+
- **`TradeConvErrorCode` gains `REQUEST_PENDING`** (60.2.0), on the SURFACE arm of
|
|
90
|
+
`propose_entry_decision`'s error: one pending request per user and coin, so a second is refused. A
|
|
91
|
+
coin already carrying a pending or live position is refused before anything is queued, as an
|
|
92
|
+
ENGINE-origin `OPEN_POSITION_CONFLICT` — a member that was already published, reaching this surface
|
|
93
|
+
for the first time.
|
|
94
|
+
|
|
95
|
+
### Additive in the same span
|
|
96
|
+
|
|
97
|
+
- **The exit names the leg that filled** (60.3.0, `label-trade-outcome-by-pnl`). `TradeOutcomeDTO`
|
|
98
|
+
and the pipeline outcome summary gain `exitRepriceSource`: the reprice that placed the protection
|
|
99
|
+
leg which actually closed the position — `BREAK_EVEN`, `TRAILING`, `TIME_DECAY`,
|
|
100
|
+
`MANUAL_OVERRIDE`, `UPDATED` — or null on every close no protection leg filled and on a leg that
|
|
101
|
+
was never repriced. It is the mechanism behind the verdict above, so read it beside `closeReason`
|
|
102
|
+
before calling a stopped-out trade a failure. The enum is the one `get_position_audit_history`
|
|
103
|
+
already publishes. Four output hashes move; no input schema does.
|
|
104
|
+
|
|
105
|
+
- **Two tools join the catalog for the request lifecycle** (60.2.0, `queue-manual-entry-for-close`).
|
|
106
|
+
`get_entry_request` (read scope) reads a request that is still pending and is `NOT_FOUND` once it
|
|
107
|
+
has been answered, cancelled or expired — the answer is in the conversation, not there.
|
|
108
|
+
`cancel_entry_request` (`mcp:wager`) withdraws one before its bar is decided. `toolCount` 115 → 117.
|
|
109
|
+
|
|
110
|
+
- **Three radar reads publish the close decision** (60.1.0, `add-radar-close-decision-state`).
|
|
111
|
+
`get_radar_deployment`, `list_radar_deployments` and `preview_radar_resolution` carry one further
|
|
112
|
+
key on `resolvesNow`: `closeDecision`, non-null whenever an agent is on duty for the pair. It
|
|
113
|
+
carries the deciding `timeframe`, the `nextCloseAt` instant, a `state` of `WAITING` or `DEFERRED`
|
|
114
|
+
(a bar has closed and no sweep has decided it yet), and `last` — the bar, the instant, the outcome
|
|
115
|
+
(`FIRED` / `NOT_QUALIFIED` / `MISSED`), the gate and the closed reading's score against its
|
|
116
|
+
minimum — or null before the pair's first close decision.
|
|
117
|
+
|
|
118
|
+
**Read it first on a close-deciding pair.** The sibling `qualified` and `qualificationBlock` fields
|
|
119
|
+
are the per-minute DISPLAY reading there and decide nothing, so an agent ranking them reports
|
|
120
|
+
"qualified — watching for a setup" about a pair whose last three closes were each refused. Neither
|
|
121
|
+
field is removed or reshaped. A `NOT_QUALIFIED` outcome with a NULL gate beside a score at or above
|
|
122
|
+
the minimum is the consumed edge — the close qualified and the baseline was already spent — stated
|
|
123
|
+
by the server so no client compares the two numbers.
|
|
124
|
+
|
|
27
125
|
## Contract history — v37 → v54
|
|
28
126
|
|
|
29
127
|
Eleven majors reached authors while this section stopped at v36. That gap is the mechanism, not an
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ import { type Implementation, type Prompt, type Resource } from '@modelcontextpr
|
|
|
49
49
|
* being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
|
|
50
50
|
* correction. Never move it to track the server.
|
|
51
51
|
*/
|
|
52
|
-
export declare const PACKAGE_VERSION = "31.2.
|
|
52
|
+
export declare const PACKAGE_VERSION = "31.2.26";
|
|
53
53
|
export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
|
|
54
54
|
export interface EnvConfig {
|
|
55
55
|
apiKeys: string[];
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema
|
|
|
52
52
|
* being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
|
|
53
53
|
* correction. Never move it to track the server.
|
|
54
54
|
*/
|
|
55
|
-
export const PACKAGE_VERSION = '31.2.
|
|
55
|
+
export const PACKAGE_VERSION = '31.2.26';
|
|
56
56
|
export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
|
|
57
57
|
const MAX_RETRIES = 3;
|
|
58
58
|
const RETRY_DELAYS_MS = [2000, 4000, 8000];
|
package/package.json
CHANGED
package/skills/EXPORT.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
|
|
3
|
-
"contractVersion": "
|
|
3
|
+
"contractVersion": "60.3.0",
|
|
4
4
|
"files": {
|
|
5
5
|
"battlegrid-agent-management/SKILL.md": "b6ea75b3d838c1dbaaf3984d7e99a11c81686de2c1a1910c35699ee7e70dafa5",
|
|
6
6
|
"battlegrid-arena-play/SKILL.md": "03fa153bf82be18bf5ed01e3ba12e2cd9f99a81b08d903a77b52169e7519a182",
|
|
7
7
|
"battlegrid-market-analysis/SKILL.md": "22fde9c4eac0261c89a8056ab8b20b66fcbe1fd58447fa0ac84fd410b4d134d7",
|
|
8
8
|
"battlegrid-radar-deployment/SKILL.md": "d2a012b154e1e529db19529bd7c3d75d069f4e410d5804add51165f0f0902b2e",
|
|
9
|
-
"battlegrid-strategy-authoring/SKILL.md": "
|
|
9
|
+
"battlegrid-strategy-authoring/SKILL.md": "ea2593ea9d6d2feb9910e285e4b132eb36001164b9d6551cc206531afad3f552",
|
|
10
10
|
"battlegrid-strategy-doctor/SKILL.md": "9ab3f79128ec81d3e90167c3f1ea4bd6f6b570290e5ebe2bf855195263c58239",
|
|
11
|
-
"battlegrid-strategy-examples/SKILL.md": "
|
|
11
|
+
"battlegrid-strategy-examples/SKILL.md": "263c3b865ea85e56ac420611a7070360d65be6f2faee071329880e49a6101eda",
|
|
12
12
|
"battlegrid-strategy-examples/references/playbooks.md": "35c543c090d00ba19e4bcf0a80502afac7a4954643fc535de0b24381c91ecbf4",
|
|
13
13
|
"battlegrid-strategy-examples/references/recipes.md": "53887daece17995543e7d3fe1bf2577fc41a6254c291acacde48d7de63853641",
|
|
14
14
|
"battlegrid-strategy-examples/references/tradingview-ports.md": "2b13adbc137178955edcca31b6ab40c01c745a932a0750b91db8820c736700bb",
|
|
15
15
|
"battlegrid-trade-analysis/SKILL.md": "32573a67e55d735e6ee4e36b3b48c815694102704bc453c4d1c13db39551b3da",
|
|
16
|
-
"battlegrid-trade-proposal/SKILL.md": "
|
|
16
|
+
"battlegrid-trade-proposal/SKILL.md": "503ed705d4f9823e21846afe5a10f35d7d6e35933c4bdd1d5b4fd923eb9d674d"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -100,11 +100,13 @@ question you would otherwise guess — and a refusal you would otherwise earn:
|
|
|
100
100
|
yourself on a CREATE is refused with a hint telling you to omit `sectionKey` on CREATE; on an
|
|
101
101
|
UPDATE, send back the key the compile returned. This is the one composition field whose right
|
|
102
102
|
answer is "leave it out".
|
|
103
|
-
- **The `entry` axis is required on every CREATE**, all
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
the trade's direction, never named.
|
|
107
|
-
|
|
103
|
+
- **The `entry` axis is required on every CREATE**, all three keys, no defaults — `trigger`,
|
|
104
|
+
`levelOffsetAtrMultiple`, `validForBars`. Every trigger is decided at the close of the strategy's
|
|
105
|
+
OWN bar, so there is no confirm-timeframe key; for the level triggers the level itself is derived
|
|
106
|
+
from the trigger and the trade's direction, never named. A multi-bar hold belongs to the condition
|
|
107
|
+
that needs it (`clock: CLOSE` with its own `closes`), not to this axis. The `strategy-examples`
|
|
108
|
+
skill carries the vocabulary and the one-directional legality matrix; a CREATE without it is
|
|
109
|
+
refused outright.
|
|
108
110
|
- `get_strategy_column_contract` → `outputs[].conditionOperators`. An empty array means that
|
|
109
111
|
rendered header has no comparison semantics and cannot appear in a condition clause at all.
|
|
110
112
|
Legality is per rendered header, not per column: a trajectory's slot header and its `_trend`
|
|
@@ -156,36 +156,34 @@ anchor, or the author gates on something that admits most bars.
|
|
|
156
156
|
|
|
157
157
|
## Entry
|
|
158
158
|
|
|
159
|
-
`{ trigger,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
- `
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
`closes`
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
that voids on any adverse move — and at or below the platform's own entry-deviation gate, since
|
|
188
|
-
a wider band cannot refuse anything the platform will not refuse anyway.
|
|
159
|
+
`{ trigger, levelOffsetAtrMultiple, validForBars }` — all three required on every CREATE, no
|
|
160
|
+
defaults. This axis is replaced WHOLE on save, so an omitted key would silently revert an author's
|
|
161
|
+
discipline rather than be refused. There is no level-source key: the level a level trigger rests at
|
|
162
|
+
is DERIVED from the trigger and the trade's direction, never named. There is no confirm-timeframe
|
|
163
|
+
key either: the bar whose close decides an entry is the strategy's OWN timeframe.
|
|
164
|
+
|
|
165
|
+
**Every trigger is decided at the close of the strategy's own bar — there are three, and the close
|
|
166
|
+
is the only entry clock.** The newest settled bar is read on the closed basis — every `LIVE`-clocked
|
|
167
|
+
condition resolves on that bar and the scorecard reads its close — and a reading that still qualifies
|
|
168
|
+
fires at that close. A bar that does not qualify decides nothing and is not revisited. The fill lands
|
|
169
|
+
at the next tick, and the platform refuses it if the market has already run past its own drift budget
|
|
170
|
+
from that close. A fourth value, `AT_SIGNAL`, is readable on strategies authored before this contract
|
|
171
|
+
and is REFUSED on every save; there is no live-reading entry to author.
|
|
172
|
+
|
|
173
|
+
- `ON_CANDLE_CLOSE` — the entry is taken AT the qualifying close, at market.
|
|
174
|
+
- `STOP_THROUGH_LEVEL` — at the qualifying close a TRIGGER order rests past the Donchian channel's
|
|
175
|
+
CURRENT edge in the trade's direction (the 20-bar high for a long, the 20-bar low for a short) by
|
|
176
|
+
the offset, and the exchange book is the watcher; the entry is taken when price trades through,
|
|
177
|
+
not when the platform notices.
|
|
178
|
+
- `ON_RETEST` — at the qualifying close a LIMIT order rests in front of the edge a close most
|
|
179
|
+
recently BROKE (the channel's break memory: the broken high for a long, the broken low for a
|
|
180
|
+
short) by the offset, waiting for a return to it. Not filling is a correct outcome, not a
|
|
181
|
+
failure; no unrecovered break in memory means no setup, never a fallback level.
|
|
182
|
+
|
|
183
|
+
**A multi-bar hold belongs to the CONDITION that needs it.** Declare `clock: CLOSE` with that
|
|
184
|
+
condition's own `closes` — the entry axis counts no bars, and there is no displacement band: the
|
|
185
|
+
platform's entry-deviation gate measures the live mark against the decided close and refuses a fill
|
|
186
|
+
that drifted past the budget in either direction.
|
|
189
187
|
|
|
190
188
|
`levelOffsetAtrMultiple` (0–2) is an UNSIGNED distance from the derived edge in ATR multiples — a
|
|
191
189
|
long adds it, a short subtracts it, so a breakout stop rests past its edge and a pullback limit
|
|
@@ -196,11 +194,10 @@ correct side of the mark when the order is placed — a buy stop above it, a buy
|
|
|
196
194
|
mirror for a sell — or the entry is refused rather than filled at the market; there is no limit on
|
|
197
195
|
how far from the mark a level may rest.
|
|
198
196
|
|
|
199
|
-
**The legality matrix runs one way.** All
|
|
200
|
-
"is it set" but "is it set to something that MEANS anything under this trigger".
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
the platform will ignore.
|
|
197
|
+
**The legality matrix runs one way.** All three keys are always present, so the question is never
|
|
198
|
+
"is it set" but "is it set to something that MEANS anything under this trigger".
|
|
199
|
+
`levelOffsetAtrMultiple` ≠ 0 and `validForBars` ≠ 4 are refused under a non-level trigger. Leave a
|
|
200
|
+
dial at its inert value rather than setting one the platform will ignore.
|
|
204
201
|
|
|
205
202
|
## Report sections
|
|
206
203
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: battlegrid-trade-proposal
|
|
3
|
-
description: Find and stage a trade for one of the player's agents, for the player to approve. Activate whenever the player asks which coins fit an agent right now, wants a trade found or proposed for an agent, asks the agent to evaluate a coin, or wants to approve or decline a proposal it made. The scan is the agent's own gates over every active coin;
|
|
3
|
+
description: Find and stage a trade for one of the player's agents, for the player to approve. Activate whenever the player asks which coins fit an agent right now, wants a trade found or proposed for an agent, asks the agent to evaluate a coin, or wants to approve or decline a proposal it made. The scan is the agent's own gates over every active coin; a proposal is QUEUED for the agent's next strategy-bar close and answered into the conversation when that bar is decided; approval is always the player's word.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Trade Proposal
|
|
@@ -29,33 +29,47 @@ re-sorts a list, re-derives a verdict, or approves on the player's behalf.
|
|
|
29
29
|
- A refused scan (`RATE_LIMITED`) is a refusal: say the scan was refused and when it can be retried
|
|
30
30
|
(`retryAfterSeconds`). Never say "nothing fits".
|
|
31
31
|
|
|
32
|
-
## 2.
|
|
32
|
+
## 2. Queue the request
|
|
33
33
|
|
|
34
34
|
- `propose_entry_decision` **only** on the coins the player named, or on the top qualifying row
|
|
35
35
|
when they asked for the best fit. One coin per call.
|
|
36
|
+
- **The call does not decide.** It registers a request against the agent's next strategy-bar close
|
|
37
|
+
and returns immediately; no model runs and nothing is spent. The agent reads that bar on its
|
|
38
|
+
settled close, through the same path its radar deployments use.
|
|
36
39
|
- `userMessage` is the player's own words for the turn. Mint a **fresh UUID** `idempotencyKey` per
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## 3.
|
|
42
|
-
|
|
43
|
-
- `type: "
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
40
|
+
request; a retry with the same key replays the recorded result and never registers a second one.
|
|
41
|
+
- **One pending request per coin.** A second is refused with `CONFLICT` / `REQUEST_PENDING` — read
|
|
42
|
+
the one that exists with `get_entry_request` rather than asking again.
|
|
43
|
+
|
|
44
|
+
## 3. Tell the player what they are waiting for
|
|
45
|
+
|
|
46
|
+
- `type: "queued"` is the normal answer. Read `request` back to them: the bar being decided
|
|
47
|
+
(`barStart`), when the answer is due (`decidesBy`), and the deadline past which that bar can no
|
|
48
|
+
longer be decided (`windowEndsAt`). `decidesBy` and `windowEndsAt` are different instants — a bar
|
|
49
|
+
that has already settled is decided at the next sweep, seconds away.
|
|
50
|
+
- Say where the answer will appear: in the agent's conversation, and — if it proposes a trade — in
|
|
51
|
+
`list_pending_approvals`. Nothing further is needed from the player until then.
|
|
52
|
+
- `get_entry_request` re-reads a request still pending; `cancel_entry_request` withdraws it. Both
|
|
53
|
+
are `NOT_FOUND` once it has been answered, cancelled or expired, and that is the honest record:
|
|
54
|
+
the answer is in the conversation.
|
|
55
|
+
- `type: "error"` — the block, with its remedy: `OPEN_POSITION_CONFLICT` means a position already
|
|
56
|
+
holds the slot (see step 0) and nothing was queued; `LLM_CREDITS_EXHAUSTED` means top up when
|
|
57
|
+
`topupAvailable` is true. A refused request (`RATE_LIMITED`) is a refusal with its retry-after.
|
|
58
|
+
Never "nothing to do".
|
|
59
|
+
- `type: "recommendation"` and `type: "no_trade"` arrive only as the **replay** of a request made
|
|
60
|
+
before the queued contract shipped. Report them as step 4 describes and do not expect them from a
|
|
61
|
+
fresh call.
|
|
62
|
+
|
|
63
|
+
## 4. When the answer arrives — approve or decline only on the player's word
|
|
64
|
+
|
|
65
|
+
- A decided close produces one of: a PROPOSED decision awaiting approval; a no-trade with the
|
|
66
|
+
reason and the next coins worth asking about; a close that did not qualify; a window that passed
|
|
67
|
+
with no sweep; or a bar the agent's own radar deployment decided first and traded in full.
|
|
68
|
+
- On a proposal, state the direction, the entry, stop and take-profit levels, the position size,
|
|
69
|
+
the **`convictionPercent`**, and the expiry (`expiresAt`). No conviction floor is applied on this
|
|
70
|
+
surface: the conviction is the agent's own reading, and the player judges it.
|
|
71
|
+
- Present it and **ask**. `accept_entry_decision` only after the player explicitly approves;
|
|
72
|
+
`cancel_entry_decision` only after they explicitly decline.
|
|
59
73
|
- Never accept because the conviction reads high, because the scan ranked the coin first, or
|
|
60
74
|
because the player asked you to "find a trade" — finding is not approving. `get_entry_decision`
|
|
61
75
|
re-reads the row if the conversation moved on before they answered.
|