@coinrithm/mcp-trading 0.7.7 → 0.7.8
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/CHANGELOG.md +83 -0
- package/dist/agent/act.js +19 -6
- package/dist/agent/capitalSizing.d.ts +32 -0
- package/dist/agent/capitalSizing.js +257 -0
- package/dist/agent/decision.d.ts +392 -0
- package/dist/agent/decision.js +177 -0
- package/dist/agent/decisionReceipt.d.ts +45 -0
- package/dist/agent/decisionReceipt.js +595 -0
- package/dist/agent/decisionValidator.d.ts +19 -2
- package/dist/agent/decisionValidator.js +74 -3
- package/dist/agent/engine.d.ts +1 -0
- package/dist/agent/engine.js +1 -0
- package/dist/agent/observe.js +175 -34
- package/dist/agent/pmContext.d.ts +13 -0
- package/dist/agent/pmContext.js +136 -0
- package/dist/agent/prompt.d.ts +11 -1
- package/dist/agent/prompt.js +135 -7
- package/dist/agent/providerCapabilities.d.ts +3 -0
- package/dist/agent/providerCapabilities.js +41 -3
- package/dist/agent/providers.js +119 -73
- package/dist/agent/resolve.js +1 -0
- package/dist/agent/runner.d.ts +4 -1
- package/dist/agent/runner.js +360 -34
- package/dist/agent/scorecard.js +7 -1
- package/dist/agent/skill.js +17 -0
- package/dist/agent/skillValidator.d.ts +1 -0
- package/dist/agent/skillValidator.js +56 -0
- package/dist/agent/state.js +6 -0
- package/dist/agent/strictLint.js +19 -0
- package/dist/agent/thesis.d.ts +40 -0
- package/dist/agent/thesis.js +319 -0
- package/dist/agent/types.d.ts +127 -0
- package/package.json +1 -1
package/dist/agent/decision.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// JSON, an unknown action type, a free-form endpoint/tool name, extra unknown
|
|
4
4
|
// fields, or a missing required field — fails closed (the runner skips).
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { coerceThesis } from "./thesis.js";
|
|
6
7
|
// Small models (especially Llama 3.1 8B) frequently emit numbers as JSON strings
|
|
7
8
|
// ("12345", "0.8", "62000"). Coerce a *clean* numeric string to a number before
|
|
8
9
|
// validating; leave anything else untouched so genuine garbage ("abc", "pos#5")
|
|
@@ -31,6 +32,15 @@ const forecastProbability = z
|
|
|
31
32
|
return typeof n === "number" && Number.isFinite(n) ? n : undefined;
|
|
32
33
|
})
|
|
33
34
|
.optional();
|
|
35
|
+
// The thesis an OPEN is made on (slice 2, 2026-09-02): {summary, invalidation}.
|
|
36
|
+
// TOLERANT like forecastProbability: a missing / malformed thesis becomes
|
|
37
|
+
// undefined and never fails an otherwise-valid open. The prompt requires it and
|
|
38
|
+
// the structured-output schema below lists it as required; the runner binds it
|
|
39
|
+
// to the position the server returns and evaluates it every cycle.
|
|
40
|
+
const thesis = z
|
|
41
|
+
.any()
|
|
42
|
+
.transform((v) => coerceThesis(v))
|
|
43
|
+
.optional();
|
|
34
44
|
const futuresOpen = z
|
|
35
45
|
.object({
|
|
36
46
|
type: z.literal("futures_open"),
|
|
@@ -42,6 +52,7 @@ const futuresOpen = z
|
|
|
42
52
|
takeProfitPrice: num(z.number()).nullable().optional(),
|
|
43
53
|
confidence,
|
|
44
54
|
rationaleSummary: z.string().optional(),
|
|
55
|
+
thesis,
|
|
45
56
|
})
|
|
46
57
|
.strict();
|
|
47
58
|
const futuresClose = z
|
|
@@ -51,6 +62,11 @@ const futuresClose = z
|
|
|
51
62
|
fraction: num(z.number().positive().max(1)).optional(),
|
|
52
63
|
confidence,
|
|
53
64
|
rationaleSummary: z.string().optional(),
|
|
65
|
+
// Accepted-and-unused: models copy the open-side `thesis` onto every
|
|
66
|
+
// action exactly as they copied `confidence` (the .strict() fail-closed
|
|
67
|
+
// lesson of 2026-08-19). The structured-output schema never asks for it
|
|
68
|
+
// here; a BYO model that echoes it must not zero the whole cycle.
|
|
69
|
+
thesis,
|
|
54
70
|
})
|
|
55
71
|
.strict();
|
|
56
72
|
const futuresSetSltp = z
|
|
@@ -66,6 +82,7 @@ const futuresSetSltp = z
|
|
|
66
82
|
// trade actions already have.
|
|
67
83
|
confidence,
|
|
68
84
|
rationaleSummary: z.string().optional(),
|
|
85
|
+
thesis, // accepted-and-unused, see futures_close
|
|
69
86
|
})
|
|
70
87
|
.strict();
|
|
71
88
|
const spotOrder = z
|
|
@@ -79,6 +96,7 @@ const spotOrder = z
|
|
|
79
96
|
stopPrice: num(z.number().positive()).optional(),
|
|
80
97
|
confidence,
|
|
81
98
|
rationaleSummary: z.string().optional(),
|
|
99
|
+
thesis,
|
|
82
100
|
})
|
|
83
101
|
.strict();
|
|
84
102
|
const spotCancel = z
|
|
@@ -88,6 +106,7 @@ const spotCancel = z
|
|
|
88
106
|
// Same accepted-and-unused tolerance as futures_set_sltp above.
|
|
89
107
|
confidence,
|
|
90
108
|
rationaleSummary: z.string().optional(),
|
|
109
|
+
thesis,
|
|
91
110
|
})
|
|
92
111
|
.strict();
|
|
93
112
|
// pm_open accepts EITHER a short ref (pm1…pmN, what the prompt now asks for) OR
|
|
@@ -107,6 +126,7 @@ const pmOpen = z
|
|
|
107
126
|
confidence,
|
|
108
127
|
rationaleSummary: z.string().optional(),
|
|
109
128
|
forecastProbability,
|
|
129
|
+
thesis,
|
|
110
130
|
})
|
|
111
131
|
.strict();
|
|
112
132
|
export const actionSchema = z.discriminatedUnion("type", [
|
|
@@ -117,6 +137,163 @@ export const actionSchema = z.discriminatedUnion("type", [
|
|
|
117
137
|
spotCancel,
|
|
118
138
|
pmOpen,
|
|
119
139
|
]);
|
|
140
|
+
// Request-side structured-output contract for providers that support JSON
|
|
141
|
+
// Schema. Keep this beside the Zod parser: the provider must not be allowed to
|
|
142
|
+
// emit an `act` with no actions (the Nano incident on 2026-08-27), while a real
|
|
143
|
+
// skip must carry an empty action list. The runner still parses with Zod and
|
|
144
|
+
// re-validates every action against live quotes/caps before any write.
|
|
145
|
+
//
|
|
146
|
+
// `pm_open` deliberately requires the short per-cycle `ref` here. The parser
|
|
147
|
+
// remains backwards-compatible with the full source/slug/id triple, but the
|
|
148
|
+
// hosted prompt asks models for refs and that is the only reliable generated
|
|
149
|
+
// shape.
|
|
150
|
+
const nullableNumber = { anyOf: [{ type: "number" }, { type: "null" }] };
|
|
151
|
+
const optionalConfidence = { type: "number", minimum: 0, maximum: 1 };
|
|
152
|
+
const optionalRationaleSummary = { type: "string" };
|
|
153
|
+
// Thesis contract for the structured-output transports (the NVIDIA endpoint
|
|
154
|
+
// forces the tool call from this schema, so a field absent here can never be
|
|
155
|
+
// emitted). One shared shape; the runner drops the fields that do not apply to
|
|
156
|
+
// the venue and re-signs nothing.
|
|
157
|
+
const thesisSchema = {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: {
|
|
160
|
+
summary: { type: "string", maxLength: 200 },
|
|
161
|
+
invalidation: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {
|
|
164
|
+
priceBelow: { type: "number", exclusiveMinimum: 0 },
|
|
165
|
+
priceAbove: { type: "number", exclusiveMinimum: 0 },
|
|
166
|
+
probabilityBelow: { type: "number", exclusiveMinimum: 0, maximum: 100 },
|
|
167
|
+
probabilityAbove: { type: "number", exclusiveMinimum: 0, maximum: 100 },
|
|
168
|
+
maxHoldMinutes: { type: "number", exclusiveMinimum: 0 },
|
|
169
|
+
catalyst: { type: "string", maxLength: 160 },
|
|
170
|
+
},
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
required: ["summary", "invalidation"],
|
|
175
|
+
additionalProperties: false,
|
|
176
|
+
};
|
|
177
|
+
const generatedActionSchemas = [
|
|
178
|
+
{
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
type: { const: "futures_open" },
|
|
182
|
+
symbol: { type: "string", minLength: 1 },
|
|
183
|
+
side: { enum: ["long", "short"] },
|
|
184
|
+
leverage: { type: "number", exclusiveMinimum: 0 },
|
|
185
|
+
marginMusd: { type: "number", exclusiveMinimum: 0 },
|
|
186
|
+
stopLossPrice: nullableNumber,
|
|
187
|
+
takeProfitPrice: nullableNumber,
|
|
188
|
+
confidence: optionalConfidence,
|
|
189
|
+
rationaleSummary: optionalRationaleSummary,
|
|
190
|
+
thesis: thesisSchema,
|
|
191
|
+
},
|
|
192
|
+
required: ["type", "symbol", "side", "leverage", "marginMusd", "thesis"],
|
|
193
|
+
additionalProperties: false,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: "object",
|
|
197
|
+
properties: {
|
|
198
|
+
type: { const: "futures_close" },
|
|
199
|
+
positionId: { type: "number" },
|
|
200
|
+
fraction: { type: "number", exclusiveMinimum: 0, maximum: 1 },
|
|
201
|
+
confidence: optionalConfidence,
|
|
202
|
+
rationaleSummary: optionalRationaleSummary,
|
|
203
|
+
},
|
|
204
|
+
required: ["type", "positionId"],
|
|
205
|
+
additionalProperties: false,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
type: { const: "futures_set_sltp" },
|
|
211
|
+
positionId: { type: "number" },
|
|
212
|
+
stopLossPrice: nullableNumber,
|
|
213
|
+
takeProfitPrice: nullableNumber,
|
|
214
|
+
confidence: optionalConfidence,
|
|
215
|
+
rationaleSummary: optionalRationaleSummary,
|
|
216
|
+
},
|
|
217
|
+
required: ["type", "positionId"],
|
|
218
|
+
additionalProperties: false,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "object",
|
|
222
|
+
properties: {
|
|
223
|
+
type: { const: "spot_order" },
|
|
224
|
+
symbol: { type: "string", minLength: 1 },
|
|
225
|
+
side: { enum: ["buy", "sell"] },
|
|
226
|
+
orderType: { enum: ["market", "limit", "stop"] },
|
|
227
|
+
quantity: { type: "number", exclusiveMinimum: 0 },
|
|
228
|
+
limitPrice: { type: "number", exclusiveMinimum: 0 },
|
|
229
|
+
stopPrice: { type: "number", exclusiveMinimum: 0 },
|
|
230
|
+
confidence: optionalConfidence,
|
|
231
|
+
rationaleSummary: optionalRationaleSummary,
|
|
232
|
+
thesis: thesisSchema,
|
|
233
|
+
},
|
|
234
|
+
required: ["type", "symbol", "side", "orderType", "quantity", "thesis"],
|
|
235
|
+
additionalProperties: false,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
type: { const: "spot_cancel" },
|
|
241
|
+
orderId: { type: "number" },
|
|
242
|
+
confidence: optionalConfidence,
|
|
243
|
+
rationaleSummary: optionalRationaleSummary,
|
|
244
|
+
},
|
|
245
|
+
required: ["type", "orderId"],
|
|
246
|
+
additionalProperties: false,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
type: { const: "pm_open" },
|
|
252
|
+
ref: { type: "string", minLength: 1 },
|
|
253
|
+
stakeMusd: { type: "number", exclusiveMinimum: 0 },
|
|
254
|
+
confidence: optionalConfidence,
|
|
255
|
+
rationaleSummary: optionalRationaleSummary,
|
|
256
|
+
forecastProbability: { type: "number" },
|
|
257
|
+
thesis: thesisSchema,
|
|
258
|
+
},
|
|
259
|
+
required: ["type", "ref", "stakeMusd", "thesis"],
|
|
260
|
+
additionalProperties: false,
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
const generatedDecisionProperties = {
|
|
264
|
+
confidence: optionalConfidence,
|
|
265
|
+
reason: { type: "string" },
|
|
266
|
+
rationale: { type: "string", maxLength: 1200 },
|
|
267
|
+
};
|
|
268
|
+
export const DECISION_JSON_SCHEMA = {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
decision: { enum: ["skip", "act"] },
|
|
272
|
+
...generatedDecisionProperties,
|
|
273
|
+
actions: {
|
|
274
|
+
type: "array",
|
|
275
|
+
items: { oneOf: generatedActionSchemas },
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
required: ["decision", "actions"],
|
|
279
|
+
additionalProperties: false,
|
|
280
|
+
allOf: [
|
|
281
|
+
{
|
|
282
|
+
if: {
|
|
283
|
+
properties: { decision: { const: "act" } },
|
|
284
|
+
required: ["decision"],
|
|
285
|
+
},
|
|
286
|
+
then: { properties: { actions: { minItems: 1 } } },
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
if: {
|
|
290
|
+
properties: { decision: { const: "skip" } },
|
|
291
|
+
required: ["decision"],
|
|
292
|
+
},
|
|
293
|
+
then: { properties: { actions: { maxItems: 0 } } },
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
};
|
|
120
297
|
const decisionSchema = z
|
|
121
298
|
.object({
|
|
122
299
|
decision: z.enum(["skip", "act"]),
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentSpec, Observation, RunState } from "./types.js";
|
|
2
|
+
import { type DailyRiskBudget } from "./prompt.js";
|
|
3
|
+
export declare const DECISION_INPUT_MAX_BYTES: number;
|
|
4
|
+
export type DecisionInputPhase = "before_observation" | "observed" | "decision_input";
|
|
5
|
+
type Scalar = string | number | boolean | null;
|
|
6
|
+
type Row = Record<string, Scalar | string[] | Record<string, Scalar | string[]>>;
|
|
7
|
+
export interface DecisionInputRecord {
|
|
8
|
+
version: "coinrithm.decision-input.v1";
|
|
9
|
+
visibility: "private";
|
|
10
|
+
completeness: "partial";
|
|
11
|
+
phase: DecisionInputPhase;
|
|
12
|
+
outcome: "pending" | "returned" | "runtime_error";
|
|
13
|
+
runId: string | null;
|
|
14
|
+
decisionId: string | null;
|
|
15
|
+
configFingerprint: string | null;
|
|
16
|
+
observationFingerprint: string | null;
|
|
17
|
+
preThesisObservationFingerprint: string | null;
|
|
18
|
+
dailyRiskBudget: DailyRiskBudget | null;
|
|
19
|
+
guardState: Record<string, number | boolean | null>;
|
|
20
|
+
account: Record<string, number | boolean | string | null> | null;
|
|
21
|
+
lists: Record<string, Row[]>;
|
|
22
|
+
counts: Record<string, {
|
|
23
|
+
source: number;
|
|
24
|
+
retained: number;
|
|
25
|
+
omitted: number;
|
|
26
|
+
}>;
|
|
27
|
+
omissions: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface DecisionInputCapture {
|
|
30
|
+
phase: DecisionInputPhase;
|
|
31
|
+
runId: string;
|
|
32
|
+
decisionId: string;
|
|
33
|
+
spec: AgentSpec;
|
|
34
|
+
mergedProse: string;
|
|
35
|
+
state: RunState;
|
|
36
|
+
observation?: Observation;
|
|
37
|
+
observationFingerprint?: string;
|
|
38
|
+
preThesisObservationFingerprint?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function buildDecisionInputRecord(input: DecisionInputCapture): DecisionInputRecord;
|
|
41
|
+
export declare function unavailableDecisionInputRecord(): DecisionInputRecord;
|
|
42
|
+
/** Defense at the storage boundary. Reject rather than preserve an arbitrary
|
|
43
|
+
* extension field. Detached JSON copy also prevents post-check mutation. */
|
|
44
|
+
export declare function sanitizeDecisionInputRecord(value: unknown): DecisionInputRecord | undefined;
|
|
45
|
+
export {};
|