@coinrithm/mcp-trading 0.7.14 → 0.7.15
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 +692 -613
- package/README.md +400 -366
- package/dist/agent/client.d.ts +13 -0
- package/dist/agent/client.js +107 -4
- package/dist/agent/decisionReceipt.d.ts +5 -1
- package/dist/agent/decisionReceipt.js +52 -3
- package/dist/agent/futuresEligibility.d.ts +36 -0
- package/dist/agent/futuresEligibility.js +90 -0
- package/dist/agent/observe.d.ts +9 -1
- package/dist/agent/observe.js +443 -42
- package/dist/agent/pmContext.d.ts +10 -1
- package/dist/agent/pmContext.js +163 -1
- package/dist/agent/prompt.d.ts +14 -0
- package/dist/agent/prompt.js +131 -26
- package/dist/agent/providerCapabilities.d.ts +3 -0
- package/dist/agent/providerCapabilities.js +34 -4
- package/dist/agent/providers.d.ts +3 -0
- package/dist/agent/providers.js +12 -2
- package/dist/agent/runner.js +100 -11
- package/dist/agent/templates.js +30 -30
- package/dist/agent/types.d.ts +117 -1
- package/dist/agent/types.js +4 -0
- package/dist/client.d.ts +9 -2
- package/dist/client.js +22 -10
- package/dist/http.d.ts +1 -0
- package/dist/http.js +17 -2
- package/dist/httpCompletion.d.ts +1 -1
- package/dist/httpCompletion.js +1 -0
- package/dist/index.js +2 -1
- package/dist/instructions.d.ts +1 -0
- package/dist/instructions.js +16 -0
- package/dist/tools.d.ts +1 -0
- package/dist/tools.js +150 -66
- package/package.json +97 -97
|
@@ -54,18 +54,48 @@ export function chatShapeFor(provider, model, baseUrl) {
|
|
|
54
54
|
minProbeCompletionTokens: 1024,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
// Transport-only restriction: drop the excluded variants from the action oneOf
|
|
58
|
+
// a route decodes against. parseDecision keeps validating the FULL contract, so
|
|
59
|
+
// a route that does not enforce a schema can still propose the action and the
|
|
60
|
+
// validator rejects it. Never returns an empty oneOf.
|
|
61
|
+
export function restrictDecisionSchema(schema, exclude) {
|
|
62
|
+
if (!exclude?.length)
|
|
63
|
+
return schema;
|
|
64
|
+
const properties = schema.properties;
|
|
65
|
+
const actions = properties?.actions;
|
|
66
|
+
const variants = actions?.items?.oneOf;
|
|
67
|
+
if (!properties || !actions || !Array.isArray(variants))
|
|
68
|
+
return schema;
|
|
69
|
+
const kept = variants.filter((variant) => {
|
|
70
|
+
const type = variant
|
|
71
|
+
.properties?.type?.const;
|
|
72
|
+
return !exclude.includes(type);
|
|
73
|
+
});
|
|
74
|
+
if (kept.length === variants.length || kept.length === 0)
|
|
75
|
+
return schema;
|
|
76
|
+
return {
|
|
77
|
+
...schema,
|
|
78
|
+
properties: {
|
|
79
|
+
...properties,
|
|
80
|
+
actions: { ...actions, items: { ...actions.items, oneOf: kept } },
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
57
84
|
/** Build the chat-completions body for a route from its capability shape. */
|
|
58
85
|
export function buildChatBody(shape, args) {
|
|
59
86
|
const system = shape.systemHint
|
|
60
87
|
? `${shape.systemHint}\n\n${args.system}`
|
|
61
88
|
: args.system;
|
|
89
|
+
const jsonSchema = shape.jsonSchema
|
|
90
|
+
? restrictDecisionSchema(shape.jsonSchema, args.excludeActionTypes)
|
|
91
|
+
: undefined;
|
|
62
92
|
return {
|
|
63
93
|
model: args.model,
|
|
64
94
|
...(shape.allowsTemperature
|
|
65
95
|
? { temperature: args.temperature ?? 0.2 }
|
|
66
96
|
: {}),
|
|
67
97
|
[shape.tokenParam]: args.maxTokens,
|
|
68
|
-
...(
|
|
98
|
+
...(jsonSchema && shape.jsonSchemaTransport === "tool_call"
|
|
69
99
|
? {
|
|
70
100
|
tools: [
|
|
71
101
|
{
|
|
@@ -73,7 +103,7 @@ export function buildChatBody(shape, args) {
|
|
|
73
103
|
function: {
|
|
74
104
|
name: DECISION_TOOL_NAME,
|
|
75
105
|
description: "Submit the complete CoinRithm paper-trading decision for this cycle.",
|
|
76
|
-
parameters:
|
|
106
|
+
parameters: jsonSchema,
|
|
77
107
|
},
|
|
78
108
|
},
|
|
79
109
|
],
|
|
@@ -85,12 +115,12 @@ export function buildChatBody(shape, args) {
|
|
|
85
115
|
: {}),
|
|
86
116
|
...(shape.jsonResponseFormat && shape.jsonSchemaTransport !== "tool_call"
|
|
87
117
|
? {
|
|
88
|
-
response_format:
|
|
118
|
+
response_format: jsonSchema
|
|
89
119
|
? {
|
|
90
120
|
type: "json_schema",
|
|
91
121
|
json_schema: {
|
|
92
122
|
name: "coinrithm_trading_decision",
|
|
93
|
-
schema:
|
|
123
|
+
schema: jsonSchema,
|
|
94
124
|
},
|
|
95
125
|
}
|
|
96
126
|
: { type: "json_object" },
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AgentSpec, ProviderName } from "./types.js";
|
|
2
|
+
import { type DecisionActionExclusion } from "./providerCapabilities.js";
|
|
2
3
|
export interface DecideInput {
|
|
3
4
|
system: string;
|
|
4
5
|
user: string;
|
|
5
6
|
maxTokens?: number;
|
|
6
7
|
timeoutMs?: number;
|
|
8
|
+
excludeActionTypes?: readonly DecisionActionExclusion[];
|
|
7
9
|
}
|
|
8
10
|
export interface DecideRouteAttempt {
|
|
9
11
|
provider: string;
|
|
@@ -26,6 +28,7 @@ export interface DecideRouteMeta {
|
|
|
26
28
|
export type DecideResult = {
|
|
27
29
|
ok: true;
|
|
28
30
|
text: string;
|
|
31
|
+
responseSource?: "tool_call" | "content_fallback" | "content";
|
|
29
32
|
usage?: {
|
|
30
33
|
promptTokens: number;
|
|
31
34
|
completionTokens: number;
|
package/dist/agent/providers.js
CHANGED
|
@@ -193,7 +193,7 @@ class AnthropicProvider {
|
|
|
193
193
|
}
|
|
194
194
|
: undefined;
|
|
195
195
|
return text
|
|
196
|
-
? { ok: true, text, usage }
|
|
196
|
+
? { ok: true, text, usage, responseSource: "content" }
|
|
197
197
|
: { ok: false, error: "anthropic returned empty content" };
|
|
198
198
|
});
|
|
199
199
|
}
|
|
@@ -246,6 +246,7 @@ class OpenAiCompatProvider {
|
|
|
246
246
|
system: input.system,
|
|
247
247
|
user: input.user,
|
|
248
248
|
maxTokens: input.maxTokens ?? 1024,
|
|
249
|
+
excludeActionTypes: input.excludeActionTypes,
|
|
249
250
|
})),
|
|
250
251
|
});
|
|
251
252
|
if (!res.ok) {
|
|
@@ -273,7 +274,16 @@ class OpenAiCompatProvider {
|
|
|
273
274
|
}
|
|
274
275
|
: undefined;
|
|
275
276
|
return text
|
|
276
|
-
? {
|
|
277
|
+
? {
|
|
278
|
+
ok: true,
|
|
279
|
+
text,
|
|
280
|
+
usage,
|
|
281
|
+
responseSource: decisionArguments != null
|
|
282
|
+
? "tool_call"
|
|
283
|
+
: shape.jsonSchemaTransport === "tool_call"
|
|
284
|
+
? "content_fallback"
|
|
285
|
+
: "content",
|
|
286
|
+
}
|
|
277
287
|
: { ok: false, error: "provider returned empty content" };
|
|
278
288
|
});
|
|
279
289
|
}
|
package/dist/agent/runner.js
CHANGED
|
@@ -9,10 +9,11 @@ import { usesCapitalSizing, prepareCapitalAction, validateCapitalAction, capital
|
|
|
9
9
|
import { decideMechanical } from "./mechanical.js";
|
|
10
10
|
import { evaluateGate, noteLlmCall, estimateCostUsd } from "./gate.js";
|
|
11
11
|
import { baseSymbol, scanSetups } from "./setups.js";
|
|
12
|
+
import { futuresEntryPreflight } from "./futuresEligibility.js";
|
|
12
13
|
import { reconcileObservation } from "./reconcileObservation.js";
|
|
13
14
|
import { createOpportunityReporter } from "./opportunityReporter.js";
|
|
14
|
-
import { observe } from "./observe.js";
|
|
15
|
-
import { buildDailyRiskBudget, buildSystemPrompt, buildUserPrompt, } from "./prompt.js";
|
|
15
|
+
import { enrichWhaleContext, observe } from "./observe.js";
|
|
16
|
+
import { buildDailyRiskBudget, buildFuturesCapacity, buildSystemPrompt, buildUserPrompt, } from "./prompt.js";
|
|
16
17
|
import { parseDecision } from "./decision.js";
|
|
17
18
|
import { validateAction, isRiskIncreasingAction, } from "./decisionValidator.js";
|
|
18
19
|
import { resolvePmRef } from "./resolvePm.js";
|
|
@@ -435,6 +436,19 @@ async function runCycleCore(deps, capture) {
|
|
|
435
436
|
const decisionId = makeDecisionId(state.cyclesRun);
|
|
436
437
|
const captureBase = { runId, decisionId, spec, mergedProse, state };
|
|
437
438
|
capture({ ...captureBase, phase: "before_observation" });
|
|
439
|
+
// These exits happen before both provider invocation and action execution.
|
|
440
|
+
// Record that known absence explicitly; missing metering means unknown to
|
|
441
|
+
// audit consumers. Do not default later failures to zero: a call/write may
|
|
442
|
+
// already have occurred by then.
|
|
443
|
+
const preDecisionMeter = {
|
|
444
|
+
llmCallMade: false,
|
|
445
|
+
decisionType: "skip",
|
|
446
|
+
tokensIn: 0,
|
|
447
|
+
tokensOut: 0,
|
|
448
|
+
estimatedCostUsd: 0,
|
|
449
|
+
writeAttempted: 0,
|
|
450
|
+
writeAccepted: 0,
|
|
451
|
+
};
|
|
438
452
|
// Kill-switch pre-check: a disabled agent never observes, decides, or acts.
|
|
439
453
|
const tripped = checkKillSwitch(spec, state);
|
|
440
454
|
if (tripped) {
|
|
@@ -448,6 +462,7 @@ async function runCycleCore(deps, capture) {
|
|
|
448
462
|
disabled: true,
|
|
449
463
|
disabledReason: tripped,
|
|
450
464
|
live,
|
|
465
|
+
...preDecisionMeter,
|
|
451
466
|
};
|
|
452
467
|
}
|
|
453
468
|
// Establish durable run identity before the first possible write. A process
|
|
@@ -511,6 +526,7 @@ async function runCycleCore(deps, capture) {
|
|
|
511
526
|
disabled: true,
|
|
512
527
|
disabledReason: state.disabledReason,
|
|
513
528
|
live,
|
|
529
|
+
...preDecisionMeter,
|
|
514
530
|
...observationReceipt,
|
|
515
531
|
};
|
|
516
532
|
}
|
|
@@ -535,6 +551,7 @@ async function runCycleCore(deps, capture) {
|
|
|
535
551
|
disabled: true,
|
|
536
552
|
disabledReason: state.disabledReason,
|
|
537
553
|
live,
|
|
554
|
+
...preDecisionMeter,
|
|
538
555
|
...observationReceipt,
|
|
539
556
|
};
|
|
540
557
|
}
|
|
@@ -546,6 +563,7 @@ async function runCycleCore(deps, capture) {
|
|
|
546
563
|
skipReason: obs.skip,
|
|
547
564
|
planned: [],
|
|
548
565
|
live,
|
|
566
|
+
...preDecisionMeter,
|
|
549
567
|
...observationReceipt,
|
|
550
568
|
};
|
|
551
569
|
}
|
|
@@ -624,15 +642,15 @@ async function runCycleCore(deps, capture) {
|
|
|
624
642
|
// so it touches NO kill-switch counter; it just records the cheap cycle.
|
|
625
643
|
const policy = spec.triggerPolicy ?? DEFAULT_TRIGGER_POLICY;
|
|
626
644
|
const gate = evaluateGate(observation, state, policy, nowMs);
|
|
627
|
-
capture({
|
|
628
|
-
...captureBase,
|
|
629
|
-
phase: "decision_input",
|
|
630
|
-
observation,
|
|
631
|
-
observationFingerprint: observationReceipt.observationHash,
|
|
632
|
-
preThesisObservationFingerprint,
|
|
633
|
-
});
|
|
634
645
|
const providerName = spec.model?.provider ?? "nvidia";
|
|
635
646
|
if (!gate.fire) {
|
|
647
|
+
capture({
|
|
648
|
+
...captureBase,
|
|
649
|
+
phase: "decision_input",
|
|
650
|
+
observation,
|
|
651
|
+
observationFingerprint: observationReceipt.observationHash,
|
|
652
|
+
preThesisObservationFingerprint,
|
|
653
|
+
});
|
|
636
654
|
saveState(stateFile, state);
|
|
637
655
|
log(`gate: skip (${gate.reason})`);
|
|
638
656
|
return {
|
|
@@ -651,6 +669,20 @@ async function runCycleCore(deps, capture) {
|
|
|
651
669
|
...observationReceipt,
|
|
652
670
|
};
|
|
653
671
|
}
|
|
672
|
+
if (providerName !== "mechanical" &&
|
|
673
|
+
spec.capabilities.includes("whale_context") &&
|
|
674
|
+
spec.venues.includes("pm")) {
|
|
675
|
+
observation.whaleContext = await enrichWhaleContext(client, observation);
|
|
676
|
+
observationReceipt = buildObservationReceipt(observation);
|
|
677
|
+
Object.assign(baseTrace, observationReceipt);
|
|
678
|
+
}
|
|
679
|
+
capture({
|
|
680
|
+
...captureBase,
|
|
681
|
+
phase: "decision_input",
|
|
682
|
+
observation,
|
|
683
|
+
observationFingerprint: observationReceipt.observationHash,
|
|
684
|
+
preThesisObservationFingerprint,
|
|
685
|
+
});
|
|
654
686
|
// DECIDE. Two paths share the same downstream validate+act loop:
|
|
655
687
|
// • mechanical benchmark agents (provider "mechanical") compute the decision
|
|
656
688
|
// deterministically from the observation — no prompt, no model call, no
|
|
@@ -678,19 +710,43 @@ async function runCycleCore(deps, capture) {
|
|
|
678
710
|
};
|
|
679
711
|
}
|
|
680
712
|
else {
|
|
713
|
+
const futuresCapacity = spec.venues.includes("futures")
|
|
714
|
+
? buildFuturesCapacity(spec, observation)
|
|
715
|
+
: undefined;
|
|
716
|
+
// One exclusion drives both the textual action menu and transport schema.
|
|
717
|
+
// It is a proposal hint only; parsing and validation stay unchanged.
|
|
718
|
+
const capacitySpent = futuresCapacity !== undefined &&
|
|
719
|
+
(futuresCapacity.slotsLeft === 0 ||
|
|
720
|
+
futuresCapacity.marginHeadroomMusd <= 0);
|
|
721
|
+
const actionAvailability = capacitySpent
|
|
722
|
+
? { excludeActionTypes: ["futures_open"] }
|
|
723
|
+
: {};
|
|
681
724
|
const system = buildSystemPrompt(spec, mergedProse, {
|
|
682
725
|
includeForecast: forecastEnabled,
|
|
726
|
+
...actionAvailability,
|
|
683
727
|
});
|
|
684
728
|
const user = buildUserPrompt(observation, state.journal, {
|
|
685
729
|
venues: spec.venues,
|
|
686
730
|
dailyRiskBudget: buildDailyRiskBudget(spec, state),
|
|
687
731
|
...(usesCapitalSizing(spec) ? { capitalSizing: spec.capitalSizing } : {}),
|
|
732
|
+
...(futuresCapacity ? { futuresCapacity } : {}),
|
|
733
|
+
...actionAvailability,
|
|
688
734
|
});
|
|
689
735
|
const tokensInEst = Math.round((system.length + user.length) / 4);
|
|
690
736
|
// Prompt-size + trigger visibility in the live terminal.
|
|
691
737
|
log(`prompt ~${tokensInEst} tok ` +
|
|
692
738
|
`(pm ${observation.pmMarkets.length}, trades ${observation.newClosedTrades.length}, watch ${observation.watch.length}, setups ${observation.setups.length}, triggers ${gate.codes.join("|") || "none"})`);
|
|
693
|
-
|
|
739
|
+
// With either futures cap spent, a schema-enforcing route is not offered
|
|
740
|
+
// futures_open at all. Other routes can still propose it; the validator
|
|
741
|
+
// rejects it there exactly as before.
|
|
742
|
+
if (capacitySpent) {
|
|
743
|
+
log(`futures capacity spent (slotsLeft ${futuresCapacity.slotsLeft}, marginHeadroomMusd ${futuresCapacity.marginHeadroomMusd}): futures_open withheld from schema-enforcing routes`);
|
|
744
|
+
}
|
|
745
|
+
const res = await provider.decide({
|
|
746
|
+
system,
|
|
747
|
+
user,
|
|
748
|
+
...actionAvailability,
|
|
749
|
+
});
|
|
694
750
|
const route = res.route;
|
|
695
751
|
const actualCallMade = route
|
|
696
752
|
? route.attempts.some((attempt) => attempt.outcome !== "deferred")
|
|
@@ -841,6 +897,22 @@ async function runCycleCore(deps, capture) {
|
|
|
841
897
|
state.consecutivePermanentModelErrors = 0;
|
|
842
898
|
delete state.permanentModelErrorRoute;
|
|
843
899
|
const parsed = parseDecision(res.text);
|
|
900
|
+
if (capacitySpent) {
|
|
901
|
+
// Persist only route provenance and a parsed action-presence bit in the
|
|
902
|
+
// existing cycle log. Unknown adapters and malformed decisions must not
|
|
903
|
+
// be miscounted as tool compliance; raw output stays unretained.
|
|
904
|
+
const responseSource = res.responseSource === "tool_call" ||
|
|
905
|
+
res.responseSource === "content_fallback" ||
|
|
906
|
+
res.responseSource === "content"
|
|
907
|
+
? res.responseSource
|
|
908
|
+
: "unknown";
|
|
909
|
+
log(`capacity-response ${JSON.stringify({
|
|
910
|
+
responseSource,
|
|
911
|
+
withheldActionProposed: parsed.ok
|
|
912
|
+
? parsed.decision.actions.some((action) => action.type === "futures_open")
|
|
913
|
+
: null,
|
|
914
|
+
})}`);
|
|
915
|
+
}
|
|
844
916
|
if (!parsed.ok) {
|
|
845
917
|
state.consecutiveModelFailures += 1;
|
|
846
918
|
saveState(stateFile, state);
|
|
@@ -966,7 +1038,7 @@ async function runCycleCore(deps, capture) {
|
|
|
966
1038
|
log(`reject pm_open: duplicate_intent (hold PM ${pm.slug})`);
|
|
967
1039
|
continue;
|
|
968
1040
|
}
|
|
969
|
-
//
|
|
1041
|
+
// Market-aware forecast submission (HOUSE_AGENT_FORECAST_ENABLED, default ON).
|
|
970
1042
|
// Attach the model's OWN probability the backed side wins — clamped to [1,99],
|
|
971
1043
|
// OMITTED when absent/unparseable (a bad forecast never blocks the trade), and
|
|
972
1044
|
// NEVER defaulted to the market price. Flag OFF strips any forecast so the open
|
|
@@ -1058,6 +1130,23 @@ async function runCycleCore(deps, capture) {
|
|
|
1058
1130
|
}
|
|
1059
1131
|
}
|
|
1060
1132
|
}
|
|
1133
|
+
// Supported-reference preflight (backend-v2 #106, J-74): skip a NEW futures
|
|
1134
|
+
// open on a coin the observation EXPLICITLY reports has no supported (or only
|
|
1135
|
+
// a stale) perpetual reference, before spending a quote the server gate would
|
|
1136
|
+
// refuse. Unknown eligibility, adds/management on a held coin, spot and PM
|
|
1137
|
+
// pass; the server gate stays authoritative at quote/open time.
|
|
1138
|
+
const preflight = futuresEntryPreflight(action, observation);
|
|
1139
|
+
if (preflight) {
|
|
1140
|
+
planned.push({
|
|
1141
|
+
action,
|
|
1142
|
+
accepted: false,
|
|
1143
|
+
code: preflight.code,
|
|
1144
|
+
...(capitalSizing ? { capitalSizing } : {}),
|
|
1145
|
+
reason: preflight.reason,
|
|
1146
|
+
});
|
|
1147
|
+
log(`reject ${action.type}: ${preflight.code}`);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1061
1150
|
const quote = await fetchQuote(client, action, observation, baseTrace, {
|
|
1062
1151
|
pmMinEntryProbabilityPct: spec.risk.pmMinEntryProbabilityPct,
|
|
1063
1152
|
});
|
package/dist/agent/templates.js
CHANGED
|
@@ -50,37 +50,37 @@ const PRESETS = {
|
|
|
50
50
|
modelFail: 5,
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
-
const THESIS_BODY = `# Momentum Futures — strategy
|
|
54
|
-
|
|
55
|
-
You operate a CoinRithm **paper-trading** futures account (50,000 virtual mUSD).
|
|
56
|
-
Everything here is simulated; it is not financial advice and never touches real
|
|
57
|
-
money. Edit this prose freely (any language) — it is your agent's borders.
|
|
58
|
-
|
|
59
|
-
## Each cycle
|
|
60
|
-
|
|
61
|
-
1. Ground yourself: read your portfolio and open positions first. Never assume
|
|
62
|
-
balances or what is already open.
|
|
63
|
-
2. Scan the watchlist. A candidate is a coin whose short and medium momentum
|
|
64
|
-
agree (both up, or both down) and is not already an open position.
|
|
65
|
-
3. Pick the strongest candidate and commit when the read is clear — even a
|
|
66
|
-
moderate-confidence one — sized small with a stop. Skip only when the signals
|
|
67
|
-
contradict or the data is stale; a quiet tape where your edge is still real
|
|
68
|
-
is an act, not a skip.
|
|
69
|
-
4. Quote before you open. Read the liquidation price and confirm it is sane. If
|
|
70
|
-
the quote is not eligible, relay the reason and stop.
|
|
71
|
-
5. Open small and protected: enter in the trend direction and set a stop-loss at
|
|
72
|
-
open. Place the take-profit a touch wider than the stop.
|
|
73
|
-
6. Stay in sync: poll your trades for any stop / take-profit / liquidation that
|
|
74
|
-
fired while you were not looking, and react to what actually happened.
|
|
75
|
-
|
|
76
|
-
The hard caps (leverage, margin, watchlist) live in the config blocks above and
|
|
53
|
+
const THESIS_BODY = `# Momentum Futures — strategy
|
|
54
|
+
|
|
55
|
+
You operate a CoinRithm **paper-trading** futures account (50,000 virtual mUSD).
|
|
56
|
+
Everything here is simulated; it is not financial advice and never touches real
|
|
57
|
+
money. Edit this prose freely (any language) — it is your agent's borders.
|
|
58
|
+
|
|
59
|
+
## Each cycle
|
|
60
|
+
|
|
61
|
+
1. Ground yourself: read your portfolio and open positions first. Never assume
|
|
62
|
+
balances or what is already open.
|
|
63
|
+
2. Scan the watchlist. A candidate is a coin whose short and medium momentum
|
|
64
|
+
agree (both up, or both down) and is not already an open position.
|
|
65
|
+
3. Pick the strongest candidate and commit when the read is clear — even a
|
|
66
|
+
moderate-confidence one — sized small with a stop. Skip only when the signals
|
|
67
|
+
contradict or the data is stale; a quiet tape where your edge is still real
|
|
68
|
+
is an act, not a skip.
|
|
69
|
+
4. Quote before you open. Read the liquidation price and confirm it is sane. If
|
|
70
|
+
the quote is not eligible, relay the reason and stop.
|
|
71
|
+
5. Open small and protected: enter in the trend direction and set a stop-loss at
|
|
72
|
+
open. Place the take-profit a touch wider than the stop.
|
|
73
|
+
6. Stay in sync: poll your trades for any stop / take-profit / liquidation that
|
|
74
|
+
fired while you were not looking, and react to what actually happened.
|
|
75
|
+
|
|
76
|
+
The hard caps (leverage, margin, watchlist) live in the config blocks above and
|
|
77
77
|
are enforced by the runner — change them there, not in this prose.`;
|
|
78
|
-
const PERSONA_STUB = `# Persona
|
|
79
|
-
|
|
80
|
-
Decisive and in character. Acts on a clear read — even a moderate-confidence one
|
|
81
|
-
— sized small and protected with a stop, and skips only when the read is
|
|
82
|
-
contradictory or the data is stale. States its reasoning plainly in its own
|
|
83
|
-
voice, and never frames paper results as real-money advice.
|
|
78
|
+
const PERSONA_STUB = `# Persona
|
|
79
|
+
|
|
80
|
+
Decisive and in character. Acts on a clear read — even a moderate-confidence one
|
|
81
|
+
— sized small and protected with a stop, and skips only when the read is
|
|
82
|
+
contradictory or the data is stale. States its reasoning plainly in its own
|
|
83
|
+
voice, and never frames paper results as real-money advice.
|
|
84
84
|
`;
|
|
85
85
|
export function buildAgentObject(name, preset) {
|
|
86
86
|
const p = PRESETS[preset];
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FuturesEntryEligibility } from "./futuresEligibility.js";
|
|
1
2
|
import { IndicatorSet } from "./indicators.js";
|
|
2
3
|
import type { DecisionInputRecord } from "./decisionReceipt.js";
|
|
3
4
|
export declare const SPEC_VERSION = "coinrithm.agent.v1";
|
|
@@ -97,7 +98,7 @@ export interface ObjectiveConfig {
|
|
|
97
98
|
secondary: string[];
|
|
98
99
|
horizon?: string;
|
|
99
100
|
}
|
|
100
|
-
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan"];
|
|
101
|
+
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan", "whale_context"];
|
|
101
102
|
export type Capability = (typeof ALLOWED_CAPABILITIES)[number];
|
|
102
103
|
export interface AgentSpec {
|
|
103
104
|
name: string;
|
|
@@ -158,12 +159,16 @@ export interface WatchEntry {
|
|
|
158
159
|
change24h?: number;
|
|
159
160
|
change7d?: number;
|
|
160
161
|
sentimentBullishPct?: number;
|
|
162
|
+
sentimentTotalVotes?: number;
|
|
163
|
+
sentimentDayUtc?: string;
|
|
164
|
+
sentimentUpdatedAt?: string;
|
|
161
165
|
freshness?: Freshness;
|
|
162
166
|
indicators?: IndicatorSet;
|
|
163
167
|
indicatorContext?: IndicatorContext;
|
|
164
168
|
discovered?: boolean;
|
|
165
169
|
slug?: string;
|
|
166
170
|
fundamentals?: CoinFundamentals;
|
|
171
|
+
futuresEntryEligibility?: FuturesEntryEligibility;
|
|
167
172
|
}
|
|
168
173
|
export interface CoinFundamentals {
|
|
169
174
|
categories?: string[];
|
|
@@ -247,6 +252,42 @@ export interface PmMarket {
|
|
|
247
252
|
liquidityUsd?: number;
|
|
248
253
|
quality?: PmQuality;
|
|
249
254
|
decisionSupport?: PmDecisionSupport;
|
|
255
|
+
consensus?: PmConsensus | null;
|
|
256
|
+
rules?: PmSettlementRule | null;
|
|
257
|
+
outcomeRule?: PmOutcomeRule;
|
|
258
|
+
}
|
|
259
|
+
export interface PmCalibration {
|
|
260
|
+
settled: number;
|
|
261
|
+
brierAgent: number | null;
|
|
262
|
+
brierMarket: number | null;
|
|
263
|
+
meanForecastPct: number | null;
|
|
264
|
+
winRatePct: number | null;
|
|
265
|
+
bands: Array<{
|
|
266
|
+
fromPct: number;
|
|
267
|
+
toPct: number;
|
|
268
|
+
n: number;
|
|
269
|
+
meanForecastPct: number;
|
|
270
|
+
winRatePct: number;
|
|
271
|
+
}>;
|
|
272
|
+
}
|
|
273
|
+
export interface PmSettlementRule {
|
|
274
|
+
published: boolean;
|
|
275
|
+
text?: string;
|
|
276
|
+
sources?: string[];
|
|
277
|
+
scope?: "per_outcome";
|
|
278
|
+
}
|
|
279
|
+
export type PmOutcomeRule = {
|
|
280
|
+
primary: string;
|
|
281
|
+
secondary?: string;
|
|
282
|
+
} | {
|
|
283
|
+
unknown: "source_rules_unavailable" | "market_not_found" | "rule_missing" | "conflicting_duplicates" | "unreadable";
|
|
284
|
+
};
|
|
285
|
+
export interface PmConsensus {
|
|
286
|
+
prob: number;
|
|
287
|
+
venues: number;
|
|
288
|
+
spreadPts: number;
|
|
289
|
+
kind: "binary" | "leader";
|
|
290
|
+
outcome: string | null;
|
|
250
291
|
}
|
|
251
292
|
export interface PmQuality {
|
|
252
293
|
decisionEligible?: boolean;
|
|
@@ -282,6 +323,78 @@ export interface NewsItem {
|
|
|
282
323
|
publishedAt?: string;
|
|
283
324
|
coins?: string[];
|
|
284
325
|
}
|
|
326
|
+
export type WhaleContextStatus = "available" | "partial" | "unavailable";
|
|
327
|
+
export type WhaleContextFailure = "timeout" | "network_error" | "http_error" | "invalid_payload";
|
|
328
|
+
export interface WhaleTradeContext {
|
|
329
|
+
source: string;
|
|
330
|
+
eventSlug: string;
|
|
331
|
+
eventTitle?: string;
|
|
332
|
+
marketQuestion?: string;
|
|
333
|
+
side: string;
|
|
334
|
+
outcome?: string;
|
|
335
|
+
usdValue?: number;
|
|
336
|
+
price?: number;
|
|
337
|
+
sourceMarketRef?: string;
|
|
338
|
+
valueBasis?: string;
|
|
339
|
+
evidenceType?: string;
|
|
340
|
+
evidenceRef?: string;
|
|
341
|
+
nativeValue?: number;
|
|
342
|
+
nativeCurrency?: string;
|
|
343
|
+
availability?: "live" | "delayed" | "unavailable";
|
|
344
|
+
tradedAt?: string;
|
|
345
|
+
observedAt?: string;
|
|
346
|
+
latencySeconds?: number;
|
|
347
|
+
walletAddress?: string;
|
|
348
|
+
}
|
|
349
|
+
export interface WhaleWalletFillContext {
|
|
350
|
+
eventSlug?: string;
|
|
351
|
+
eventTitle?: string;
|
|
352
|
+
marketQuestion?: string;
|
|
353
|
+
side?: string;
|
|
354
|
+
outcome?: string;
|
|
355
|
+
usdValue?: number;
|
|
356
|
+
price?: number;
|
|
357
|
+
evidenceType?: string;
|
|
358
|
+
tradedAt?: string;
|
|
359
|
+
sourceMarketRef?: string;
|
|
360
|
+
}
|
|
361
|
+
export interface WhaleWalletContext {
|
|
362
|
+
source: string;
|
|
363
|
+
address: string;
|
|
364
|
+
asOf?: string;
|
|
365
|
+
summary30d?: {
|
|
366
|
+
basis?: string;
|
|
367
|
+
windowStart?: string;
|
|
368
|
+
tradeCount?: number;
|
|
369
|
+
notionalUsd?: number;
|
|
370
|
+
buyNotionalUsd?: number;
|
|
371
|
+
sellNotionalUsd?: number;
|
|
372
|
+
};
|
|
373
|
+
rollup?: {
|
|
374
|
+
available?: boolean;
|
|
375
|
+
coveredDays?: number;
|
|
376
|
+
from?: string;
|
|
377
|
+
to?: string;
|
|
378
|
+
computedAt?: string;
|
|
379
|
+
};
|
|
380
|
+
daily: Array<{
|
|
381
|
+
day?: string;
|
|
382
|
+
tradeCount?: number;
|
|
383
|
+
buyCount?: number;
|
|
384
|
+
sellCount?: number;
|
|
385
|
+
notionalUsd?: number;
|
|
386
|
+
}>;
|
|
387
|
+
recentFills: WhaleWalletFillContext[];
|
|
388
|
+
}
|
|
389
|
+
export interface WhaleContext {
|
|
390
|
+
status: WhaleContextStatus;
|
|
391
|
+
fetchedAt: string;
|
|
392
|
+
coverage: "relevant_events" | "no_relevant_events";
|
|
393
|
+
trades: WhaleTradeContext[];
|
|
394
|
+
wallets: WhaleWalletContext[];
|
|
395
|
+
omitted: number;
|
|
396
|
+
reason?: WhaleContextFailure;
|
|
397
|
+
}
|
|
285
398
|
export interface Observation {
|
|
286
399
|
asOf: string;
|
|
287
400
|
scopes: string[];
|
|
@@ -293,16 +406,19 @@ export interface Observation {
|
|
|
293
406
|
pmPositions: PmPosition[];
|
|
294
407
|
pmResolutions: PmResolution[];
|
|
295
408
|
pmMarkets: PmMarket[];
|
|
409
|
+
pmCalibration?: PmCalibration;
|
|
296
410
|
watch: WatchEntry[];
|
|
297
411
|
setups: SetupSignal[];
|
|
298
412
|
marketMood?: {
|
|
299
413
|
fearGreed: number;
|
|
300
414
|
label: string;
|
|
415
|
+
fetchedAt?: string;
|
|
301
416
|
};
|
|
302
417
|
syncCursor: string | null;
|
|
303
418
|
newClosedTrades: Array<Record<string, unknown>>;
|
|
304
419
|
polledBeforeWrite: boolean;
|
|
305
420
|
news?: NewsItem[];
|
|
421
|
+
whaleContext?: WhaleContext;
|
|
306
422
|
universeMovers?: Array<{
|
|
307
423
|
symbol: string;
|
|
308
424
|
name?: string;
|
package/dist/agent/types.js
CHANGED
|
@@ -50,6 +50,9 @@ export const OBJECTIVE_PRIMARIES = [
|
|
|
50
50
|
// slice. `websearch` = external lookups (an injection surface + a cost — it can
|
|
51
51
|
// inform reasoning but NEVER widen a cap, since caps live in the runner);
|
|
52
52
|
// `indicators` = runner-computed RSI/MACD/etc. fed into the observation.
|
|
53
|
+
// `whale_context` reads bounded public prediction-market whale context only
|
|
54
|
+
// after the deterministic gate fires, and only when PM is in the venue scope.
|
|
55
|
+
// It never widens the candidate set or acts as a trading signal by itself.
|
|
53
56
|
// `universe_scan` (2026-08-18, direct user request): each cycle the runner
|
|
54
57
|
// pulls the top 24h movers across CoinRithm's tracked coin universe, resolves
|
|
55
58
|
// the top few into FULL watch entries (price, sentiment, indicators when that
|
|
@@ -64,6 +67,7 @@ export const ALLOWED_CAPABILITIES = [
|
|
|
64
67
|
"indicators",
|
|
65
68
|
"news",
|
|
66
69
|
"universe_scan",
|
|
70
|
+
"whale_context",
|
|
67
71
|
];
|
|
68
72
|
export const ok = () => ({ valid: true });
|
|
69
73
|
export const fail = (code, reason) => ({
|
package/dist/client.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export type ProvenanceReport = {
|
|
|
39
39
|
sourceCapturedAt?: string;
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
+
export declare const API_KEY_MINT_URL = "https://www.coinrithm.com/en/settings/api-keys";
|
|
42
43
|
export declare class CoinRithmClient {
|
|
43
44
|
private readonly defaultApiKey?;
|
|
44
45
|
private readonly baseUrl;
|
|
@@ -113,6 +114,12 @@ export declare class CoinRithmClient {
|
|
|
113
114
|
range?: "1H" | "1D" | "1W" | "1M" | "3M";
|
|
114
115
|
fiat?: string;
|
|
115
116
|
}, apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
|
|
117
|
+
getNews(query: {
|
|
118
|
+
coins: string;
|
|
119
|
+
limit?: number;
|
|
120
|
+
hours?: number;
|
|
121
|
+
minImportance?: number;
|
|
122
|
+
}, apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
|
|
116
123
|
discoverPmMarkets(query?: {
|
|
117
124
|
q?: string;
|
|
118
125
|
source?: "all" | "kalshi" | "polymarket";
|
|
@@ -145,8 +152,8 @@ export declare class CoinRithmClient {
|
|
|
145
152
|
page?: number;
|
|
146
153
|
pageSize?: number;
|
|
147
154
|
window?: "7d" | "30d" | "all";
|
|
148
|
-
}
|
|
149
|
-
getArenaAgent(handle: string
|
|
155
|
+
}): Promise<ApiResult>;
|
|
156
|
+
getArenaAgent(handle: string): Promise<ApiResult>;
|
|
150
157
|
listOpenOrders(query?: {
|
|
151
158
|
coinId?: string;
|
|
152
159
|
limit?: number;
|