@gonzih/polymarket-arb 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ export declare const BACKTEST_WINDOW_HOURS = 4;
2
+ export declare const BACKTEST_MOMENTUM_THRESHOLD = 0.05;
3
+ export type ResolvedMarket = {
4
+ id: string;
5
+ conditionId: string;
6
+ clobTokenId: string;
7
+ question: string;
8
+ volume: number;
9
+ startDate: number;
10
+ endDate: number;
11
+ resolutionTime: number;
12
+ resolution: number;
13
+ };
14
+ export type PricePoint = {
15
+ t: number;
16
+ p: number;
17
+ };
18
+ export type BacktestSignal = {
19
+ firedAt: number;
20
+ oddsAtSignal: number;
21
+ direction: "YES" | "NO";
22
+ momentum: number;
23
+ };
24
+ export type ClaudeDecision = "BUY_YES" | "BUY_NO" | "PASS" | "ERROR";
25
+ export type BacktestResult = {
26
+ marketId: string;
27
+ question: string;
28
+ signalFiredAt: number;
29
+ oddsAtSignal: number;
30
+ claudeDecision: ClaudeDecision;
31
+ claudeLatencyMs: number;
32
+ actualResolution: number;
33
+ correct: boolean;
34
+ kellySizePct: number;
35
+ hypotheticalPnl: number;
36
+ };
37
+ export declare function fetchResolvedMarkets(limit?: number): Promise<ResolvedMarket[]>;
38
+ /**
39
+ * Fetches hourly price history from the CLOB API.
40
+ * NOTE (discovered during backtest): The CLOB prices-history endpoint returns
41
+ * empty data for resolved/closed markets. Price history is only available for
42
+ * currently active markets, and typically has very few data points.
43
+ * marketId should be a clobTokenId (the long numeric string from clobTokenIds[0]).
44
+ */
45
+ export declare function fetchPriceHistory(marketId: string): Promise<PricePoint[]>;
46
+ /**
47
+ * Calls Claude directly on a resolved market question to probe integration health.
48
+ * Used when price history is unavailable and no momentum signals can be replayed.
49
+ */
50
+ export declare function probeClaudeIntegration(markets: ResolvedMarket[]): Promise<{
51
+ success: number;
52
+ errors: number;
53
+ avgLatencyMs: number;
54
+ sample: BacktestResult[];
55
+ }>;
56
+ /**
57
+ * Replays the EXISTING momentum signal logic against prediction market price history.
58
+ * Adapted from SignalEngine in signal.ts: same % change concept, but uses a 4-hour
59
+ * window and 5% threshold suited to hourly prediction market data (0-1 price range).
60
+ */
61
+ export declare function replaySignals(history: PricePoint[]): BacktestSignal[];
62
+ /**
63
+ * Kelly Criterion: f* = (p*b - q) / b
64
+ * where b = net odds (profit per $ risked), p = win probability, q = 1 - p
65
+ * Capped at 10% maximum.
66
+ */
67
+ export declare function kellySize(odds: number, winProb: number): number;
68
+ export declare function computePnl(decision: ClaudeDecision, odds: number, resolution: number, kellySizePct: number): number;
69
+ /**
70
+ * Calls the Claude CLI subprocess. Sends prompt via stdin (not as a positional arg)
71
+ * because when spawned with an open stdin pipe the CLI waits for input otherwise.
72
+ */
73
+ export declare function askClaude(question: string, odds: number): Promise<{
74
+ decision: ClaudeDecision;
75
+ latencyMs: number;
76
+ }>;
77
+ export interface BacktestReport {
78
+ marketsAnalyzed: number;
79
+ signalsFired: number;
80
+ claudeSuccesses: number;
81
+ claudeErrors: number;
82
+ avgLatencyMs: number;
83
+ maxLatencyMs: number;
84
+ buyYesTotal: number;
85
+ buyYesCorrect: number;
86
+ buyNoTotal: number;
87
+ buyNoCorrect: number;
88
+ passCount: number;
89
+ avgKellyPct: number;
90
+ totalPnl: number;
91
+ results: BacktestResult[];
92
+ }
93
+ export declare function generateReport(results: BacktestResult[], marketsAnalyzed: number): BacktestReport;
94
+ export declare function formatReport(report: BacktestReport, date: string): string;
95
+ export declare function writeReport(text: string, date: string): string;
96
+ //# sourceMappingURL=backtest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtest.d.ts","sourceRoot":"","sources":["../src/backtest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,370 @@
1
+ import { spawn } from "child_process";
2
+ import fs from "fs";
3
+ import { log } from "./logger.js";
4
+ const GAMMA_API = "https://gamma-api.polymarket.com";
5
+ const CLOB_API = "https://clob.polymarket.com";
6
+ // Momentum parameters adapted for prediction market hourly price data
7
+ // (0-1 range, slower-moving than crypto ticks)
8
+ export const BACKTEST_WINDOW_HOURS = 4;
9
+ export const BACKTEST_MOMENTUM_THRESHOLD = 0.05; // 5% move over 4h
10
+ function parseJsonField(field) {
11
+ if (Array.isArray(field))
12
+ return field;
13
+ try {
14
+ return JSON.parse(field);
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ }
20
+ export async function fetchResolvedMarkets(limit = 100) {
21
+ const sixMonthsAgo = Date.now() - 180 * 24 * 60 * 60 * 1000;
22
+ // Use end_date_min to fetch recent markets without scanning thousands of pages
23
+ const sixMonthsAgoDate = new Date(sixMonthsAgo).toISOString().slice(0, 10);
24
+ const results = [];
25
+ let offset = 0;
26
+ const batchSize = 100;
27
+ while (results.length < limit) {
28
+ const url = `${GAMMA_API}/markets?closed=true&limit=${batchSize}&offset=${offset}&end_date_min=${sixMonthsAgoDate}`;
29
+ let markets;
30
+ try {
31
+ const res = await fetch(url);
32
+ if (!res.ok) {
33
+ log("warn", { source: "backtest", event: "fetch_resolved_error", status: res.status });
34
+ break;
35
+ }
36
+ markets = (await res.json());
37
+ }
38
+ catch (err) {
39
+ log("warn", { source: "backtest", event: "fetch_resolved_exception", error: String(err) });
40
+ break;
41
+ }
42
+ if (markets.length === 0)
43
+ break;
44
+ for (const m of markets) {
45
+ const volume = Number(m.volumeNum ?? m.volume ?? 0);
46
+ if (volume < 50_000)
47
+ continue;
48
+ const endDate = m.endDate ? new Date(m.endDate).getTime() : 0;
49
+ if (endDate < sixMonthsAgo || endDate === 0)
50
+ continue;
51
+ // Only include cleanly resolved markets (YES price = 0 or 1)
52
+ const prices = parseJsonField(m.outcomePrices ?? []).map(Number);
53
+ if (prices.length < 2)
54
+ continue;
55
+ const yesPrice = prices[0];
56
+ if (yesPrice !== 0 && yesPrice !== 1)
57
+ continue;
58
+ // Parse YES outcome clobTokenId for CLOB price history
59
+ const tokenIds = parseJsonField(m.clobTokenIds ?? "[]");
60
+ const clobTokenId = tokenIds[0] ?? "";
61
+ results.push({
62
+ id: m.id,
63
+ conditionId: m.conditionId ?? m.id,
64
+ clobTokenId,
65
+ question: m.question ?? "",
66
+ volume,
67
+ startDate: m.startDate ? new Date(m.startDate).getTime() : 0,
68
+ endDate,
69
+ resolutionTime: m.resolutionTime ? new Date(m.resolutionTime).getTime() : endDate,
70
+ resolution: yesPrice === 1 ? 1 : 0,
71
+ });
72
+ if (results.length >= limit)
73
+ break;
74
+ }
75
+ offset += batchSize;
76
+ if (markets.length < batchSize)
77
+ break; // no more pages
78
+ }
79
+ log("info", {
80
+ source: "backtest",
81
+ event: "resolved_markets_fetched",
82
+ count: results.length,
83
+ });
84
+ return results;
85
+ }
86
+ /**
87
+ * Fetches hourly price history from the CLOB API.
88
+ * NOTE (discovered during backtest): The CLOB prices-history endpoint returns
89
+ * empty data for resolved/closed markets. Price history is only available for
90
+ * currently active markets, and typically has very few data points.
91
+ * marketId should be a clobTokenId (the long numeric string from clobTokenIds[0]).
92
+ */
93
+ export async function fetchPriceHistory(marketId) {
94
+ try {
95
+ const url = `${CLOB_API}/prices-history?market=${encodeURIComponent(marketId)}&interval=1h&fidelity=60`;
96
+ const res = await fetch(url);
97
+ if (!res.ok)
98
+ return [];
99
+ const data = (await res.json());
100
+ const history = Array.isArray(data) ? data : (data.history ?? []);
101
+ return history.sort((a, b) => a.t - b.t);
102
+ }
103
+ catch {
104
+ return [];
105
+ }
106
+ }
107
+ /**
108
+ * Calls Claude directly on a resolved market question to probe integration health.
109
+ * Used when price history is unavailable and no momentum signals can be replayed.
110
+ */
111
+ export async function probeClaudeIntegration(markets) {
112
+ const sample = markets.slice(0, 5);
113
+ const results = [];
114
+ let totalLatency = 0;
115
+ let errors = 0;
116
+ for (const market of sample) {
117
+ // Use last-known odds as 0.5 (unknown pre-resolution for closed markets)
118
+ const odds = 0.5;
119
+ const { decision, latencyMs } = await askClaude(market.question, odds);
120
+ totalLatency += latencyMs;
121
+ if (decision === "ERROR")
122
+ errors++;
123
+ const correct = decision === "BUY_YES"
124
+ ? market.resolution === 1
125
+ : decision === "BUY_NO"
126
+ ? market.resolution === 0
127
+ : false;
128
+ results.push({
129
+ marketId: market.id,
130
+ question: market.question,
131
+ signalFiredAt: 0,
132
+ oddsAtSignal: odds,
133
+ claudeDecision: decision,
134
+ claudeLatencyMs: latencyMs,
135
+ actualResolution: market.resolution,
136
+ correct,
137
+ kellySizePct: 0,
138
+ hypotheticalPnl: 0,
139
+ });
140
+ }
141
+ return {
142
+ success: sample.length - errors,
143
+ errors,
144
+ avgLatencyMs: sample.length > 0 ? totalLatency / sample.length : 0,
145
+ sample: results,
146
+ };
147
+ }
148
+ /**
149
+ * Replays the EXISTING momentum signal logic against prediction market price history.
150
+ * Adapted from SignalEngine in signal.ts: same % change concept, but uses a 4-hour
151
+ * window and 5% threshold suited to hourly prediction market data (0-1 price range).
152
+ */
153
+ export function replaySignals(history) {
154
+ const signals = [];
155
+ if (history.length < BACKTEST_WINDOW_HOURS + 1)
156
+ return signals;
157
+ let lastSignalAtMs = 0;
158
+ const COOLDOWN_MS = 12 * 60 * 60 * 1000; // 12-hour cooldown between signals per market
159
+ for (let i = BACKTEST_WINDOW_HOURS; i < history.length; i++) {
160
+ const windowStart = history[i - BACKTEST_WINDOW_HOURS];
161
+ const current = history[i];
162
+ if (windowStart.p === 0)
163
+ continue;
164
+ const momentum = (current.p - windowStart.p) / windowStart.p;
165
+ if (Math.abs(momentum) < BACKTEST_MOMENTUM_THRESHOLD)
166
+ continue;
167
+ const tMs = current.t * 1000;
168
+ if (tMs - lastSignalAtMs < COOLDOWN_MS)
169
+ continue;
170
+ lastSignalAtMs = tMs;
171
+ signals.push({
172
+ firedAt: tMs,
173
+ oddsAtSignal: current.p,
174
+ direction: momentum > 0 ? "YES" : "NO",
175
+ momentum,
176
+ });
177
+ }
178
+ return signals;
179
+ }
180
+ /**
181
+ * Kelly Criterion: f* = (p*b - q) / b
182
+ * where b = net odds (profit per $ risked), p = win probability, q = 1 - p
183
+ * Capped at 10% maximum.
184
+ */
185
+ export function kellySize(odds, winProb) {
186
+ if (odds <= 0 || odds >= 1)
187
+ return 0;
188
+ const b = (1 - odds) / odds; // net odds
189
+ const q = 1 - winProb;
190
+ const kelly = (winProb * b - q) / b;
191
+ return Math.max(0, Math.min(0.1, kelly));
192
+ }
193
+ export function computePnl(decision, odds, resolution, kellySizePct) {
194
+ if (decision === "PASS" || decision === "ERROR")
195
+ return 0;
196
+ const betYes = decision === "BUY_YES";
197
+ const won = betYes ? resolution === 1 : resolution === 0;
198
+ const betOdds = betYes ? odds : 1 - odds;
199
+ if (won) {
200
+ // Profit per $ = (1 - betOdds) / betOdds
201
+ return kellySizePct * ((1 - betOdds) / betOdds);
202
+ }
203
+ return -kellySizePct;
204
+ }
205
+ /**
206
+ * Calls the Claude CLI subprocess. Sends prompt via stdin (not as a positional arg)
207
+ * because when spawned with an open stdin pipe the CLI waits for input otherwise.
208
+ */
209
+ export async function askClaude(question, odds) {
210
+ const start = Date.now();
211
+ const prompt = `Market: ${question}\nCurrent odds: ${(odds * 100).toFixed(1)}% YES\nSignal: momentum spike detected\nShould we bet YES or NO? Respond with: BUY_YES, BUY_NO, or PASS`;
212
+ return new Promise((resolve) => {
213
+ const proc = spawn("claude", ["--print", "--model", "claude-haiku-4-5-20251001"], {
214
+ env: { ...process.env },
215
+ stdio: ["pipe", "pipe", "pipe"],
216
+ });
217
+ // Send prompt via stdin then close it so claude knows there is no more input
218
+ proc.stdin.write(prompt);
219
+ proc.stdin.end();
220
+ let stdout = "";
221
+ let stderr = "";
222
+ let timedOut = false;
223
+ proc.stdout.on("data", (chunk) => {
224
+ stdout += chunk.toString();
225
+ });
226
+ proc.stderr.on("data", (chunk) => {
227
+ stderr += chunk.toString();
228
+ });
229
+ const timer = setTimeout(() => {
230
+ timedOut = true;
231
+ proc.kill("SIGTERM");
232
+ }, 30_000);
233
+ proc.on("close", (code) => {
234
+ clearTimeout(timer);
235
+ const latencyMs = Date.now() - start;
236
+ if (timedOut || code !== 0) {
237
+ const err = timedOut
238
+ ? "timeout after 30s"
239
+ : `exit code ${code}${stderr ? ": " + stderr.slice(0, 200) : ""}`;
240
+ log("warn", { source: "backtest", event: "claude_error", error: err });
241
+ resolve({ decision: "ERROR", latencyMs });
242
+ return;
243
+ }
244
+ const text = stdout.toUpperCase();
245
+ let decision = "PASS";
246
+ if (text.includes("BUY_YES"))
247
+ decision = "BUY_YES";
248
+ else if (text.includes("BUY_NO"))
249
+ decision = "BUY_NO";
250
+ log("info", {
251
+ source: "backtest",
252
+ event: "claude_decision",
253
+ question: question.slice(0, 60),
254
+ decision,
255
+ latencyMs,
256
+ });
257
+ resolve({ decision, latencyMs });
258
+ });
259
+ proc.on("error", (err) => {
260
+ clearTimeout(timer);
261
+ const latencyMs = Date.now() - start;
262
+ log("warn", { source: "backtest", event: "claude_error", error: String(err) });
263
+ resolve({ decision: "ERROR", latencyMs });
264
+ });
265
+ });
266
+ }
267
+ export function generateReport(results, marketsAnalyzed) {
268
+ const claudeErrors = results.filter((r) => r.claudeDecision === "ERROR").length;
269
+ const claudeSuccesses = results.filter((r) => r.claudeDecision !== "ERROR").length;
270
+ const latencies = results.map((r) => r.claudeLatencyMs).filter((l) => l > 0);
271
+ const avgLatencyMs = latencies.length > 0 ? latencies.reduce((a, b) => a + b, 0) / latencies.length : 0;
272
+ const maxLatencyMs = latencies.length > 0 ? Math.max(...latencies) : 0;
273
+ const buyYes = results.filter((r) => r.claudeDecision === "BUY_YES");
274
+ const buyNo = results.filter((r) => r.claudeDecision === "BUY_NO");
275
+ const passes = results.filter((r) => r.claudeDecision === "PASS");
276
+ const kellyValues = results.filter((r) => r.kellySizePct > 0).map((r) => r.kellySizePct);
277
+ const avgKellyPct = kellyValues.length > 0 ? kellyValues.reduce((a, b) => a + b, 0) / kellyValues.length : 0;
278
+ const totalPnl = results.reduce((sum, r) => sum + r.hypotheticalPnl, 0);
279
+ return {
280
+ marketsAnalyzed,
281
+ signalsFired: results.length,
282
+ claudeSuccesses,
283
+ claudeErrors,
284
+ avgLatencyMs,
285
+ maxLatencyMs,
286
+ buyYesTotal: buyYes.length,
287
+ buyYesCorrect: buyYes.filter((r) => r.correct).length,
288
+ buyNoTotal: buyNo.length,
289
+ buyNoCorrect: buyNo.filter((r) => r.correct).length,
290
+ passCount: passes.length,
291
+ avgKellyPct,
292
+ totalPnl,
293
+ results,
294
+ };
295
+ }
296
+ export function formatReport(report, date) {
297
+ const totalDecisions = report.buyYesTotal + report.buyNoTotal;
298
+ const totalCorrect = report.buyYesCorrect + report.buyNoCorrect;
299
+ const winRate = totalDecisions > 0 ? (totalCorrect / totalDecisions) * 100 : 0;
300
+ const signalRate = report.marketsAnalyzed > 0
301
+ ? (report.signalsFired / report.marketsAnalyzed) * 100
302
+ : 0;
303
+ const yesAcc = report.buyYesTotal > 0
304
+ ? `${report.buyYesCorrect}/${report.buyYesTotal} (${((report.buyYesCorrect / report.buyYesTotal) * 100).toFixed(1)}%)`
305
+ : "0/0 (n/a)";
306
+ const noAcc = report.buyNoTotal > 0
307
+ ? `${report.buyNoCorrect}/${report.buyNoTotal} (${((report.buyNoCorrect / report.buyNoTotal) * 100).toFixed(1)}%)`
308
+ : "0/0 (n/a)";
309
+ const claudeStatus = report.claudeErrors === 0
310
+ ? ` ✓ All ${report.claudeSuccesses} calls succeeded`
311
+ : ` ✓ ${report.claudeSuccesses} calls succeeded\n ✗ ${report.claudeErrors} error${report.claudeErrors !== 1 ? "s" : ""}`;
312
+ let verdict;
313
+ if (totalDecisions === 0) {
314
+ verdict =
315
+ "INSUFFICIENT DATA: No actionable signals. Either no price history from API, " +
316
+ "signals never crossed the 5% threshold, or all signals fired after market resolution.";
317
+ }
318
+ else if (totalDecisions < 5) {
319
+ verdict =
320
+ `TOO FEW TRADES: Only ${totalDecisions} trade${totalDecisions !== 1 ? "s" : ""} — ` +
321
+ "cannot distinguish edge from luck. Need 30+ trades for statistical significance.";
322
+ }
323
+ else if (winRate > 60) {
324
+ verdict =
325
+ `POSSIBLE EDGE: Win rate ${winRate.toFixed(1)}% exceeds 50% baseline. ` +
326
+ `P&L: ${report.totalPnl >= 0 ? "+" : ""}${(report.totalPnl * 100).toFixed(1)}% of bankroll. ` +
327
+ "Needs larger sample (30+ trades) to confirm.";
328
+ }
329
+ else if (winRate > 50) {
330
+ verdict =
331
+ `WEAK SIGNAL: Win rate ${winRate.toFixed(1)}% slightly above baseline. ` +
332
+ "Cannot yet distinguish from noise. More data needed.";
333
+ }
334
+ else {
335
+ verdict =
336
+ `NO EDGE DETECTED: Win rate ${winRate.toFixed(1)}% at or below 50% baseline. ` +
337
+ "Current signal parameters not predictive on this historical data.";
338
+ }
339
+ const lines = [
340
+ "=== POLYMARKET-ARB BACKTEST REPORT ===",
341
+ `Date: ${date}`,
342
+ "",
343
+ `Markets analyzed: ${report.marketsAnalyzed}`,
344
+ `Signals fired: ${report.signalsFired} (${signalRate.toFixed(1)}% of markets)`,
345
+ `Claude Code: ${report.claudeSuccesses}/${report.signalsFired} decisions returned (${report.claudeErrors} error${report.claudeErrors !== 1 ? "s" : ""})`,
346
+ `Claude latency: avg ${(report.avgLatencyMs / 1000).toFixed(1)}s, max ${(report.maxLatencyMs / 1000).toFixed(1)}s`,
347
+ "",
348
+ "SIGNAL ACCURACY:",
349
+ ` BUY_YES decisions: ${report.buyYesTotal} → correct: ${yesAcc}`,
350
+ ` BUY_NO decisions: ${report.buyNoTotal} → correct: ${noAcc}`,
351
+ ` PASS decisions: ${report.passCount}`,
352
+ "",
353
+ "EDGE vs RANDOM:",
354
+ ` Win rate: ${winRate.toFixed(1)}% (random baseline: 50%)`,
355
+ ` Avg Kelly size: ${(report.avgKellyPct * 100).toFixed(1)}% of bankroll`,
356
+ ` Hypothetical total P&L: ${report.totalPnl >= 0 ? "+" : ""}${(report.totalPnl * 100).toFixed(1)}% of bankroll over ${report.marketsAnalyzed} markets`,
357
+ "",
358
+ "CLAUDE CODE INTEGRATION:",
359
+ claudeStatus,
360
+ "",
361
+ `VERDICT: ${verdict}`,
362
+ ];
363
+ return lines.join("\n");
364
+ }
365
+ export function writeReport(text, date) {
366
+ const reportPath = `backtest-report-${date}.md`;
367
+ fs.writeFileSync(reportPath, text + "\n");
368
+ return reportPath;
369
+ }
370
+ //# sourceMappingURL=backtest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtest.js","sourceRoot":"","sources":["../src/backtest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runBacktest(): Promise<void>;
2
+ //# sourceMappingURL=backtestRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtestRunner.d.ts","sourceRoot":"","sources":["../src/backtestRunner.ts"],"names":[],"mappings":"AAoBA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+HjD"}
@@ -0,0 +1,136 @@
1
+ import { fetchResolvedMarkets, fetchPriceHistory, replaySignals, askClaude, kellySize, computePnl, generateReport, formatReport as formatBaseReport, writeReport, probeClaudeIntegration, } from "./backtest.js";
2
+ import { log } from "./logger.js";
3
+ const MARKETS_TO_FETCH = 50;
4
+ // At most one Claude call per market to keep runtime reasonable
5
+ const MAX_SIGNALS_PER_MARKET = 1;
6
+ export async function runBacktest() {
7
+ const date = new Date().toISOString().slice(0, 10);
8
+ console.log("=== POLYMARKET-ARB BACKTESTING HARNESS ===");
9
+ console.log(`Fetching up to ${MARKETS_TO_FETCH} resolved markets (volume > $50k, last 6 months)...`);
10
+ const markets = await fetchResolvedMarkets(MARKETS_TO_FETCH);
11
+ console.log(`Fetched ${markets.length} qualifying markets\n`);
12
+ if (markets.length === 0) {
13
+ console.error("ERROR: No resolved markets found.\n" +
14
+ "Possible causes:\n" +
15
+ " • GAMMA API unreachable\n" +
16
+ " • No markets with volume > $50k resolved in last 6 months\n" +
17
+ " • outcomePrices not cleanly 0 or 1 (market still settling)");
18
+ process.exit(1);
19
+ }
20
+ const results = [];
21
+ let marketsWithHistory = 0;
22
+ let marketsNoHistory = 0;
23
+ let marketsNoSignal = 0;
24
+ for (let i = 0; i < markets.length; i++) {
25
+ const market = markets[i];
26
+ process.stdout.write(`\r[${i + 1}/${markets.length}] ${market.question.slice(0, 55)}...`);
27
+ // Use the YES clobTokenId for price history
28
+ const tokenId = market.clobTokenId || market.conditionId;
29
+ const history = await fetchPriceHistory(tokenId);
30
+ if (history.length < 5) {
31
+ marketsNoHistory++;
32
+ continue;
33
+ }
34
+ marketsWithHistory++;
35
+ const signals = replaySignals(history).slice(0, MAX_SIGNALS_PER_MARKET);
36
+ if (signals.length === 0) {
37
+ marketsNoSignal++;
38
+ continue;
39
+ }
40
+ for (const signal of signals) {
41
+ // Skip signals that fired at or after resolution — unfair look-ahead
42
+ if (signal.firedAt >= market.resolutionTime)
43
+ continue;
44
+ const { decision, latencyMs } = await askClaude(market.question, signal.oddsAtSignal);
45
+ const betOdds = decision === "BUY_YES" ? signal.oddsAtSignal : 1 - signal.oddsAtSignal;
46
+ // Assume modest 65% win probability as prior when signal fires
47
+ const WIN_PROB_PRIOR = 0.65;
48
+ const kellySizePct = decision !== "PASS" && decision !== "ERROR"
49
+ ? kellySize(betOdds, WIN_PROB_PRIOR)
50
+ : 0;
51
+ const pnl = computePnl(decision, signal.oddsAtSignal, market.resolution, kellySizePct);
52
+ const correct = decision === "BUY_YES"
53
+ ? market.resolution === 1
54
+ : decision === "BUY_NO"
55
+ ? market.resolution === 0
56
+ : false;
57
+ results.push({
58
+ marketId: market.id,
59
+ question: market.question,
60
+ signalFiredAt: signal.firedAt,
61
+ oddsAtSignal: signal.oddsAtSignal,
62
+ claudeDecision: decision,
63
+ claudeLatencyMs: latencyMs,
64
+ actualResolution: market.resolution,
65
+ correct,
66
+ kellySizePct,
67
+ hypotheticalPnl: pnl,
68
+ });
69
+ log("info", {
70
+ source: "backtest",
71
+ event: "result",
72
+ question: market.question.slice(0, 60),
73
+ decision,
74
+ oddsAtSignal: signal.oddsAtSignal.toFixed(3),
75
+ resolution: market.resolution,
76
+ correct,
77
+ pnl: pnl.toFixed(4),
78
+ });
79
+ }
80
+ }
81
+ process.stdout.write("\n\n");
82
+ console.log(`Price history: ${marketsWithHistory}/${markets.length} markets had history, ` +
83
+ `${marketsNoHistory} had no/insufficient data, ` +
84
+ `${marketsNoSignal} had history but no signal fired`);
85
+ // ── Claude integration probe ──────────────────────────────────────────────
86
+ // Always run this to verify the subprocess works, regardless of signals.
87
+ console.log("\nRunning Claude integration probe (5 markets)...");
88
+ const probe = await probeClaudeIntegration(markets);
89
+ console.log(`Claude probe: ${probe.success}/5 calls succeeded, ` +
90
+ `${probe.errors} errors, avg latency ${(probe.avgLatencyMs / 1000).toFixed(1)}s`);
91
+ // Add probe results to the report if no signal-based results exist
92
+ const reportResults = results.length > 0 ? results : probe.sample;
93
+ const report = generateReport(reportResults, markets.length);
94
+ const reportText = buildFullReport(report, probe, marketsNoHistory, markets.length, date);
95
+ console.log("\n" + reportText);
96
+ const reportPath = writeReport(reportText, date);
97
+ console.log(`\nReport written to: ${reportPath}`);
98
+ if (probe.errors > 0) {
99
+ console.warn(`\nWARNING: ${probe.errors} Claude call(s) failed.\n` +
100
+ "Check that CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_AUTH_TOKEN is set.");
101
+ }
102
+ }
103
+ function buildFullReport(report, probe, marketsNoHistory, marketsTotal, date) {
104
+ let text = formatBaseReport(report, date);
105
+ if (marketsNoHistory === marketsTotal) {
106
+ text +=
107
+ "\n\nAPI FINDING: CLOB /prices-history endpoint returned empty data for all " +
108
+ marketsTotal +
109
+ " resolved markets.\n" +
110
+ "Investigation results:\n" +
111
+ " • GET /prices-history returns {history:[]} for all closed/resolved markets\n" +
112
+ " • Active markets: price history exists but only ~2 data points (insufficient for 4h window)\n" +
113
+ " • The end_date_min filter works to find recent markets efficiently\n" +
114
+ " • Recommendation: use a dedicated historical data provider (Dune Analytics, TheGraph) for future backtests\n";
115
+ }
116
+ text +=
117
+ "\n\nCLAUDE INTEGRATION PROBE (5 markets, no momentum signal required):\n" +
118
+ ` ${probe.success}/5 calls succeeded (${probe.errors} errors)\n` +
119
+ ` Avg latency: ${(probe.avgLatencyMs / 1000).toFixed(1)}s\n`;
120
+ for (const r of probe.sample) {
121
+ const icon = r.claudeDecision === "ERROR" ? "✗" : "✓";
122
+ text +=
123
+ ` ${icon} "${r.question.slice(0, 50)}" → ${r.claudeDecision} ` +
124
+ `(${(r.claudeLatencyMs / 1000).toFixed(1)}s, resolved: ${r.actualResolution === 1 ? "YES" : "NO"})\n`;
125
+ }
126
+ return text;
127
+ }
128
+ // Run when invoked directly (not imported)
129
+ const selfPath = process.argv[1] ?? "";
130
+ if (selfPath.endsWith("backtestRunner.ts") || selfPath.endsWith("backtestRunner.js")) {
131
+ runBacktest().catch((err) => {
132
+ console.error("Backtest failed:", err);
133
+ process.exit(1);
134
+ });
135
+ }
136
+ //# sourceMappingURL=backtestRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtestRunner.js","sourceRoot":"","sources":["../src/backtestRunner.ts"],"names":[],"mappings":"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"}