@coinrithm/mcp-trading 0.7.14 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +811 -613
- package/README.md +415 -366
- package/dist/agent/bench/bench.d.ts +59 -0
- package/dist/agent/bench/bench.js +319 -0
- package/dist/agent/bench/cassette.d.ts +97 -0
- package/dist/agent/bench/cassette.js +181 -0
- package/dist/agent/bench/costs.d.ts +28 -0
- package/dist/agent/bench/costs.js +60 -0
- package/dist/agent/bench/labelBuilder.d.ts +123 -0
- package/dist/agent/bench/labelBuilder.js +282 -0
- package/dist/agent/bench/labels.d.ts +75 -0
- package/dist/agent/bench/labels.js +259 -0
- package/dist/agent/bench/recordingClient.d.ts +51 -0
- package/dist/agent/bench/recordingClient.js +193 -0
- package/dist/agent/bench/replayClient.d.ts +41 -0
- package/dist/agent/bench/replayClient.js +262 -0
- package/dist/agent/bench/report.d.ts +115 -0
- package/dist/agent/bench/report.js +435 -0
- package/dist/agent/cli.d.ts +25 -0
- package/dist/agent/cli.js +300 -2
- package/dist/agent/client.d.ts +14 -0
- package/dist/agent/client.js +113 -4
- package/dist/agent/configurationWarnings.js +11 -0
- package/dist/agent/decision.d.ts +3 -0
- package/dist/agent/decision.js +85 -3
- package/dist/agent/decisionReceipt.d.ts +5 -1
- package/dist/agent/decisionReceipt.js +52 -3
- package/dist/agent/decisionValidator.d.ts +30 -0
- package/dist/agent/decisionValidator.js +147 -2
- package/dist/agent/engine.d.ts +3 -3
- package/dist/agent/engine.js +3 -3
- package/dist/agent/futuresEligibility.d.ts +36 -0
- package/dist/agent/futuresEligibility.js +90 -0
- package/dist/agent/observe.d.ts +35 -2
- package/dist/agent/observe.js +969 -128
- package/dist/agent/pmContext.d.ts +10 -1
- package/dist/agent/pmContext.js +163 -1
- package/dist/agent/prompt.d.ts +18 -1
- package/dist/agent/prompt.js +229 -33
- package/dist/agent/promptTables.d.ts +1 -0
- package/dist/agent/promptTables.js +33 -0
- package/dist/agent/providerCapabilities.d.ts +9 -0
- package/dist/agent/providerCapabilities.js +57 -4
- package/dist/agent/providers.d.ts +28 -7
- package/dist/agent/providers.js +200 -50
- package/dist/agent/resolve.js +3 -0
- package/dist/agent/runner.d.ts +8 -0
- package/dist/agent/runner.js +159 -26
- package/dist/agent/setups.d.ts +2 -1
- package/dist/agent/setups.js +17 -16
- package/dist/agent/signals.d.ts +33 -0
- package/dist/agent/signals.js +89 -0
- package/dist/agent/skill.js +37 -0
- package/dist/agent/skillValidator.js +33 -0
- package/dist/agent/strictLint.js +29 -0
- package/dist/agent/templates.js +30 -30
- package/dist/agent/types.d.ts +266 -13
- package/dist/agent/types.js +4 -0
- package/dist/agent/universe.d.ts +78 -0
- package/dist/agent/universe.js +215 -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 +98 -97
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { fail, VENUES, PROVIDERS, SPEC_VERSION, OBJECTIVE_PRIMARIES, ALLOWED_CAPABILITIES, } from "./types.js";
|
|
2
|
+
import { universeIssues } from "./universe.js";
|
|
2
3
|
import { parseCadenceMs, scanForSecrets } from "./util.js";
|
|
4
|
+
import { signalIssues } from "./signals.js";
|
|
3
5
|
import { entryPredicateIssues } from "./entryPredicates.js";
|
|
4
6
|
const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5
7
|
const isPosNum = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
@@ -88,6 +90,12 @@ export function validateSkill(parsed, mode = "self-host") {
|
|
|
88
90
|
add("skill_venue_unknown", `unknown venue "${String(v)}" (allowed: ${VENUES.join(", ")})`);
|
|
89
91
|
}
|
|
90
92
|
}
|
|
93
|
+
// Signal thresholds and data diet (optional).
|
|
94
|
+
for (const [code, reason] of signalIssues(raw.signals, raw.data))
|
|
95
|
+
add(code, reason);
|
|
96
|
+
// Market boundaries (optional).
|
|
97
|
+
for (const [code, reason] of universeIssues(raw.universe))
|
|
98
|
+
add(code, reason);
|
|
91
99
|
// Risk — always required
|
|
92
100
|
if (!isObj(raw.risk)) {
|
|
93
101
|
add("skill_risk", "risk block is required (the caps the agent runs under)");
|
|
@@ -115,6 +123,31 @@ export function validateSkill(parsed, mode = "self-host") {
|
|
|
115
123
|
if (typeof f !== "number" || !Number.isFinite(f) || f < 0 || f > 100)
|
|
116
124
|
add("skill_risk_pm_entry_floor", "risk.pmMinEntryProbabilityPct must be a number between 0 and 100 (points; omit for no floor)");
|
|
117
125
|
}
|
|
126
|
+
if (r.pmMinEdgeGapPct !== undefined) {
|
|
127
|
+
const g = r.pmMinEdgeGapPct;
|
|
128
|
+
if (typeof g !== "number" || !Number.isFinite(g) || g < 0 || g > 100)
|
|
129
|
+
add("skill_risk_pm_edge_gap", "risk.pmMinEdgeGapPct must be a number between 0 and 100 (% of the gap to 100; omit for the global minimum edge only)");
|
|
130
|
+
}
|
|
131
|
+
if (r.pmMaxEdgePoints !== undefined) {
|
|
132
|
+
const m = r.pmMaxEdgePoints;
|
|
133
|
+
if (typeof m !== "number" || !Number.isFinite(m) || m < 0 || m > 100)
|
|
134
|
+
add("skill_risk_pm_max_edge", "risk.pmMaxEdgePoints must be a number between 0 and 100 (points over cost; omit for no cap)");
|
|
135
|
+
}
|
|
136
|
+
if (r.pmMaxOpenPerEvent !== undefined) {
|
|
137
|
+
const n = r.pmMaxOpenPerEvent;
|
|
138
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1 || n > 50)
|
|
139
|
+
add("skill_risk_pm_per_event", "risk.pmMaxOpenPerEvent must be a whole number between 1 and 50 (omit for no per-event cap)");
|
|
140
|
+
}
|
|
141
|
+
if (r.pmMaxOpenPerCoinHorizon !== undefined) {
|
|
142
|
+
const n = r.pmMaxOpenPerCoinHorizon;
|
|
143
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1 || n > 50)
|
|
144
|
+
add("skill_risk_pm_coin_horizon", "risk.pmMaxOpenPerCoinHorizon must be a whole number between 1 and 50 (omit for no per-coin-horizon cap)");
|
|
145
|
+
}
|
|
146
|
+
if (r.pmMinMinutesToClose !== undefined) {
|
|
147
|
+
const m = r.pmMinMinutesToClose;
|
|
148
|
+
if (typeof m !== "number" || !Number.isFinite(m) || m < 0 || m > 10_080)
|
|
149
|
+
add("skill_risk_pm_close_cutoff", "risk.pmMinMinutesToClose must be a number of minutes between 0 and 10080 (omit for no cutoff)");
|
|
150
|
+
}
|
|
118
151
|
// Fail-closed on the side restriction: a typo ("shorts_only") must never
|
|
119
152
|
// silently mean "unrestricted" — that is exactly how a prose-only
|
|
120
153
|
// constraint failed live on 2026-08-24.
|
package/dist/agent/strictLint.js
CHANGED
|
@@ -30,8 +30,29 @@ const ALLOWED_KEYS = {
|
|
|
30
30
|
// it) but was missing here, so any bundle actually SETTING it got an
|
|
31
31
|
// unknown_key lint — the knob existed and was unreachable (audit rank 10).
|
|
32
32
|
"triggerPolicy",
|
|
33
|
+
"signals",
|
|
34
|
+
"data",
|
|
35
|
+
"universe",
|
|
33
36
|
],
|
|
34
37
|
trigger: ["cadence", "timezone", "events"],
|
|
38
|
+
signals: [
|
|
39
|
+
"rsiOversold",
|
|
40
|
+
"rsiOverbought",
|
|
41
|
+
"strongMovePct",
|
|
42
|
+
"leanMovePct",
|
|
43
|
+
"minStrength",
|
|
44
|
+
],
|
|
45
|
+
data: ["indicatorRange"],
|
|
46
|
+
universe: [
|
|
47
|
+
"rank",
|
|
48
|
+
"minVolume24hUsd",
|
|
49
|
+
"excludeStablecoins",
|
|
50
|
+
"includeSectors",
|
|
51
|
+
"excludeSectors",
|
|
52
|
+
"sort",
|
|
53
|
+
"resolveTop",
|
|
54
|
+
"scanLimit",
|
|
55
|
+
],
|
|
35
56
|
triggerPolicy: [
|
|
36
57
|
"mode",
|
|
37
58
|
"skipLlmWhenNoTrigger",
|
|
@@ -51,6 +72,11 @@ const ALLOWED_KEYS = {
|
|
|
51
72
|
"direction",
|
|
52
73
|
"entryPredicates",
|
|
53
74
|
"pmMinEntryProbabilityPct",
|
|
75
|
+
"pmMinEdgeGapPct",
|
|
76
|
+
"pmMaxEdgePoints",
|
|
77
|
+
"pmMaxOpenPerEvent",
|
|
78
|
+
"pmMaxOpenPerCoinHorizon",
|
|
79
|
+
"pmMinMinutesToClose",
|
|
54
80
|
],
|
|
55
81
|
sizing: null,
|
|
56
82
|
capitalSizing: [
|
|
@@ -141,6 +167,9 @@ export function strictLint(raw) {
|
|
|
141
167
|
"sync",
|
|
142
168
|
"killSwitch",
|
|
143
169
|
"objective",
|
|
170
|
+
"signals",
|
|
171
|
+
"data",
|
|
172
|
+
"universe",
|
|
144
173
|
]) {
|
|
145
174
|
if (isObj(raw[block]))
|
|
146
175
|
lintKeys(block, raw[block], issues);
|
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,5 +1,7 @@
|
|
|
1
|
+
import type { FuturesEntryEligibility } from "./futuresEligibility.js";
|
|
1
2
|
import { IndicatorSet } from "./indicators.js";
|
|
2
3
|
import type { DecisionInputRecord } from "./decisionReceipt.js";
|
|
4
|
+
import type { DecideRouteAttempt } from "./providers.js";
|
|
3
5
|
export declare const SPEC_VERSION = "coinrithm.agent.v1";
|
|
4
6
|
export type Venue = "spot" | "futures" | "pm";
|
|
5
7
|
export declare const VENUES: readonly Venue[];
|
|
@@ -35,6 +37,11 @@ export interface RiskConfig {
|
|
|
35
37
|
blocklist?: string[];
|
|
36
38
|
direction?: "long_only" | "short_only";
|
|
37
39
|
pmMinEntryProbabilityPct?: number;
|
|
40
|
+
pmMinEdgeGapPct?: number;
|
|
41
|
+
pmMaxEdgePoints?: number;
|
|
42
|
+
pmMaxOpenPerEvent?: number;
|
|
43
|
+
pmMaxOpenPerCoinHorizon?: number;
|
|
44
|
+
pmMinMinutesToClose?: number;
|
|
38
45
|
}
|
|
39
46
|
export interface LimitsConfig {
|
|
40
47
|
maxTradesPerDay: number;
|
|
@@ -97,7 +104,7 @@ export interface ObjectiveConfig {
|
|
|
97
104
|
secondary: string[];
|
|
98
105
|
horizon?: string;
|
|
99
106
|
}
|
|
100
|
-
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan"];
|
|
107
|
+
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news", "universe_scan", "whale_context"];
|
|
101
108
|
export type Capability = (typeof ALLOWED_CAPABILITIES)[number];
|
|
102
109
|
export interface AgentSpec {
|
|
103
110
|
name: string;
|
|
@@ -115,6 +122,25 @@ export interface AgentSpec {
|
|
|
115
122
|
objective?: ObjectiveConfig;
|
|
116
123
|
capabilities: Capability[];
|
|
117
124
|
triggerPolicy?: TriggerPolicy;
|
|
125
|
+
signals?: Partial<import("./signals.js").SignalThresholds>;
|
|
126
|
+
data?: {
|
|
127
|
+
indicatorRange?: import("./signals.js").IndicatorRange;
|
|
128
|
+
};
|
|
129
|
+
universe?: UniverseConfig;
|
|
130
|
+
}
|
|
131
|
+
/** Declared market boundaries; see universe.ts for semantics and defaults. */
|
|
132
|
+
export interface UniverseConfig {
|
|
133
|
+
rank?: {
|
|
134
|
+
min?: number;
|
|
135
|
+
max?: number;
|
|
136
|
+
};
|
|
137
|
+
minVolume24hUsd?: number;
|
|
138
|
+
excludeStablecoins?: boolean;
|
|
139
|
+
includeSectors?: string[];
|
|
140
|
+
excludeSectors?: string[];
|
|
141
|
+
sort?: "gainers_24h" | "losers_24h" | "abs_change_24h" | "abs_change_1h" | "volume_24h" | "rank";
|
|
142
|
+
resolveTop?: number;
|
|
143
|
+
scanLimit?: number;
|
|
118
144
|
}
|
|
119
145
|
export interface ParsedSkill {
|
|
120
146
|
spec: AgentSpec;
|
|
@@ -135,8 +161,8 @@ export interface Freshness {
|
|
|
135
161
|
basis?: string;
|
|
136
162
|
}
|
|
137
163
|
export interface IndicatorContext {
|
|
138
|
-
range: "
|
|
139
|
-
nominalIntervalSeconds:
|
|
164
|
+
range: import("./signals.js").IndicatorRange;
|
|
165
|
+
nominalIntervalSeconds: number;
|
|
140
166
|
barCount: number;
|
|
141
167
|
timestampedBarCount: number;
|
|
142
168
|
checkedIntervalCount: number;
|
|
@@ -149,6 +175,105 @@ export interface IndicatorContext {
|
|
|
149
175
|
intervalStatus: "regular" | "irregular" | "unknown";
|
|
150
176
|
};
|
|
151
177
|
}
|
|
178
|
+
export interface OpenInterestContext {
|
|
179
|
+
totalUsd: number;
|
|
180
|
+
venues: string[];
|
|
181
|
+
change1hPct: number | null;
|
|
182
|
+
change1hVenues: string[];
|
|
183
|
+
change24hPct: number | null;
|
|
184
|
+
change24hVenues: string[];
|
|
185
|
+
asOf: string;
|
|
186
|
+
stale: boolean;
|
|
187
|
+
}
|
|
188
|
+
export interface DatedValue {
|
|
189
|
+
value: number;
|
|
190
|
+
asOf: string;
|
|
191
|
+
stale: boolean;
|
|
192
|
+
}
|
|
193
|
+
export interface PositioningContext {
|
|
194
|
+
venue: string;
|
|
195
|
+
symbol: string;
|
|
196
|
+
longShortAccountRatio?: DatedValue;
|
|
197
|
+
longAccountPct?: DatedValue;
|
|
198
|
+
topTraderPositionRatio?: DatedValue;
|
|
199
|
+
takerBuySellRatio?: DatedValue;
|
|
200
|
+
}
|
|
201
|
+
export interface LiquidationWindow {
|
|
202
|
+
longLiquidatedUsdt: number;
|
|
203
|
+
shortLiquidatedUsdt: number;
|
|
204
|
+
events: number;
|
|
205
|
+
capturedPct: number;
|
|
206
|
+
}
|
|
207
|
+
export interface LiquidationContext {
|
|
208
|
+
venue: string;
|
|
209
|
+
instId: string;
|
|
210
|
+
last1h: LiquidationWindow;
|
|
211
|
+
last24h: LiquidationWindow;
|
|
212
|
+
lastEventAt: string | null;
|
|
213
|
+
}
|
|
214
|
+
export interface MacroQuote {
|
|
215
|
+
symbol: string;
|
|
216
|
+
label: string;
|
|
217
|
+
kind: string;
|
|
218
|
+
price: number;
|
|
219
|
+
change24hPct: number | null;
|
|
220
|
+
asOf: string;
|
|
221
|
+
stale: boolean;
|
|
222
|
+
}
|
|
223
|
+
export interface MacroContext {
|
|
224
|
+
note: string;
|
|
225
|
+
quotes: MacroQuote[];
|
|
226
|
+
}
|
|
227
|
+
export interface ChainTvlContext {
|
|
228
|
+
chain: string;
|
|
229
|
+
tvlUsd: number;
|
|
230
|
+
publishedAt: string | null;
|
|
231
|
+
fetchedAt: string | null;
|
|
232
|
+
sourceObservedAt: null;
|
|
233
|
+
stale: boolean;
|
|
234
|
+
dayAt: string | null;
|
|
235
|
+
change1dPct: number | null;
|
|
236
|
+
change7dPct: number | null;
|
|
237
|
+
}
|
|
238
|
+
export interface StablecoinSupplyContext {
|
|
239
|
+
totalUsd: number;
|
|
240
|
+
dayAt: string;
|
|
241
|
+
change1dPct: number | null;
|
|
242
|
+
change7dPct: number | null;
|
|
243
|
+
stale: boolean;
|
|
244
|
+
}
|
|
245
|
+
export interface DepthSideContext {
|
|
246
|
+
usd: number;
|
|
247
|
+
reachPct: number;
|
|
248
|
+
levels: number;
|
|
249
|
+
complete: boolean;
|
|
250
|
+
}
|
|
251
|
+
export interface DepthContext {
|
|
252
|
+
venue: string;
|
|
253
|
+
bid: DepthSideContext | null;
|
|
254
|
+
ask: DepthSideContext | null;
|
|
255
|
+
asOf: string;
|
|
256
|
+
stale: boolean;
|
|
257
|
+
}
|
|
258
|
+
export interface FundingVenueRate {
|
|
259
|
+
role: "settlement_reference" | "context_only";
|
|
260
|
+
venue: string;
|
|
261
|
+
symbol: string;
|
|
262
|
+
source: "paper_futures_reference" | "hyperliquid_predicted_fundings";
|
|
263
|
+
rateFraction: number;
|
|
264
|
+
intervalHours: number | null;
|
|
265
|
+
hourlyEquivalentFraction: number | null;
|
|
266
|
+
nextFundingTime: string | null;
|
|
267
|
+
fetchedAt: string;
|
|
268
|
+
sourceAt: null;
|
|
269
|
+
ageSeconds: number;
|
|
270
|
+
stale: boolean;
|
|
271
|
+
freshnessBasis: "collection_time";
|
|
272
|
+
}
|
|
273
|
+
export interface FundingByVenueContext {
|
|
274
|
+
rates: FundingVenueRate[];
|
|
275
|
+
sameTime: false;
|
|
276
|
+
}
|
|
152
277
|
export interface WatchEntry {
|
|
153
278
|
symbol: string;
|
|
154
279
|
coinId: string | null;
|
|
@@ -158,12 +283,23 @@ export interface WatchEntry {
|
|
|
158
283
|
change24h?: number;
|
|
159
284
|
change7d?: number;
|
|
160
285
|
sentimentBullishPct?: number;
|
|
286
|
+
sentimentTotalVotes?: number;
|
|
287
|
+
sentimentDayUtc?: string;
|
|
288
|
+
sentimentUpdatedAt?: string;
|
|
161
289
|
freshness?: Freshness;
|
|
162
290
|
indicators?: IndicatorSet;
|
|
163
291
|
indicatorContext?: IndicatorContext;
|
|
164
292
|
discovered?: boolean;
|
|
293
|
+
withinBoundaries?: boolean;
|
|
165
294
|
slug?: string;
|
|
166
295
|
fundamentals?: CoinFundamentals;
|
|
296
|
+
openInterest?: OpenInterestContext;
|
|
297
|
+
positioning?: PositioningContext;
|
|
298
|
+
liquidations?: LiquidationContext;
|
|
299
|
+
chainTvl?: ChainTvlContext;
|
|
300
|
+
depth?: DepthContext;
|
|
301
|
+
fundingByVenue?: FundingByVenueContext;
|
|
302
|
+
futuresEntryEligibility?: FuturesEntryEligibility;
|
|
167
303
|
}
|
|
168
304
|
export interface CoinFundamentals {
|
|
169
305
|
categories?: string[];
|
|
@@ -222,6 +358,8 @@ export interface PmPosition {
|
|
|
222
358
|
entryProbability?: number;
|
|
223
359
|
currentProbability?: number;
|
|
224
360
|
openedAt?: string;
|
|
361
|
+
coins?: string[];
|
|
362
|
+
endDate?: string;
|
|
225
363
|
thesis?: ThesisView;
|
|
226
364
|
}
|
|
227
365
|
export interface PmResolution {
|
|
@@ -245,8 +383,45 @@ export interface PmMarket {
|
|
|
245
383
|
volumeUsd?: number;
|
|
246
384
|
endDate?: string;
|
|
247
385
|
liquidityUsd?: number;
|
|
386
|
+
coins?: string[];
|
|
248
387
|
quality?: PmQuality;
|
|
249
388
|
decisionSupport?: PmDecisionSupport;
|
|
389
|
+
consensus?: PmConsensus | null;
|
|
390
|
+
rules?: PmSettlementRule | null;
|
|
391
|
+
outcomeRule?: PmOutcomeRule;
|
|
392
|
+
}
|
|
393
|
+
export interface PmCalibration {
|
|
394
|
+
settled: number;
|
|
395
|
+
brierAgent: number | null;
|
|
396
|
+
brierMarket: number | null;
|
|
397
|
+
meanForecastPct: number | null;
|
|
398
|
+
winRatePct: number | null;
|
|
399
|
+
bands: Array<{
|
|
400
|
+
fromPct: number;
|
|
401
|
+
toPct: number;
|
|
402
|
+
n: number;
|
|
403
|
+
meanForecastPct: number;
|
|
404
|
+
winRatePct: number;
|
|
405
|
+
}>;
|
|
406
|
+
}
|
|
407
|
+
export interface PmSettlementRule {
|
|
408
|
+
published: boolean;
|
|
409
|
+
text?: string;
|
|
410
|
+
sources?: string[];
|
|
411
|
+
scope?: "per_outcome";
|
|
412
|
+
}
|
|
413
|
+
export type PmOutcomeRule = {
|
|
414
|
+
primary: string;
|
|
415
|
+
secondary?: string;
|
|
416
|
+
} | {
|
|
417
|
+
unknown: "source_rules_unavailable" | "market_not_found" | "rule_missing" | "conflicting_duplicates" | "unreadable";
|
|
418
|
+
};
|
|
419
|
+
export interface PmConsensus {
|
|
420
|
+
prob: number;
|
|
421
|
+
venues: number;
|
|
422
|
+
spreadPts: number;
|
|
423
|
+
kind: "binary" | "leader";
|
|
424
|
+
outcome: string | null;
|
|
250
425
|
}
|
|
251
426
|
export interface PmQuality {
|
|
252
427
|
decisionEligible?: boolean;
|
|
@@ -281,6 +456,80 @@ export interface NewsItem {
|
|
|
281
456
|
ageHours?: number;
|
|
282
457
|
publishedAt?: string;
|
|
283
458
|
coins?: string[];
|
|
459
|
+
url?: string;
|
|
460
|
+
category?: string;
|
|
461
|
+
}
|
|
462
|
+
export type WhaleContextStatus = "available" | "partial" | "unavailable";
|
|
463
|
+
export type WhaleContextFailure = "timeout" | "network_error" | "http_error" | "invalid_payload";
|
|
464
|
+
export interface WhaleTradeContext {
|
|
465
|
+
source: string;
|
|
466
|
+
eventSlug: string;
|
|
467
|
+
eventTitle?: string;
|
|
468
|
+
marketQuestion?: string;
|
|
469
|
+
side: string;
|
|
470
|
+
outcome?: string;
|
|
471
|
+
usdValue?: number;
|
|
472
|
+
price?: number;
|
|
473
|
+
sourceMarketRef?: string;
|
|
474
|
+
valueBasis?: string;
|
|
475
|
+
evidenceType?: string;
|
|
476
|
+
evidenceRef?: string;
|
|
477
|
+
nativeValue?: number;
|
|
478
|
+
nativeCurrency?: string;
|
|
479
|
+
availability?: "live" | "delayed" | "unavailable";
|
|
480
|
+
tradedAt?: string;
|
|
481
|
+
observedAt?: string;
|
|
482
|
+
latencySeconds?: number;
|
|
483
|
+
walletAddress?: string;
|
|
484
|
+
}
|
|
485
|
+
export interface WhaleWalletFillContext {
|
|
486
|
+
eventSlug?: string;
|
|
487
|
+
eventTitle?: string;
|
|
488
|
+
marketQuestion?: string;
|
|
489
|
+
side?: string;
|
|
490
|
+
outcome?: string;
|
|
491
|
+
usdValue?: number;
|
|
492
|
+
price?: number;
|
|
493
|
+
evidenceType?: string;
|
|
494
|
+
tradedAt?: string;
|
|
495
|
+
sourceMarketRef?: string;
|
|
496
|
+
}
|
|
497
|
+
export interface WhaleWalletContext {
|
|
498
|
+
source: string;
|
|
499
|
+
address: string;
|
|
500
|
+
asOf?: string;
|
|
501
|
+
summary30d?: {
|
|
502
|
+
basis?: string;
|
|
503
|
+
windowStart?: string;
|
|
504
|
+
tradeCount?: number;
|
|
505
|
+
notionalUsd?: number;
|
|
506
|
+
buyNotionalUsd?: number;
|
|
507
|
+
sellNotionalUsd?: number;
|
|
508
|
+
};
|
|
509
|
+
rollup?: {
|
|
510
|
+
available?: boolean;
|
|
511
|
+
coveredDays?: number;
|
|
512
|
+
from?: string;
|
|
513
|
+
to?: string;
|
|
514
|
+
computedAt?: string;
|
|
515
|
+
};
|
|
516
|
+
daily: Array<{
|
|
517
|
+
day?: string;
|
|
518
|
+
tradeCount?: number;
|
|
519
|
+
buyCount?: number;
|
|
520
|
+
sellCount?: number;
|
|
521
|
+
notionalUsd?: number;
|
|
522
|
+
}>;
|
|
523
|
+
recentFills: WhaleWalletFillContext[];
|
|
524
|
+
}
|
|
525
|
+
export interface WhaleContext {
|
|
526
|
+
status: WhaleContextStatus;
|
|
527
|
+
fetchedAt: string;
|
|
528
|
+
coverage: "relevant_events" | "no_relevant_events";
|
|
529
|
+
trades: WhaleTradeContext[];
|
|
530
|
+
wallets: WhaleWalletContext[];
|
|
531
|
+
omitted: number;
|
|
532
|
+
reason?: WhaleContextFailure;
|
|
284
533
|
}
|
|
285
534
|
export interface Observation {
|
|
286
535
|
asOf: string;
|
|
@@ -293,16 +542,21 @@ export interface Observation {
|
|
|
293
542
|
pmPositions: PmPosition[];
|
|
294
543
|
pmResolutions: PmResolution[];
|
|
295
544
|
pmMarkets: PmMarket[];
|
|
545
|
+
pmCalibration?: PmCalibration;
|
|
296
546
|
watch: WatchEntry[];
|
|
297
547
|
setups: SetupSignal[];
|
|
298
548
|
marketMood?: {
|
|
299
549
|
fearGreed: number;
|
|
300
550
|
label: string;
|
|
551
|
+
fetchedAt?: string;
|
|
301
552
|
};
|
|
553
|
+
macro?: MacroContext;
|
|
554
|
+
stablecoinSupply?: StablecoinSupplyContext;
|
|
302
555
|
syncCursor: string | null;
|
|
303
556
|
newClosedTrades: Array<Record<string, unknown>>;
|
|
304
557
|
polledBeforeWrite: boolean;
|
|
305
558
|
news?: NewsItem[];
|
|
559
|
+
whaleContext?: WhaleContext;
|
|
306
560
|
universeMovers?: Array<{
|
|
307
561
|
symbol: string;
|
|
308
562
|
name?: string;
|
|
@@ -522,19 +776,18 @@ export interface CycleResult {
|
|
|
522
776
|
tokensIn?: number;
|
|
523
777
|
tokensOut?: number;
|
|
524
778
|
estimatedCostUsd?: number;
|
|
779
|
+
providerUsage?: {
|
|
780
|
+
promptTokens: number;
|
|
781
|
+
completionTokens: number;
|
|
782
|
+
cacheReadTokens?: number;
|
|
783
|
+
cacheWriteTokens?: number;
|
|
784
|
+
cacheWrite5mTokens?: number;
|
|
785
|
+
cacheWrite1hTokens?: number;
|
|
786
|
+
};
|
|
525
787
|
effectiveProvider?: string;
|
|
526
788
|
effectiveModel?: string;
|
|
527
789
|
routeReason?: string;
|
|
528
|
-
routeAttempts?:
|
|
529
|
-
provider: string;
|
|
530
|
-
model: string;
|
|
531
|
-
outcome: "success" | "failed" | "deferred";
|
|
532
|
-
failureClass?: "capacity" | "permanent" | "transient" | "malformed";
|
|
533
|
-
status?: number;
|
|
534
|
-
retryAfterMs?: number;
|
|
535
|
-
latencyMs: number;
|
|
536
|
-
error?: string;
|
|
537
|
-
}>;
|
|
790
|
+
routeAttempts?: DecideRouteAttempt[];
|
|
538
791
|
decisionType?: "act" | "skip" | "gate_skip" | "model_error";
|
|
539
792
|
writeAttempted?: number;
|
|
540
793
|
writeAccepted?: number;
|
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) => ({
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { UniverseConfig } from "./types.js";
|
|
2
|
+
export declare const UNIVERSE_SORTS: readonly ["gainers_24h", "losers_24h", "abs_change_24h", "abs_change_1h", "volume_24h", "rank"];
|
|
3
|
+
export type UniverseSort = (typeof UNIVERSE_SORTS)[number];
|
|
4
|
+
export declare const UNIVERSE_DEFAULTS: {
|
|
5
|
+
rankMin: number;
|
|
6
|
+
rankMax: number;
|
|
7
|
+
minVolume24hUsd: number;
|
|
8
|
+
sort: UniverseSort;
|
|
9
|
+
resolveTop: number;
|
|
10
|
+
scanLimit: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const UNIVERSE_MAX_RESOLVE_TOP = 10;
|
|
13
|
+
export declare const UNIVERSE_MAX_SCAN_LIMIT = 50;
|
|
14
|
+
/** True when the agent discovers candidates beyond its watchlist. */
|
|
15
|
+
export declare function scansUniverse(spec: {
|
|
16
|
+
capabilities: readonly string[];
|
|
17
|
+
universe?: UniverseConfig;
|
|
18
|
+
}): boolean;
|
|
19
|
+
export interface UniverseQuery {
|
|
20
|
+
rankMin: number;
|
|
21
|
+
rankMax: number;
|
|
22
|
+
minVolume24hUsd: number;
|
|
23
|
+
excludeStablecoins: boolean;
|
|
24
|
+
includeSectors: string[];
|
|
25
|
+
excludeSectors: string[];
|
|
26
|
+
sort: UniverseSort;
|
|
27
|
+
limit: number;
|
|
28
|
+
}
|
|
29
|
+
/** The screener query a validated universe block asks for. */
|
|
30
|
+
export declare function universeQuery(u: UniverseConfig): UniverseQuery;
|
|
31
|
+
/** How many screener rows become full watch entries this cycle. */
|
|
32
|
+
export declare function universeResolveTop(u: UniverseConfig): number;
|
|
33
|
+
/** Wire form: csv sectors, string numbers; empty filters are omitted. With
|
|
34
|
+
* `ucids`, the same boundaries are applied to just those coins (membership). */
|
|
35
|
+
export declare function universeQueryParams(q: UniverseQuery, ucids?: readonly string[]): Record<string, string | number>;
|
|
36
|
+
export interface UniverseRow {
|
|
37
|
+
symbol: string;
|
|
38
|
+
coinId?: string;
|
|
39
|
+
name?: string;
|
|
40
|
+
slug?: string;
|
|
41
|
+
marketCapRank?: number;
|
|
42
|
+
priceUsd?: number;
|
|
43
|
+
change1h?: number;
|
|
44
|
+
change24h?: number;
|
|
45
|
+
change7d?: number;
|
|
46
|
+
volume24hUsd?: number;
|
|
47
|
+
sectors?: string[];
|
|
48
|
+
}
|
|
49
|
+
/** Keep malformed evidence unknown; dropping bad elements can widen a screen. */
|
|
50
|
+
export declare function universeSectorsOf(raw: unknown): string[] | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Defence in depth on server rows (never widen): a row is kept only when its
|
|
53
|
+
* own fields PROVE it is inside every enabled boundary. The rank band is
|
|
54
|
+
* always enabled, so a row without a rank is dropped; a volume floor needs a
|
|
55
|
+
* volume; any sector rule (stablecoins out, sectors out, sectors in) needs the
|
|
56
|
+
* row's sector list. Missing or malformed evidence drops the row (root review
|
|
57
|
+
* 2026-10-07: unverifiable is not inside).
|
|
58
|
+
*/
|
|
59
|
+
export declare function filterUniverseRows(rows: UniverseRow[], q: UniverseQuery): UniverseRow[];
|
|
60
|
+
/**
|
|
61
|
+
* Entry eligibility under declared boundaries (root review 2026-10-07): with a
|
|
62
|
+
* `universe` block, a NEW position may only be opened on a coin proven inside
|
|
63
|
+
* the boundaries this cycle: a discovered row, or a watchlist coin the
|
|
64
|
+
* screener confirmed (`withinBoundaries: true`). Anything else (outside, or
|
|
65
|
+
* unverifiable because the check failed) stays visible as context and can
|
|
66
|
+
* still be closed or sold, but not entered. Returns the rejection reason, or
|
|
67
|
+
* null when the entry is allowed. No universe block = no extra rule.
|
|
68
|
+
*/
|
|
69
|
+
export declare function universeEntryBlock(spec: {
|
|
70
|
+
universe?: UniverseConfig;
|
|
71
|
+
}, entry: {
|
|
72
|
+
discovered?: boolean;
|
|
73
|
+
withinBoundaries?: boolean;
|
|
74
|
+
}): string | null;
|
|
75
|
+
/** Validation issues for a raw `universe` block ([] when valid or absent). */
|
|
76
|
+
export declare function universeIssues(raw: unknown): Array<[string, string]>;
|
|
77
|
+
/** One line for the prompt: the boundaries in plain words. */
|
|
78
|
+
export declare function describeUniverse(u: UniverseConfig): string;
|