@coinrithm/mcp-trading 0.4.0 → 0.7.0
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 +37 -1
- package/README.md +48 -9
- package/dist/agent/act.js +8 -1
- package/dist/agent/cli.d.ts +1 -0
- package/dist/agent/cli.js +58 -4
- package/dist/agent/client.d.ts +8 -1
- package/dist/agent/client.js +14 -2
- package/dist/agent/decision.d.ts +66 -63
- package/dist/agent/decision.js +95 -24
- package/dist/agent/decisionValidator.js +41 -1
- package/dist/agent/deploymentOverlay.d.ts +22 -0
- package/dist/agent/deploymentOverlay.js +55 -0
- package/dist/agent/gate.d.ts +9 -0
- package/dist/agent/gate.js +114 -0
- package/dist/agent/indicators.js +22 -7
- package/dist/agent/observe.js +201 -18
- package/dist/agent/prompt.d.ts +6 -2
- package/dist/agent/prompt.js +116 -26
- package/dist/agent/providers.d.ts +6 -0
- package/dist/agent/providers.js +89 -12
- package/dist/agent/resolvePm.d.ts +14 -0
- package/dist/agent/resolvePm.js +69 -0
- package/dist/agent/runner.d.ts +6 -1
- package/dist/agent/runner.js +312 -10
- package/dist/agent/setups.d.ts +3 -0
- package/dist/agent/setups.js +133 -0
- package/dist/agent/skill.d.ts +1 -0
- package/dist/agent/skill.js +21 -3
- package/dist/agent/skillValidator.js +4 -2
- package/dist/agent/state.js +10 -2
- package/dist/agent/templates.js +8 -4
- package/dist/agent/types.d.ts +75 -2
- package/dist/agent/types.js +14 -1
- package/dist/agent/version.d.ts +2 -2
- package/dist/agent/version.js +12 -2
- package/dist/client.d.ts +19 -0
- package/dist/client.js +56 -0
- package/dist/tools.js +139 -5
- package/package.json +78 -78
package/dist/agent/templates.js
CHANGED
|
@@ -62,8 +62,10 @@ money. Edit this prose freely (any language) — it is your agent's borders.
|
|
|
62
62
|
balances or what is already open.
|
|
63
63
|
2. Scan the watchlist. A candidate is a coin whose short and medium momentum
|
|
64
64
|
agree (both up, or both down) and is not already an open position.
|
|
65
|
-
3. Pick
|
|
66
|
-
|
|
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.
|
|
67
69
|
4. Quote before you open. Read the liquidation price and confirm it is sane. If
|
|
68
70
|
the quote is not eligible, relay the reason and stop.
|
|
69
71
|
5. Open small and protected: enter in the trend direction and set a stop-loss at
|
|
@@ -75,8 +77,10 @@ The hard caps (leverage, margin, watchlist) live in the config blocks above and
|
|
|
75
77
|
are enforced by the runner — change them there, not in this prose.`;
|
|
76
78
|
const PERSONA_STUB = `# Persona
|
|
77
79
|
|
|
78
|
-
|
|
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.
|
|
80
84
|
`;
|
|
81
85
|
export function buildAgentObject(name, preset) {
|
|
82
86
|
const p = PRESETS[preset];
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type Venue = "spot" | "futures" | "pm";
|
|
|
4
4
|
export declare const VENUES: readonly Venue[];
|
|
5
5
|
export declare const ACTION_TYPES: readonly ["futures_open", "futures_close", "futures_set_sltp", "spot_order", "spot_cancel", "pm_open"];
|
|
6
6
|
export type ActionType = (typeof ACTION_TYPES)[number];
|
|
7
|
-
export type ProviderName = "anthropic" | "openai" | "groq" | "nvidia" | "openai-compatible";
|
|
7
|
+
export type ProviderName = "anthropic" | "openai" | "groq" | "nvidia" | "gemini" | "openai-compatible";
|
|
8
8
|
export declare const PROVIDERS: readonly ProviderName[];
|
|
9
9
|
export interface ModelConfig {
|
|
10
10
|
provider: ProviderName;
|
|
@@ -15,6 +15,15 @@ export interface TriggerConfig {
|
|
|
15
15
|
cadence: string;
|
|
16
16
|
timezone?: string;
|
|
17
17
|
}
|
|
18
|
+
export interface TriggerPolicy {
|
|
19
|
+
mode: "event_driven" | "always";
|
|
20
|
+
skipLlmWhenNoTrigger: boolean;
|
|
21
|
+
alwaysManageOpenPositions: boolean;
|
|
22
|
+
maxLlmCallsPerHour: number;
|
|
23
|
+
debounceMinutes: number;
|
|
24
|
+
pmEvalCooldownMinutes: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_TRIGGER_POLICY: TriggerPolicy;
|
|
18
27
|
export interface RiskConfig {
|
|
19
28
|
maxLeverage: number;
|
|
20
29
|
perTradeMarginMusd: number;
|
|
@@ -52,7 +61,7 @@ export interface ObjectiveConfig {
|
|
|
52
61
|
secondary: string[];
|
|
53
62
|
horizon?: string;
|
|
54
63
|
}
|
|
55
|
-
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators"];
|
|
64
|
+
export declare const ALLOWED_CAPABILITIES: readonly ["websearch", "indicators", "news"];
|
|
56
65
|
export type Capability = (typeof ALLOWED_CAPABILITIES)[number];
|
|
57
66
|
export interface AgentSpec {
|
|
58
67
|
name: string;
|
|
@@ -68,6 +77,7 @@ export interface AgentSpec {
|
|
|
68
77
|
killSwitch: KillSwitchConfig;
|
|
69
78
|
objective?: ObjectiveConfig;
|
|
70
79
|
capabilities: Capability[];
|
|
80
|
+
triggerPolicy?: TriggerPolicy;
|
|
71
81
|
}
|
|
72
82
|
export interface ParsedSkill {
|
|
73
83
|
spec: AgentSpec;
|
|
@@ -93,6 +103,7 @@ export interface WatchEntry {
|
|
|
93
103
|
change1h?: number;
|
|
94
104
|
change24h?: number;
|
|
95
105
|
change7d?: number;
|
|
106
|
+
sentimentBullishPct?: number;
|
|
96
107
|
freshness?: Freshness;
|
|
97
108
|
indicators?: IndicatorSet;
|
|
98
109
|
}
|
|
@@ -103,8 +114,14 @@ export interface OpenPosition {
|
|
|
103
114
|
symbol?: string;
|
|
104
115
|
side?: string;
|
|
105
116
|
status?: string;
|
|
117
|
+
leverage?: number;
|
|
106
118
|
marginMusd?: number;
|
|
107
119
|
unrealizedPnlMusd?: number;
|
|
120
|
+
entryPrice?: number;
|
|
121
|
+
markPrice?: number;
|
|
122
|
+
liquidationPrice?: number;
|
|
123
|
+
stopLossPrice?: number;
|
|
124
|
+
takeProfitPrice?: number;
|
|
108
125
|
}
|
|
109
126
|
export interface SpotOrder {
|
|
110
127
|
id: number;
|
|
@@ -121,15 +138,44 @@ export interface PmPosition {
|
|
|
121
138
|
slug?: string;
|
|
122
139
|
outcomeExternalMarketId?: string;
|
|
123
140
|
stakeMusd?: number;
|
|
141
|
+
unrealizedPnlMusd?: number;
|
|
142
|
+
status?: string;
|
|
143
|
+
}
|
|
144
|
+
export interface PmResolution {
|
|
145
|
+
id: number;
|
|
146
|
+
eventTitle?: string;
|
|
147
|
+
slug?: string;
|
|
148
|
+
side?: string;
|
|
124
149
|
status?: string;
|
|
150
|
+
pnlMusd?: number;
|
|
151
|
+
stakeMusd?: number;
|
|
125
152
|
}
|
|
126
153
|
export interface PmMarket {
|
|
154
|
+
ref?: string;
|
|
127
155
|
source: string;
|
|
128
156
|
slug: string;
|
|
129
157
|
outcomeExternalMarketId: string;
|
|
158
|
+
outcomeName?: string;
|
|
159
|
+
probability?: number;
|
|
130
160
|
title?: string;
|
|
131
161
|
freshness?: Freshness;
|
|
132
162
|
}
|
|
163
|
+
export interface SetupSignal {
|
|
164
|
+
symbol: string;
|
|
165
|
+
kind: "breakout" | "breakdown" | "uptrend" | "downtrend" | "stretched";
|
|
166
|
+
bias: "long" | "short" | "fade-long" | "fade-short";
|
|
167
|
+
strength: number;
|
|
168
|
+
note: string;
|
|
169
|
+
held?: "long" | "short";
|
|
170
|
+
}
|
|
171
|
+
export interface NewsItem {
|
|
172
|
+
title: string;
|
|
173
|
+
source?: string;
|
|
174
|
+
sentiment?: string;
|
|
175
|
+
importance?: number;
|
|
176
|
+
ageHours?: number;
|
|
177
|
+
coins?: string[];
|
|
178
|
+
}
|
|
133
179
|
export interface Observation {
|
|
134
180
|
asOf: string;
|
|
135
181
|
scopes: string[];
|
|
@@ -138,11 +184,18 @@ export interface Observation {
|
|
|
138
184
|
openPositions: OpenPosition[];
|
|
139
185
|
openOrders: SpotOrder[];
|
|
140
186
|
pmPositions: PmPosition[];
|
|
187
|
+
pmResolutions: PmResolution[];
|
|
141
188
|
pmMarkets: PmMarket[];
|
|
142
189
|
watch: WatchEntry[];
|
|
190
|
+
setups: SetupSignal[];
|
|
191
|
+
marketMood?: {
|
|
192
|
+
fearGreed: number;
|
|
193
|
+
label: string;
|
|
194
|
+
};
|
|
143
195
|
syncCursor: string | null;
|
|
144
196
|
newClosedTrades: Array<Record<string, unknown>>;
|
|
145
197
|
polledBeforeWrite: boolean;
|
|
198
|
+
news?: NewsItem[];
|
|
146
199
|
}
|
|
147
200
|
export type ProposedAction = {
|
|
148
201
|
type: "futures_open";
|
|
@@ -180,6 +233,7 @@ export type ProposedAction = {
|
|
|
180
233
|
orderId: number;
|
|
181
234
|
} | {
|
|
182
235
|
type: "pm_open";
|
|
236
|
+
ref?: string;
|
|
183
237
|
source: string;
|
|
184
238
|
slug: string;
|
|
185
239
|
outcomeExternalMarketId: string;
|
|
@@ -199,6 +253,7 @@ export declare function spotBuyCost(action: Extract<ProposedAction, {
|
|
|
199
253
|
export interface Decision {
|
|
200
254
|
decision: "skip" | "act";
|
|
201
255
|
reason?: string;
|
|
256
|
+
rationale?: string;
|
|
202
257
|
confidence?: number;
|
|
203
258
|
actions: ProposedAction[];
|
|
204
259
|
}
|
|
@@ -228,6 +283,13 @@ export interface RunState {
|
|
|
228
283
|
realizedPnlTodayMusd: number;
|
|
229
284
|
consecutiveExecFailures: number;
|
|
230
285
|
intentSeq: Record<string, number>;
|
|
286
|
+
llmCallTimestamps?: number[];
|
|
287
|
+
lastLlmCallAt?: number;
|
|
288
|
+
lastTriggerFingerprint?: string;
|
|
289
|
+
journal?: Array<{
|
|
290
|
+
at: string;
|
|
291
|
+
did: string;
|
|
292
|
+
}>;
|
|
231
293
|
}
|
|
232
294
|
export interface AgentTrace {
|
|
233
295
|
runId?: string;
|
|
@@ -256,11 +318,22 @@ export interface PlannedAction {
|
|
|
256
318
|
export interface CycleResult {
|
|
257
319
|
decision: "skip" | "act";
|
|
258
320
|
skipReason?: string;
|
|
321
|
+
rationale?: string;
|
|
322
|
+
confidence?: number;
|
|
323
|
+
rawModelOutput?: string;
|
|
259
324
|
planned: PlannedAction[];
|
|
260
325
|
modelFailed?: boolean;
|
|
261
326
|
disabled?: boolean;
|
|
262
327
|
disabledReason?: string;
|
|
263
328
|
live: boolean;
|
|
329
|
+
triggerCodes?: string[];
|
|
330
|
+
llmCallMade?: boolean;
|
|
331
|
+
tokensIn?: number;
|
|
332
|
+
tokensOut?: number;
|
|
333
|
+
estimatedCostUsd?: number;
|
|
334
|
+
decisionType?: "act" | "skip" | "gate_skip" | "model_error";
|
|
335
|
+
writeAttempted?: number;
|
|
336
|
+
writeAccepted?: number;
|
|
264
337
|
}
|
|
265
338
|
export interface ResolveIssue {
|
|
266
339
|
code: string;
|
package/dist/agent/types.js
CHANGED
|
@@ -25,8 +25,17 @@ export const PROVIDERS = [
|
|
|
25
25
|
"openai",
|
|
26
26
|
"groq",
|
|
27
27
|
"nvidia",
|
|
28
|
+
"gemini",
|
|
28
29
|
"openai-compatible",
|
|
29
30
|
];
|
|
31
|
+
export const DEFAULT_TRIGGER_POLICY = {
|
|
32
|
+
mode: "event_driven",
|
|
33
|
+
skipLlmWhenNoTrigger: true,
|
|
34
|
+
alwaysManageOpenPositions: true,
|
|
35
|
+
maxLlmCallsPerHour: 0,
|
|
36
|
+
debounceMinutes: 0,
|
|
37
|
+
pmEvalCooldownMinutes: 10,
|
|
38
|
+
};
|
|
30
39
|
// What the agent declares it is optimizing for — so two similar-looking agents
|
|
31
40
|
// are distinguishable and the scorecard/Arena can read intent.
|
|
32
41
|
export const OBJECTIVE_PRIMARIES = [
|
|
@@ -40,7 +49,11 @@ export const OBJECTIVE_PRIMARIES = [
|
|
|
40
49
|
// slice. `websearch` = external lookups (an injection surface + a cost — it can
|
|
41
50
|
// inform reasoning but NEVER widen a cap, since caps live in the runner);
|
|
42
51
|
// `indicators` = runner-computed RSI/MACD/etc. fed into the observation.
|
|
43
|
-
export const ALLOWED_CAPABILITIES = [
|
|
52
|
+
export const ALLOWED_CAPABILITIES = [
|
|
53
|
+
"websearch",
|
|
54
|
+
"indicators",
|
|
55
|
+
"news",
|
|
56
|
+
];
|
|
44
57
|
export const ok = () => ({ valid: true });
|
|
45
58
|
export const fail = (code, reason) => ({
|
|
46
59
|
valid: false,
|
package/dist/agent/version.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const COINRITHM_API: {
|
|
|
5
5
|
readonly kind: "coinrithm-agent-api";
|
|
6
6
|
readonly baseUrl: "https://api.coinrithm.com";
|
|
7
7
|
readonly mcpUrl: "https://mcp.coinrithm.com/mcp";
|
|
8
|
-
readonly openapiVersion: "1.
|
|
8
|
+
readonly openapiVersion: "1.5.0";
|
|
9
9
|
readonly mcpPackage: "@coinrithm/mcp-trading";
|
|
10
|
-
readonly mcpVersion:
|
|
10
|
+
readonly mcpVersion: string;
|
|
11
11
|
};
|
package/dist/agent/version.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
// Versions stamped into manifest.lock.json so a resolved agent is reproducible
|
|
2
2
|
// only against the exact compile that produced it.
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
export const RUNNER_VERSION = "0.1.0";
|
|
4
6
|
export const RESOLVER_VERSION = "1";
|
|
5
7
|
export const MANIFEST_SCHEMA = "coinrithm.manifest.v1";
|
|
8
|
+
// The published package version, read from package.json at runtime (same
|
|
9
|
+
// createRequire pattern as src/version.ts) so the API pin's mcpVersion can never
|
|
10
|
+
// drift from the real published version again. package.json lives outside
|
|
11
|
+
// rootDir=src; at runtime dist/agent/version.js resolves ../../package.json to
|
|
12
|
+
// the package root in both the repo and the published tarball.
|
|
13
|
+
const PACKAGE_VERSION = require("../../package.json").version;
|
|
6
14
|
// The CoinRithm execution surface a generated agent talks to. Written into
|
|
7
15
|
// functionality/coinrithm.yaml as a version PIN; the CLI warns when an agent's
|
|
8
16
|
// pin lags this, but never blocks self-host use.
|
|
@@ -10,7 +18,9 @@ export const COINRITHM_API = {
|
|
|
10
18
|
kind: "coinrithm-agent-api",
|
|
11
19
|
baseUrl: "https://api.coinrithm.com",
|
|
12
20
|
mcpUrl: "https://mcp.coinrithm.com/mcp",
|
|
13
|
-
|
|
21
|
+
// The API CONTRACT version (openapi.yaml info.version). Versioned independently
|
|
22
|
+
// from the npm package below — hand-bump this when the OpenAPI contract changes.
|
|
23
|
+
openapiVersion: "1.5.0",
|
|
14
24
|
mcpPackage: "@coinrithm/mcp-trading",
|
|
15
|
-
mcpVersion:
|
|
25
|
+
mcpVersion: PACKAGE_VERSION,
|
|
16
26
|
};
|
package/dist/client.d.ts
CHANGED
|
@@ -29,6 +29,23 @@ export declare class CoinRithmClient {
|
|
|
29
29
|
private readonly baseUrl;
|
|
30
30
|
constructor(config: ClientConfig);
|
|
31
31
|
private request;
|
|
32
|
+
private publicRequest;
|
|
33
|
+
getPublicPmOverview(query?: {
|
|
34
|
+
fiat?: string;
|
|
35
|
+
}): Promise<ApiResult>;
|
|
36
|
+
listPublicPmEvents(query?: {
|
|
37
|
+
q?: string;
|
|
38
|
+
source?: string;
|
|
39
|
+
status?: string;
|
|
40
|
+
sort?: string;
|
|
41
|
+
limit?: number;
|
|
42
|
+
offset?: number;
|
|
43
|
+
fiat?: string;
|
|
44
|
+
}): Promise<ApiResult>;
|
|
45
|
+
getPublicPmEvent(source: string, slug: string, query?: {
|
|
46
|
+
fiat?: string;
|
|
47
|
+
}): Promise<ApiResult>;
|
|
48
|
+
getPublicPmWhales(): Promise<ApiResult>;
|
|
32
49
|
whoami(apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
|
|
33
50
|
getPortfolio(query?: {
|
|
34
51
|
fiat?: string;
|
|
@@ -111,6 +128,7 @@ export declare class CoinRithmClient {
|
|
|
111
128
|
source: string;
|
|
112
129
|
slug: string;
|
|
113
130
|
outcomeExternalMarketId: string;
|
|
131
|
+
side?: "yes" | "no";
|
|
114
132
|
stakeMusd: number;
|
|
115
133
|
} & {
|
|
116
134
|
agentTrace?: AgentTrace;
|
|
@@ -155,6 +173,7 @@ export declare class CoinRithmClient {
|
|
|
155
173
|
source: string;
|
|
156
174
|
slug: string;
|
|
157
175
|
outcomeExternalMarketId: string;
|
|
176
|
+
side?: "yes" | "no";
|
|
158
177
|
stakeMusd: number;
|
|
159
178
|
idempotencyKey: string;
|
|
160
179
|
}>, apiKey?: string): Promise<ApiResult>;
|
package/dist/client.js
CHANGED
|
@@ -157,6 +157,62 @@ export class CoinRithmClient {
|
|
|
157
157
|
data,
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
|
+
// Public, keyless GET against the free cross-venue data API
|
|
161
|
+
// (/api/prediction-markets/*). No Authorization header is ever attached:
|
|
162
|
+
// these endpoints require no key, and the caller's trading key must not
|
|
163
|
+
// leak into them. No ledger headers exist on this surface either.
|
|
164
|
+
async publicRequest(path, query) {
|
|
165
|
+
const url = new URL(this.baseUrl + path);
|
|
166
|
+
if (query) {
|
|
167
|
+
for (const [k, v] of Object.entries(query)) {
|
|
168
|
+
if (v !== undefined && v !== null && v !== "") {
|
|
169
|
+
url.searchParams.set(k, String(v));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let res;
|
|
174
|
+
try {
|
|
175
|
+
res = await fetch(url.toString(), {
|
|
176
|
+
method: "GET",
|
|
177
|
+
headers: { Accept: "application/json" },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
log(`network error calling GET ${path}:`, err);
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
status: 0,
|
|
185
|
+
data: {
|
|
186
|
+
error: "network_error",
|
|
187
|
+
message: err instanceof Error ? err.message : String(err),
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const text = await res.text();
|
|
192
|
+
let data = text;
|
|
193
|
+
if (text) {
|
|
194
|
+
try {
|
|
195
|
+
data = JSON.parse(text);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// leave as text
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { ok: res.ok, status: res.status, data };
|
|
202
|
+
}
|
|
203
|
+
// ---- public PM data (no key required) ----
|
|
204
|
+
getPublicPmOverview(query) {
|
|
205
|
+
return this.publicRequest("/api/prediction-markets/overview", query);
|
|
206
|
+
}
|
|
207
|
+
listPublicPmEvents(query) {
|
|
208
|
+
return this.publicRequest("/api/prediction-markets/events", query);
|
|
209
|
+
}
|
|
210
|
+
getPublicPmEvent(source, slug, query) {
|
|
211
|
+
return this.publicRequest(`/api/prediction-markets/events/${encodeURIComponent(source)}/${encodeURIComponent(slug)}`, query);
|
|
212
|
+
}
|
|
213
|
+
getPublicPmWhales() {
|
|
214
|
+
return this.publicRequest("/api/prediction-markets/whales");
|
|
215
|
+
}
|
|
160
216
|
// Every method takes an optional trailing `apiKey` (the per-request key for
|
|
161
217
|
// the multi-user HTTP path). When omitted, the constructor key (stdio) is used.
|
|
162
218
|
// ---- reads (scope: read) ----
|
package/dist/tools.js
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
// isError results rather than thrown so the model can react.
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { bearerFromHeader } from "./client.js";
|
|
9
|
-
const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). Not financial advice."
|
|
9
|
+
const PAPER_NOTE = "Paper trading only — virtual funds (50,000 mUSD). Not financial advice. " +
|
|
10
|
+
"Paper fills apply a disclosed execution cost folded into realized PnL: " +
|
|
11
|
+
"spot/futures pay a taker fee (spot market orders also pay half-spread + " +
|
|
12
|
+
"slippage); PM fills at the ask with size-based slippage and a Polymarket-" +
|
|
13
|
+
"shaped taker fee, with entryProbability kept at the mid for calibration. " +
|
|
14
|
+
"See the executionModel in quote/trade results — a rehearsal cost, not an " +
|
|
15
|
+
"exchange fill guarantee.";
|
|
10
16
|
const API_RESULT_OUTPUT_SCHEMA = {
|
|
11
17
|
httpStatus: z
|
|
12
18
|
.number()
|
|
@@ -569,7 +575,9 @@ export function registerTools(server, client) {
|
|
|
569
575
|
"estimate, max payout, eligibility, freshness, and decisionSupport " +
|
|
570
576
|
"(market quality/liquidity/volume/spread tiers + flags) so you can " +
|
|
571
577
|
"quote and gauge tradability in one call. Never mutates state. " +
|
|
572
|
-
"stakeMusd must be > 0 (min to open is 10). " +
|
|
578
|
+
"stakeMusd must be > 0 (min to open is 10). Pass side: 'no' to quote " +
|
|
579
|
+
"backing the NO side (omitted = yes); a NO entry fills at 100 minus the " +
|
|
580
|
+
"outcome probability and pays out if the outcome resolves false. " +
|
|
573
581
|
PAPER_NOTE,
|
|
574
582
|
inputSchema: {
|
|
575
583
|
source: z.string().describe("Source slug (e.g. kalshi, polymarket)."),
|
|
@@ -577,12 +585,18 @@ export function registerTools(server, client) {
|
|
|
577
585
|
outcomeExternalMarketId: z
|
|
578
586
|
.string()
|
|
579
587
|
.describe("Case-sensitive outcome / market id."),
|
|
588
|
+
side: z
|
|
589
|
+
.enum(["yes", "no"])
|
|
590
|
+
.optional()
|
|
591
|
+
.describe("Which side of the binary outcome to back. NO pays out if it " +
|
|
592
|
+
"resolves false; fills at 100 minus the outcome probability. " +
|
|
593
|
+
"Omitted = yes."),
|
|
580
594
|
stakeMusd: z.number().positive().describe("mUSD to stake (> 0)."),
|
|
581
595
|
agentTrace: AGENT_TRACE_SCHEMA,
|
|
582
596
|
},
|
|
583
597
|
outputSchema: API_RESULT_OUTPUT_SCHEMA,
|
|
584
598
|
annotations: readOnlyAnnotations("Prediction-market quote"),
|
|
585
|
-
}, async ({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, stakeMusd, agentTrace }, requestKey(extra))));
|
|
599
|
+
}, async ({ source, slug, outcomeExternalMarketId, side, stakeMusd, agentTrace }, extra) => present(await client.pmQuote({ source, slug, outcomeExternalMarketId, side, stakeMusd, agentTrace }, requestKey(extra))));
|
|
586
600
|
server.registerTool("spot_quote", {
|
|
587
601
|
title: "Spot quote",
|
|
588
602
|
description: "Read-only spot MARKET quote: live execution price, estimated cost " +
|
|
@@ -800,7 +814,9 @@ export function registerTools(server, client) {
|
|
|
800
814
|
description: "Open a mock prediction-market position (binary outcomes only). Requires " +
|
|
801
815
|
"the trade:pm scope. Enabled now (server-flag gated — returns 403 'not " +
|
|
802
816
|
"enabled' only if CoinRithm later disables it). idempotencyKey is " +
|
|
803
|
-
"REQUIRED. stakeMusd >= 10.
|
|
817
|
+
"REQUIRED. stakeMusd >= 10. Pass side: 'no' to back the NO side (omitted " +
|
|
818
|
+
"= yes); a NO entry fills at 100 minus the outcome probability and pays " +
|
|
819
|
+
"out if the outcome resolves false. Quote first and CONFIRM with the user. " +
|
|
804
820
|
PAPER_NOTE,
|
|
805
821
|
inputSchema: {
|
|
806
822
|
source: z
|
|
@@ -810,6 +826,12 @@ export function registerTools(server, client) {
|
|
|
810
826
|
outcomeExternalMarketId: z
|
|
811
827
|
.string()
|
|
812
828
|
.describe("Case-sensitive outcome or market id returned by discovery."),
|
|
829
|
+
side: z
|
|
830
|
+
.enum(["yes", "no"])
|
|
831
|
+
.optional()
|
|
832
|
+
.describe("Which side of the binary outcome to back. NO pays out if it " +
|
|
833
|
+
"resolves false; fills at 100 minus the outcome probability. " +
|
|
834
|
+
"Omitted = yes."),
|
|
813
835
|
stakeMusd: z.number().min(10).describe("mUSD stake (>= 10)."),
|
|
814
836
|
idempotencyKey: z
|
|
815
837
|
.string()
|
|
@@ -821,12 +843,124 @@ export function registerTools(server, client) {
|
|
|
821
843
|
annotations: mutatingAnnotations("Open prediction-market position", {
|
|
822
844
|
idempotent: true,
|
|
823
845
|
}),
|
|
824
|
-
}, async ({ source, slug, outcomeExternalMarketId, stakeMusd, idempotencyKey, agentTrace, }, extra) => present(await client.openPmPosition({
|
|
846
|
+
}, async ({ source, slug, outcomeExternalMarketId, side, stakeMusd, idempotencyKey, agentTrace, }, extra) => present(await client.openPmPosition({
|
|
825
847
|
source,
|
|
826
848
|
slug,
|
|
827
849
|
outcomeExternalMarketId,
|
|
850
|
+
side,
|
|
828
851
|
stakeMusd,
|
|
829
852
|
idempotencyKey,
|
|
830
853
|
agentTrace,
|
|
831
854
|
}, requestKey(extra))));
|
|
855
|
+
// ---- Public cross-venue PM data (no API key required) ----
|
|
856
|
+
// These wrap the free /api/prediction-markets/* endpoints — CoinRithm's
|
|
857
|
+
// citable cross-venue dataset. They never attach the caller's key.
|
|
858
|
+
server.registerTool("pm_data_overview", {
|
|
859
|
+
title: "Cross-venue prediction-market statistics",
|
|
860
|
+
description: "Free public cross-venue prediction-market statistics: total/open/" +
|
|
861
|
+
"closed market counts, total volume, 24h volume, and liquidity " +
|
|
862
|
+
"aggregated across Polymarket, Kalshi, Metaculus, PredictIt, " +
|
|
863
|
+
"Limitless, Manifold, and Smarkets, plus market highlights. Volume is " +
|
|
864
|
+
"reported on each venue's own basis (see the methodology at " +
|
|
865
|
+
"https://coinrithm.com/en/prediction-markets/stats) and monetary " +
|
|
866
|
+
"totals cover real-money venues only — these are self-computed " +
|
|
867
|
+
"aggregates, so cite CoinRithm when quoting them. No API key required.",
|
|
868
|
+
inputSchema: {
|
|
869
|
+
fiat: z
|
|
870
|
+
.string()
|
|
871
|
+
.optional()
|
|
872
|
+
.describe("Fiat currency code for monetary figures (default usd)."),
|
|
873
|
+
},
|
|
874
|
+
outputSchema: API_RESULT_OUTPUT_SCHEMA,
|
|
875
|
+
annotations: readOnlyAnnotations("Cross-venue prediction-market statistics"),
|
|
876
|
+
}, async ({ fiat }) => present(await client.getPublicPmOverview({ fiat })));
|
|
877
|
+
server.registerTool("pm_data_events", {
|
|
878
|
+
title: "Search prediction markets across all venues",
|
|
879
|
+
description: "Free public search over prediction-market events across ALL seven " +
|
|
880
|
+
"venues (Polymarket, Kalshi, Metaculus, PredictIt, Limitless, " +
|
|
881
|
+
"Manifold, Smarkets) — broader than discover_pm_markets, which is " +
|
|
882
|
+
"scoped to the paper-tradeable venues. Returns titles, probabilities, " +
|
|
883
|
+
"volume/liquidity, status, and source per event. Research/data only: " +
|
|
884
|
+
"to trade, use discover_pm_markets + pm_quote instead. No API key " +
|
|
885
|
+
"required.",
|
|
886
|
+
inputSchema: {
|
|
887
|
+
q: z.string().optional().describe("Optional search text."),
|
|
888
|
+
source: z
|
|
889
|
+
.string()
|
|
890
|
+
.optional()
|
|
891
|
+
.describe("Optional venue filter: polymarket, kalshi, metaculus, predictit, " +
|
|
892
|
+
"limitless, manifold, or smarkets."),
|
|
893
|
+
status: z
|
|
894
|
+
.string()
|
|
895
|
+
.optional()
|
|
896
|
+
.describe("Optional status filter (e.g. open or closed)."),
|
|
897
|
+
sort: z.string().optional().describe("Optional sort key."),
|
|
898
|
+
limit: z
|
|
899
|
+
.number()
|
|
900
|
+
.int()
|
|
901
|
+
.min(1)
|
|
902
|
+
.max(50)
|
|
903
|
+
.optional()
|
|
904
|
+
.describe("Max rows (1-50, default 20)."),
|
|
905
|
+
offset: z
|
|
906
|
+
.number()
|
|
907
|
+
.int()
|
|
908
|
+
.min(0)
|
|
909
|
+
.optional()
|
|
910
|
+
.describe("Pagination offset (default 0)."),
|
|
911
|
+
fiat: z
|
|
912
|
+
.string()
|
|
913
|
+
.optional()
|
|
914
|
+
.describe("Fiat currency code for monetary figures (default usd)."),
|
|
915
|
+
},
|
|
916
|
+
outputSchema: API_RESULT_OUTPUT_SCHEMA,
|
|
917
|
+
annotations: readOnlyAnnotations("Search prediction markets across all venues"),
|
|
918
|
+
}, async ({ q, source, status, sort, limit, offset, fiat }) => present(await client.listPublicPmEvents({
|
|
919
|
+
q,
|
|
920
|
+
source,
|
|
921
|
+
status,
|
|
922
|
+
sort,
|
|
923
|
+
limit,
|
|
924
|
+
offset,
|
|
925
|
+
fiat,
|
|
926
|
+
})));
|
|
927
|
+
server.registerTool("pm_data_event", {
|
|
928
|
+
title: "Get full prediction-market event detail",
|
|
929
|
+
description: "Free public detail for one prediction-market event by venue + slug: " +
|
|
930
|
+
"outcomes with probabilities, price snapshots, resolution evidence, " +
|
|
931
|
+
"crossSourceMatches (the SAME real-world question priced on other " +
|
|
932
|
+
"venues — read probability divergence directly from it), " +
|
|
933
|
+
"referenceProbability when present (CoinRithm's canonical cross-venue " +
|
|
934
|
+
"number: the liquidity-weighted median Yes probability across matched " +
|
|
935
|
+
"real-money venues, with venueCount and spreadPoints — quote all " +
|
|
936
|
+
"three together, venues disagree and the spread says by how much), " +
|
|
937
|
+
"recent whale trades on the event, related events, and related news. " +
|
|
938
|
+
"This is the cross-venue research view; for tradability use pm_quote. " +
|
|
939
|
+
"No API key required.",
|
|
940
|
+
inputSchema: {
|
|
941
|
+
source: z
|
|
942
|
+
.string()
|
|
943
|
+
.describe("Venue slug: polymarket, kalshi, metaculus, predictit, limitless, " +
|
|
944
|
+
"manifold, or smarkets."),
|
|
945
|
+
slug: z.string().describe("Event slug on that venue."),
|
|
946
|
+
fiat: z
|
|
947
|
+
.string()
|
|
948
|
+
.optional()
|
|
949
|
+
.describe("Fiat currency code for monetary figures (default usd)."),
|
|
950
|
+
},
|
|
951
|
+
outputSchema: API_RESULT_OUTPUT_SCHEMA,
|
|
952
|
+
annotations: readOnlyAnnotations("Get full prediction-market event detail"),
|
|
953
|
+
}, async ({ source, slug, fiat }) => present(await client.getPublicPmEvent(source, slug, { fiat })));
|
|
954
|
+
server.registerTool("pm_data_whales", {
|
|
955
|
+
title: "Get latest prediction-market whale trades",
|
|
956
|
+
description: "Free public tape of the latest large prediction-market trades " +
|
|
957
|
+
"(roughly $1k+ notional) across venues, newest first (top 50): side, " +
|
|
958
|
+
"outcome, USD value, price, market question, and the event it printed " +
|
|
959
|
+
"on. Polymarket rows are wallet-attributed; Kalshi rows are anonymized " +
|
|
960
|
+
"exchange prints. A large print is information, not a recommendation. " +
|
|
961
|
+
"No API key required.",
|
|
962
|
+
inputSchema: {},
|
|
963
|
+
outputSchema: API_RESULT_OUTPUT_SCHEMA,
|
|
964
|
+
annotations: readOnlyAnnotations("Get latest prediction-market whale trades"),
|
|
965
|
+
}, async () => present(await client.getPublicPmWhales()));
|
|
832
966
|
}
|