Require forward proof for sports capital

This commit is contained in:
Theodore Song
2026-08-22 09:08:00 -04:00
parent d32ce0de42
commit d64810fc18
7 changed files with 135 additions and 57 deletions
+6 -3
View File
@@ -1,6 +1,7 @@
const GAMMA = "https://gamma-api.polymarket.com";
const CLOB = "https://clob.polymarket.com";
const MARKET_LIMIT = Math.max(500, Math.min(5000, Number(process.env.SPORTS_FAVORITE_MARKETS || 3000)));
const MARKET_SKIP = Math.max(0, Math.min(20000, Number(process.env.SPORTS_FAVORITE_SKIP || 0)));
const CONCURRENCY = Math.max(1, Math.min(12, Number(process.env.SPORTS_FAVORITE_CONCURRENCY || 10)));
const COST = Math.max(0, Math.min(0.10, Number(process.env.SPORTS_FAVORITE_COST_CENTS || 5) / 100));
const MAX_STALENESS_HOURS = Math.max(1, Math.min(12, Number(process.env.SPORTS_FAVORITE_MAX_STALENESS_HOURS || 3)));
@@ -59,8 +60,9 @@ function gameStart(raw) {
return timestamp(raw.gameStartTime || raw.eventStartTime || event.startTime || event.eventDate);
}
async function fetchMarkets(limit) {
async function fetchMarkets(limit, skip = 0) {
const markets = [], seen = new Set();
let eligibleSeen = 0;
let cursor = "";
while (markets.length < limit) {
const params = new URLSearchParams({ closed: "true", order: "closedTime", ascending: "false", limit: "100", include_tag: "true" });
@@ -75,6 +77,7 @@ async function fetchMarkets(limit) {
if (!id || seen.has(id) || labels[0] !== "yes" || labels[1] !== "no" || tokens.length !== 2 || finalYes == null
|| !startsAt || !closedAt || !sportsMarket(raw)) continue;
seen.add(id);
if (eligibleSeen++ < skip) continue;
markets.push({ id, question: raw.question || "", eventKey: String(raw.events?.[0]?.id || id), event: raw.events?.[0]?.title || "",
tokenId: tokens[0], finalYes, startsAt, closedAt, marketType: String(raw.sportsMarketType || "unknown") });
if (markets.length >= limit) break;
@@ -126,7 +129,7 @@ for (const leadHours of [12, 18, 24, 30, 36]) {
}
}
const markets = await fetchMarkets(MARKET_LIMIT);
const markets = await fetchMarkets(MARKET_LIMIT, MARKET_SKIP);
const histories = await mapLimit(markets, CONCURRENCY, async (market) => {
const data = await fetchJson(`${CLOB}/prices-history?market=${encodeURIComponent(market.tokenId)}&interval=max&fidelity=60`);
const points = (data.history || []).map((point) => ({ t: Number(point.t), p: Number(point.p) }))
@@ -177,7 +180,7 @@ const closest = evaluated.filter((row) => row.train.events >= 25 && row.validati
.sort((a, b) => Math.min(b.train.lower, b.validation.lower, b.holdout.lower)
- Math.min(a.train.lower, a.validation.lower, a.holdout.lower));
console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedMarkets: MARKET_LIMIT, sportsMarkets: markets.length,
console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedMarkets: MARKET_LIMIT, skippedEligibleMarkets: MARKET_SKIP, sportsMarkets: markets.length,
historiesWithData: usable.length, failures: histories.filter((row) => row?.error).length,
methodology: { selection: "most recently closed eligible Yes/No sports markets", decisionAnchor: "published game start",
historyFidelityMinutes: 60, maxPriceStalenessHours: MAX_STALENESS_HOURS, modeledCostCents: COST * 100,
+16 -1
View File
@@ -8,10 +8,13 @@ const workflow = fs.readFileSync(new URL("../.github/workflows/autonomous-cycle.
const runner = fs.readFileSync(new URL("./run-autonomous-cycle.mjs", import.meta.url), "utf8");
const resolutionAudit = JSON.parse(fs.readFileSync(new URL("../research/resolution-week-no-audit.json", import.meta.url), "utf8"));
const sportsContestAudit = JSON.parse(fs.readFileSync(new URL("../research/sports-contest-no-exploration-audit.json", import.meta.url), "utf8"));
const sportsDisjointAudit = JSON.parse(fs.readFileSync(new URL("../research/sports-contest-no-disjoint-10000-audit.json", import.meta.url), "utf8"));
const adaptiveAudit = JSON.parse(fs.readFileSync(new URL("../research/adaptive-strategy-64-audit.json", import.meta.url), "utf8"));
const sportsEvaluator = fs.readFileSync(new URL("./evaluate-sports-favorites.mjs", import.meta.url), "utf8");
const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]);
assert.equal(build, 121);
assert.equal(build, 122);
assert.match(index, /Adaptive strategy 64 · durable evidence · build 122/);
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\]\)/);
@@ -126,6 +129,15 @@ assert.ok(sportsContestAudit.corrected_5000_market_result.validation.lower_95 <
assert.equal(sportsContestAudit.production_constraints.initial_position_pct, 0.5);
assert.equal(sportsContestAudit.production_constraints.total_lane_cap_pct, 3);
assert.equal(sportsContestAudit.production_constraints.exact_entry_fee_required, true);
assert.equal(sportsDisjointAudit.status, "zero-capital-forward-shadow");
assert.equal(sportsDisjointAudit.recent_5000.holdout_rules_passing, 0);
assert.equal(sportsDisjointAudit.older_disjoint_5000.validation_rules_passing, 0);
assert.ok(sportsDisjointAudit.recent_5000.closest_rule.holdout_lower_90 < 0);
assert.ok(sportsDisjointAudit.older_disjoint_5000.closest_rule.holdout_lower_90 < 0);
assert.equal(sportsDisjointAudit.decision.capital_enabled, false);
assert.equal(sportsDisjointAudit.decision.initial_position_pct, 0);
assert.match(sportsEvaluator, /const MARKET_SKIP =/);
assert.match(sportsEvaluator, /fetchMarkets\(MARKET_LIMIT, MARKET_SKIP\)/);
assert.equal(adaptiveAudit.strategy, 64);
assert.equal(adaptiveAudit.fetched_markets, 500);
assert.deepEqual(adaptiveAudit.probation_rule_ids, []);
@@ -134,6 +146,9 @@ assert.ok(adaptiveAudit.horizons.every((row) => row.validation_selected === 0 &&
assert.match(index, /function sportsContestKey\(m\)/);
assert.match(index, /function sportsContestNoSuggestions\(markets\)/);
assert.match(index, /const SPORTS_FAVORITE_MAX_NEW_PER_CYCLE=1;/);
assert.match(index, /const SPORTS_FAVORITE_PILOT_POSITION_PCT=0;/);
assert.match(index, /capitalRequiresForwardPromotion:true,boundedExploration:false/);
assert.match(index, /initialStateIsZeroCapitalShadow:/);
assert.match(index, /function compactDecisionForPublic\(decision\)/);
assert.match(index, /delete out\.learning\.buckets/);