mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-24 13:08:10 +00:00
Grade adaptive signals with exact market fees
This commit is contained in:
@@ -6,6 +6,7 @@ const CONCURRENCY = Math.max(1, Math.min(12, Number(process.env.SETTLEMENT_CONCU
|
||||
const HORIZON_DAYS = [...new Set(String(process.env.SETTLEMENT_HORIZONS || "1,3,7,14,30,90").split(",")
|
||||
.map(Number).filter((value) => Number.isFinite(value) && value >= 1 && value <= 365))].sort((a, b) => a - b);
|
||||
const COST_CENTS = Math.max(0, Math.min(5, Number(process.env.SETTLEMENT_COST_CENTS || 0.5)));
|
||||
const EXACT_GAMMA_FEES = process.env.SETTLEMENT_EXACT_GAMMA_FEES === "1";
|
||||
const FINE_GRID = String(process.env.SETTLEMENT_FINE_GRID || "false").toLowerCase() === "true";
|
||||
const SELECTION_ORDER = ["volumeNum", "closedTime", "createdAt", "id"].includes(process.env.SETTLEMENT_ORDER)
|
||||
? process.env.SETTLEMENT_ORDER : "volumeNum";
|
||||
@@ -33,6 +34,18 @@ function categoryOf(raw) {
|
||||
return "Other";
|
||||
}
|
||||
|
||||
function feeScheduleOf(raw) {
|
||||
const rate = Number(raw?.feeSchedule?.rate), exponent = Number(raw?.feeSchedule?.exponent);
|
||||
return Number.isFinite(rate) && rate >= 0 && Number.isFinite(exponent) && exponent > 0
|
||||
? { rate, exponent } : null;
|
||||
}
|
||||
|
||||
function takerFeePerShare(schedule, price) {
|
||||
const p = Number(price), rate = Number(schedule?.rate), exponent = Number(schedule?.exponent);
|
||||
if (!(p > 0 && p < 1) || !Number.isFinite(rate) || rate < 0 || !Number.isFinite(exponent) || exponent <= 0) return null;
|
||||
return rate * Math.pow(p * (1 - p), exponent);
|
||||
}
|
||||
|
||||
function hardSettlementJumpRisk(raw) {
|
||||
const text = `${raw?.question || ""} ${raw?.events?.[0]?.title || ""}`.toLowerCase();
|
||||
const numericRange = /\b\d+(?:\.\d+)?\s*(?:%|percent)?\s*(?:-|–|—|to)\s*\d+(?:\.\d+)?\s*(?:%|percent|votes?|points?|seats?|bps|basis points?|tweets?|posts?|goals?)(?![a-z])/;
|
||||
@@ -122,7 +135,8 @@ async function fetchResolvedMarkets(limit, skip = 0) {
|
||||
seen.add(id); raw.push({ id, question: market.question || "", category: categoryOf(market),
|
||||
eventId: String(market.events?.[0]?.id || id),
|
||||
tokenId: String(tokens[0]), finalYes: outcomes[0] >= 0.99 ? 1 : 0, closedAt,
|
||||
safeContract: !hardSettlementJumpRisk(market), volume: Number(market.volumeNum || market.volume || 0) });
|
||||
safeContract: !hardSettlementJumpRisk(market), feeSchedule: feeScheduleOf(market),
|
||||
volume: Number(market.volumeNum || market.volume || 0) });
|
||||
if (raw.length >= targetCount) break;
|
||||
}
|
||||
if (page.length < pageSize || !payload.next_cursor || payload.next_cursor === cursor) break;
|
||||
@@ -141,14 +155,16 @@ function evaluateMarket(market, points) {
|
||||
const winningSide = market.finalYes ? "YES" : "NO", trend = confirmedTrendAt(points, target, point);
|
||||
for (const side of ["YES", "NO"]) {
|
||||
const entry = side === "YES" ? yesEntry : noEntry, final = side === winningSide ? 1 : 0;
|
||||
const netReturn = final / entry - 1 - (COST_CENTS / 100) / entry;
|
||||
const fee = EXACT_GAMMA_FEES ? takerFeePerShare(market.feeSchedule, entry) : null;
|
||||
const entryCost = COST_CENTS / 100 + (EXACT_GAMMA_FEES && Number.isFinite(fee) ? fee : 0);
|
||||
const netReturn = final / entry - 1 - entryCost / entry;
|
||||
rows.push({ marketId: market.id, eventId: market.eventId, question: market.question, category: market.category,
|
||||
safeContract: market.safeContract, closedAt: market.closedAt, volume: market.volume,
|
||||
horizonDays, side, favorite: side === favoriteSide, winner: side === winningSide,
|
||||
trend: Boolean(trend && trend.side === side), trendSide: trend?.side || null,
|
||||
dayMove: trend?.dayMove || 0, weekMove: trend?.weekMove || 0,
|
||||
strongTrend: Boolean(trend?.strong), moderateTrend: Boolean(trend?.moderate),
|
||||
entry, band: priceBand(entry), netReturn });
|
||||
entry, band: priceBand(entry), entryCost, exactFeeSchedule: EXACT_GAMMA_FEES && market.feeSchedule != null, netReturn });
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
@@ -308,7 +324,8 @@ const rows = successful.flatMap((result) => result.rows);
|
||||
const report = {
|
||||
generatedAt: new Date().toISOString(), requestedMarkets: MARKET_LIMIT, resolvedMarkets: markets.length,
|
||||
marketsWithHistory: successful.length, failures: histories.filter((result) => result?.error).length,
|
||||
methodology: { horizonDays: HORIZON_DAYS, estimatedRoundTripCostCents: COST_CENTS,
|
||||
methodology: { horizonDays: HORIZON_DAYS, estimatedEntrySlippageCents: COST_CENTS,
|
||||
exactGammaFeeSchedules: EXACT_GAMMA_FEES, settlementRedemptionExitFeeCents: 0,
|
||||
historyFidelityMinutes: 1440,
|
||||
fineGrid:FINE_GRID,
|
||||
testedRules:RULES.length,
|
||||
|
||||
@@ -8,12 +8,15 @@ const EXTERNAL_MARKET_LIMIT = Math.max(100, Math.min(2000, Number(process.env.SH
|
||||
const HISTORY_DAYS = Math.max(14, Math.min(30, Number(process.env.SHOCK_HISTORY_DAYS || 30)));
|
||||
const CONCURRENCY = Math.max(1, Math.min(8, Number(process.env.SHOCK_CONCURRENCY || 6)));
|
||||
const COST = Math.max(0, Math.min(0.05, Number(process.env.SHOCK_COST_CENTS || 0.5) / 100));
|
||||
const EXACT_GAMMA_FEES = process.env.SHOCK_EXACT_GAMMA_FEES === "1";
|
||||
const MIN_ENTRY_PRICE = Math.max(0.02, Math.min(0.40, Number(process.env.SHOCK_MIN_ENTRY_PRICE || 0.08)));
|
||||
const MAX_ENTRY_PRICE = Math.max(0.60, Math.min(0.98, Number(process.env.SHOCK_MAX_ENTRY_PRICE || 0.92)));
|
||||
const CACHE_FILE = String(process.env.SHOCK_CACHE_FILE || "").trim();
|
||||
const OUTPUT_FILE = String(process.env.SHOCK_OUTPUT_FILE || "").trim();
|
||||
const SUMMARY_ONLY = process.env.SHOCK_SUMMARY === "1";
|
||||
const SUMMARY_CANDIDATES = Math.max(0, Math.min(10, Number(process.env.SHOCK_SUMMARY_CANDIDATES || 10)));
|
||||
const CLOSED_ORDER = String(process.env.SHOCK_CLOSED_ORDER || "closedTime").trim() || "closedTime";
|
||||
const CLOSED_ASCENDING = process.env.SHOCK_CLOSED_ASCENDING === "true";
|
||||
const HOUR = 3600;
|
||||
const STRATEGY4_MINIMUM_RUNWAY_HOURS = 14;
|
||||
const HORIZONS = [3, 6, 12, 24];
|
||||
@@ -29,6 +32,19 @@ function parseJson(value) {
|
||||
try { return JSON.parse(value || "[]"); } catch { return []; }
|
||||
}
|
||||
|
||||
function feeScheduleOf(raw) {
|
||||
if (raw?.feesEnabled === false) return { rate: 0, exponent: 1, takerOnly: true };
|
||||
const rate = Number(raw?.feeSchedule?.rate), exponent = Number(raw?.feeSchedule?.exponent);
|
||||
return raw?.feesEnabled === true && Number.isFinite(rate) && rate >= 0 && Number.isFinite(exponent) && exponent > 0
|
||||
? { rate, exponent, takerOnly: raw.feeSchedule.takerOnly !== false } : null;
|
||||
}
|
||||
|
||||
function takerFeePerShare(schedule, price) {
|
||||
const p = Number(price);
|
||||
return schedule && Number.isFinite(p) && p > 0 && p < 1
|
||||
? Number(schedule.rate) * Math.pow(p * (1 - p), Number(schedule.exponent || 1)) : null;
|
||||
}
|
||||
|
||||
async function fetchJson(url, options = {}, attempts = 3) {
|
||||
let lastError;
|
||||
for (let attempt = 0; attempt < attempts; attempt++) {
|
||||
@@ -88,7 +104,7 @@ async function fetchMarkets(limit, universe = "active") {
|
||||
if (universe === "closed") {
|
||||
let cursor = "";
|
||||
while (markets.length < limit) {
|
||||
const params = new URLSearchParams({ closed: "true", order: "volumeNum", ascending: "false", limit: "100", include_tag: "true" });
|
||||
const params = new URLSearchParams({ closed: "true", order: CLOSED_ORDER, ascending: String(CLOSED_ASCENDING), limit: "100", include_tag: "true" });
|
||||
if (cursor) params.set("after_cursor", cursor);
|
||||
const payload = await fetchJson(`${GAMMA}/markets/keyset?${params}`), page = payload?.markets;
|
||||
if (!Array.isArray(page) || !page.length) break;
|
||||
@@ -100,7 +116,7 @@ async function fetchMarkets(limit, universe = "active") {
|
||||
if (!id || seen.has(id) || !resolved || labels[0] !== "yes" || labels[1] !== "no" || tokens.length !== 2) continue;
|
||||
seen.add(id);
|
||||
const endTs = Date.parse(raw.endDate || raw.events?.[0]?.endDate || "") / 1000;
|
||||
markets.push({ id, token: tokens[0], category: categoryOf(raw), question: raw.question || "",
|
||||
markets.push({ id, token: tokens[0], category: categoryOf(raw), question: raw.question || "", feeSchedule: feeScheduleOf(raw),
|
||||
eventTitle: raw.events?.[0]?.title || "", endTs: Number.isFinite(endTs) ? endTs : null,
|
||||
eventKey: String(raw.events?.[0]?.id || raw.events?.[0]?.slug || raw.eventId || id) });
|
||||
if (markets.length >= limit) break;
|
||||
@@ -124,7 +140,7 @@ async function fetchMarkets(limit, universe = "active") {
|
||||
if (skipped < ACTIVE_SKIP) { skipped++; continue; }
|
||||
seen.add(id);
|
||||
const endTs = Date.parse(raw.endDate || raw.events?.[0]?.endDate || "") / 1000;
|
||||
markets.push({ id, token: tokens[0], category: categoryOf(raw), question: raw.question || "",
|
||||
markets.push({ id, token: tokens[0], category: categoryOf(raw), question: raw.question || "", feeSchedule: feeScheduleOf(raw),
|
||||
eventTitle: raw.events?.[0]?.title || "", endTs: Number.isFinite(endTs) ? endTs : null,
|
||||
eventKey: String(raw.events?.[0]?.id || raw.events?.[0]?.slug || raw.eventId || id) });
|
||||
if (markets.length >= limit) break;
|
||||
@@ -226,6 +242,7 @@ function observations(market, points) {
|
||||
const future = atOrAfter(points, current.t + horizon * HOUR);
|
||||
if (!future || future.t - (current.t + horizon * HOUR) > 2 * HOUR) continue;
|
||||
rows.push({ marketId: market.id, eventKey: market.eventKey, category: market.category, question: market.question,
|
||||
feeSchedule: market.feeSchedule,
|
||||
endTs: Number(market.endTs), hardSettlementJumpRisk: hardSettlementJumpRisk(market), observedAt: current.t,
|
||||
evaluatedAt: future.t, horizon, price: current.p, futurePrice: future.p, ...features });
|
||||
captured = true;
|
||||
@@ -255,10 +272,13 @@ function simulate(row, rule) {
|
||||
if (entry < MIN_ENTRY_PRICE || entry > MAX_ENTRY_PRICE) return null;
|
||||
const band = priceBand(entry);
|
||||
if (rule.band !== "All" && band !== rule.band) return null;
|
||||
const rawNetReturn = exit / entry - 1 - COST / entry;
|
||||
const entryFee = EXACT_GAMMA_FEES ? takerFeePerShare(row.feeSchedule, entry) : null;
|
||||
const exitFee = EXACT_GAMMA_FEES ? takerFeePerShare(row.feeSchedule, exit) : null;
|
||||
const roundTripCost = EXACT_GAMMA_FEES && Number.isFinite(entryFee) && Number.isFinite(exitFee) ? entryFee + exitFee : COST;
|
||||
const rawNetReturn = (exit - entry - roundTripCost) / entry;
|
||||
const netReturn = Math.max(-1, Math.min(2, rawNetReturn));
|
||||
return { marketId: row.marketId, eventKey: row.eventKey, observedAt: row.observedAt, evaluatedAt: row.evaluatedAt,
|
||||
category: row.category, band, side, entry, exit, netReturn };
|
||||
category: row.category, band, side, entry, exit, roundTripCost, exactFeeSchedule: EXACT_GAMMA_FEES && Number.isFinite(entryFee) && Number.isFinite(exitFee), netReturn };
|
||||
}
|
||||
|
||||
function summary(rows) {
|
||||
@@ -399,10 +419,12 @@ const report = { generatedAt: new Date().toISOString(), requestedMarkets: MARKET
|
||||
archivePassed: finalists.filter((candidate) => candidate.passesArchive).length,
|
||||
methodology: { requestedHistoryDays: HISTORY_DAYS, medianHistoryCoverageDays: +medianCoverageDays.toFixed(2),
|
||||
maximumHistoryCoverageDays: +(coverageDays.at(-1) || 0).toFixed(2), observationSpacingHours: 3, horizons: HORIZONS, windows: WINDOWS,
|
||||
costCents: COST * 100, entryPriceRange: [MIN_ENTRY_PRICE, MAX_ENTRY_PRICE], returnWinsorization: [-1, 2],
|
||||
costCents: COST * 100, exactGammaFeeSchedules: EXACT_GAMMA_FEES, feeFallbackCents: COST * 100,
|
||||
entryPriceRange: [MIN_ENTRY_PRICE, MAX_ENTRY_PRICE], returnWinsorization: [-1, 2],
|
||||
eventSplit: "25% deterministic event-disjoint holdout reserved before search",
|
||||
split: "Remaining events use 60% train / 20% validation / 20% untouched chronological holdout with future-mark purge",
|
||||
clusterUnit: "Polymarket event", refinementSource: "Only base rules with a positive train lower bound are refined by category and entry band",
|
||||
closedArchiveOrder: `${CLOSED_ORDER} ${CLOSED_ASCENDING ? "ascending" : "descending"}`,
|
||||
strategy4ContractGate: `Reject path barriers and exact numeric ranges; require ${STRATEGY4_MINIMUM_RUNWAY_HOURS} hours to market end at entry`,
|
||||
limitation: "Current active-market selection is survivorship biased; any holdout winner still requires resolved-market external validation" },
|
||||
partitions: Object.fromEntries(Object.entries(partitions).map(([key, value]) => [key, value.length])), exactStrategy3, exactStrategy4,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
const GAMMA = "https://gamma-api.polymarket.com";
|
||||
const CLOB = "https://clob.polymarket.com";
|
||||
const MARKET_LIMIT = Math.max(10, Math.min(500, Number(process.env.EVAL_MARKETS || 80)));
|
||||
const MARKET_LIMIT = Math.max(10, Math.min(1000, Number(process.env.EVAL_MARKETS || 80)));
|
||||
const ACTIVE_SKIP = Math.max(0, Math.min(5000, Number(process.env.EVAL_SKIP || 0)));
|
||||
const CONCURRENCY = Math.max(1, Math.min(12, Number(process.env.EVAL_CONCURRENCY || 6)));
|
||||
const HORIZONS = [...new Set(String(process.env.EVAL_HORIZONS || "6,12,24,72").split(",")
|
||||
.map(Number).filter((value) => Number.isFinite(value) && value >= 1 && value <= 168))].sort((a, b) => a - b);
|
||||
const COST_CENTS = Math.max(0, Math.min(5, Number(process.env.EVAL_COST_CENTS || 0.5)));
|
||||
const EXACT_GAMMA_FEES = process.env.EVAL_EXACT_GAMMA_FEES === "1";
|
||||
const HOUR = 3600;
|
||||
|
||||
const CATEGORY_RULES = [
|
||||
@@ -26,6 +27,18 @@ function categoryOf(raw) {
|
||||
return CATEGORY_RULES.find(([, keys]) => tags.some((tag) => keys.includes(tag)))?.[0] || "Other";
|
||||
}
|
||||
|
||||
function feeScheduleOf(raw) {
|
||||
const rate = Number(raw?.feeSchedule?.rate), exponent = Number(raw?.feeSchedule?.exponent);
|
||||
return Number.isFinite(rate) && rate >= 0 && Number.isFinite(exponent) && exponent > 0
|
||||
? { rate, exponent } : null;
|
||||
}
|
||||
|
||||
function takerFeePerShare(schedule, price) {
|
||||
const p = Number(price), rate = Number(schedule?.rate), exponent = Number(schedule?.exponent);
|
||||
if (!(p > 0 && p < 1) || !Number.isFinite(rate) || rate < 0 || !Number.isFinite(exponent) || exponent <= 0) return null;
|
||||
return rate * Math.pow(p * (1 - p), exponent);
|
||||
}
|
||||
|
||||
async function fetchJson(url, attempts = 3) {
|
||||
let lastError;
|
||||
for (let attempt = 0; attempt < attempts; attempt++) {
|
||||
@@ -116,12 +129,21 @@ function evaluateMarket(market, points) {
|
||||
const exit = signal.side === "YES" ? future.p : 1 - future.p;
|
||||
const fadeExit = signal.side === "YES" ? 1 - future.p : future.p;
|
||||
const grossReturn = exit / entry - 1;
|
||||
const netReturn = grossReturn - (COST_CENTS / 100) / entry;
|
||||
const entryFee = EXACT_GAMMA_FEES ? takerFeePerShare(market.feeSchedule, entry) : null;
|
||||
const exitFee = EXACT_GAMMA_FEES ? takerFeePerShare(market.feeSchedule, exit) : null;
|
||||
const roundTripCost = COST_CENTS / 100
|
||||
+ (EXACT_GAMMA_FEES && Number.isFinite(entryFee) && Number.isFinite(exitFee) ? entryFee + exitFee : 0);
|
||||
const netReturn = grossReturn - roundTripCost / entry;
|
||||
const fadeEntryFee = EXACT_GAMMA_FEES ? takerFeePerShare(market.feeSchedule, fadeEntry) : null;
|
||||
const fadeExitFee = EXACT_GAMMA_FEES ? takerFeePerShare(market.feeSchedule, fadeExit) : null;
|
||||
const fadeRoundTripCost = COST_CENTS / 100
|
||||
+ (EXACT_GAMMA_FEES && Number.isFinite(fadeEntryFee) && Number.isFinite(fadeExitFee) ? fadeEntryFee + fadeExitFee : 0);
|
||||
const fadeNetReturn = fadeEntry > 0.02 && fadeEntry < 0.98
|
||||
? fadeExit / fadeEntry - 1 - (COST_CENTS / 100) / fadeEntry : null;
|
||||
? fadeExit / fadeEntry - 1 - fadeRoundTripCost / fadeEntry : null;
|
||||
outcomes.push({ marketId: market.id, eventKey: market.eventKey, question: market.question, category: market.category,
|
||||
type: signal.type, side: signal.side, band: priceBand(entry), entry, exit, horizonHours,
|
||||
grossReturn, netReturn, fadeNetReturn, hourMove: signal.hourMove, dayMove: signal.dayMove, weekMove: signal.weekMove,
|
||||
grossReturn, netReturn, fadeNetReturn, roundTripCost, exactFeeSchedule: EXACT_GAMMA_FEES && market.feeSchedule != null,
|
||||
hourMove: signal.hourMove, dayMove: signal.dayMove, weekMove: signal.weekMove,
|
||||
observedAt: current.t, evaluatedAt: future.t });
|
||||
captured = true;
|
||||
}
|
||||
@@ -261,11 +283,12 @@ function chronologicalEvaluation(rows) {
|
||||
|
||||
async function fetchActiveMarkets(limit, skip = 0) {
|
||||
const markets = [], seen = new Set(), pageSize = 100;
|
||||
let eligibleSkipped = 0;
|
||||
for (let offset = 0; markets.length < limit && offset < (limit + skip) * 4; offset += pageSize) {
|
||||
let eligibleSkipped = 0, cursor = "";
|
||||
while (markets.length < limit) {
|
||||
const params = new URLSearchParams({ active: "true", closed: "false", archived: "false", include_tag: "true",
|
||||
limit: String(pageSize), offset: String(offset), order: "volume24hr", ascending: "false" });
|
||||
const page = await fetchJson(`${GAMMA}/markets?${params}`);
|
||||
limit: String(pageSize), order: "volume24hr", ascending: "false" });
|
||||
if (cursor) params.set("after_cursor", cursor);
|
||||
const payload = await fetchJson(`${GAMMA}/markets/keyset?${params}`), page = payload?.markets;
|
||||
if (!Array.isArray(page) || !page.length) break;
|
||||
for (const market of page) {
|
||||
const id = String(market.id || ""), labels = parseJson(market.outcomes).map((outcome) => String(outcome).trim().toLowerCase());
|
||||
@@ -274,7 +297,8 @@ async function fetchActiveMarkets(limit, skip = 0) {
|
||||
seen.add(id); markets.push(market);
|
||||
if (markets.length >= limit) break;
|
||||
}
|
||||
if (page.length < pageSize) break;
|
||||
if (page.length < pageSize || !payload.next_cursor || payload.next_cursor === cursor) break;
|
||||
cursor = payload.next_cursor;
|
||||
}
|
||||
return markets.slice(0, limit);
|
||||
}
|
||||
@@ -283,7 +307,7 @@ const rawMarkets = await fetchActiveMarkets(MARKET_LIMIT, ACTIVE_SKIP);
|
||||
const markets = rawMarkets.map((raw) => ({ id: String(raw.id), question: raw.question || "", category: categoryOf(raw),
|
||||
binaryLabels: parseJson(raw.outcomes).map((outcome) => String(outcome).trim().toLowerCase()),
|
||||
eventKey: String(raw.events?.[0]?.id || raw.events?.[0]?.slug || raw.eventId || raw.id),
|
||||
tokenId: String(parseJson(raw.clobTokenIds)[0] || "") })).filter((market) => market.id && market.tokenId
|
||||
tokenId: String(parseJson(raw.clobTokenIds)[0] || ""), feeSchedule: feeScheduleOf(raw) })).filter((market) => market.id && market.tokenId
|
||||
&& market.binaryLabels[0] === "yes" && market.binaryLabels[1] === "no");
|
||||
const histories = await mapLimit(markets, CONCURRENCY, async (market) => {
|
||||
const data = await fetchJson(`${CLOB}/prices-history?market=${encodeURIComponent(market.tokenId)}&interval=1m&fidelity=60`);
|
||||
@@ -298,7 +322,7 @@ const primaryOutcomes = outcomes.filter((row) => row.horizonHours === primaryHor
|
||||
const report = {
|
||||
generatedAt: new Date().toISOString(), marketLimit: MARKET_LIMIT, activeMarketsSkipped: ACTIVE_SKIP, marketsWithHistory: successful.length,
|
||||
methodology: { horizonHours: HORIZONS, primaryHorizon, observationBucketHours: 6, historyInterval: "1m", fidelityMinutes: 60,
|
||||
estimatedRoundTripCostCents: COST_CENTS, clusterUnit: "event",
|
||||
estimatedRoundTripSlippageCents: COST_CENTS, exactGammaFeeSchedules: EXACT_GAMMA_FEES, clusterUnit: "event",
|
||||
note: "Current active-market selection and current category tags are a survivorship-biased proxy; signal inputs and future marks are time-ordered without lookahead. Confidence intervals cluster correlated markets by Polymarket event." },
|
||||
overall: summarize(primaryOutcomes), byType: grouped(primaryOutcomes, "type"), byCategory: grouped(primaryOutcomes, "category"),
|
||||
byBand: grouped(primaryOutcomes, "band"), bySide: grouped(primaryOutcomes, "side"),
|
||||
|
||||
@@ -128,7 +128,7 @@ async function main() {
|
||||
const branch = process.env.RUNTIME_BRANCH || "runtime-state";
|
||||
const pathname = process.env.RUNTIME_STATE_PATH || "runtime/state.json";
|
||||
const arenaUrl = (process.env.ARENA_URL || "https://polymarket-site-eta.vercel.app").replace(/\/$/, "");
|
||||
const expectedBuild = Number(required("EXPECTED_BUILD", "102"));
|
||||
const expectedBuild = Number(required("EXPECTED_BUILD", "103"));
|
||||
await ensureRuntimeBranch(repository, branch);
|
||||
const prior = await readRuntimeFile(repository, branch, pathname);
|
||||
if (prior.snapshot) validateRuntimeSnapshot(prior.snapshot, 0, { allowIncomplete: true });
|
||||
|
||||
@@ -9,7 +9,7 @@ const runner = fs.readFileSync(new URL("./run-autonomous-cycle.mjs", import.meta
|
||||
const resolutionAudit = JSON.parse(fs.readFileSync(new URL("../research/resolution-week-no-audit.json", import.meta.url), "utf8"));
|
||||
const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]);
|
||||
|
||||
assert.equal(build, 102);
|
||||
assert.equal(build, 103);
|
||||
assert.deepEqual([...ALLOWED_RUNTIME_KEYS].sort(), ["pma_agents_v2", "pma_suggestions_v5"]);
|
||||
assert.match(index, /function collectPublicRuntimeItems\(\)/);
|
||||
assert.match(index, /const PUBLIC_RUNTIME_KEYS=Object\.freeze\(\[AGENTS_KEY,SUG_KEY\]\)/);
|
||||
@@ -26,7 +26,7 @@ assert.match(api, /Buffer\.from\(file\.content/);
|
||||
assert.match(api, /searchParams\.set\("runtime", `\$\{Date\.now\(\)\}/);
|
||||
assert.match(workflow, /cron: "2,7,12,17,22,27,32,37,42,47,52,57 \* \* \* \*"/);
|
||||
assert.match(workflow, /contents: write/);
|
||||
assert.match(workflow, /EXPECTED_BUILD: "102"/);
|
||||
assert.match(workflow, /EXPECTED_BUILD: "103"/);
|
||||
assert.match(index, /saveSuggestions\(sugs,markets\.length,analysisMarkets\.length,bundleAudit\)/);
|
||||
assert.match(index, /const NEG_RISK_EVENT_SCAN_LIMIT=1000;/);
|
||||
assert.match(index, /bundleOpportunityTelemetry:true/);
|
||||
|
||||
Reference in New Issue
Block a user