Make sports forward learner quote aware

This commit is contained in:
Theodore Song
2026-08-20 18:27:39 -04:00
parent 39e893a553
commit 9fca4e303d
4 changed files with 64 additions and 32 deletions
+13 -2
View File
@@ -5,6 +5,8 @@ const CONCURRENCY = Math.max(1, Math.min(12, Number(process.env.SPORTS_FAVORITE_
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)));
const EVENT_LEGS = Math.max(1, Math.min(4, Number(process.env.SPORTS_FAVORITE_EVENT_LEGS || 1)));
const COST_GRID = String(process.env.SPORTS_FAVORITE_COST_GRID_CENTS || "0,1,2,3,4,5")
.split(",").map(Number).filter((value) => Number.isFinite(value) && value >= 0 && value <= 10).map((value) => value / 100);
const HOUR = 3600;
function parseJson(value) {
@@ -161,9 +163,16 @@ const evaluated = rules.map((rule) => {
});
const selected = evaluated.filter((row) => row.validationPassed).sort((a, b) => Number(b.passesHoldout) - Number(a.passesHoldout) || b.holdout.lower - a.holdout.lower);
const baseline = evaluated.find((row) => row.rule.leadHours === 24 && row.rule.minEntry === 0.55 && row.rule.maxEntry === 0.95);
const livePilot = evaluated.find((row) => row.rule.leadHours === 24 && row.rule.minEntry === 0.60 && row.rule.maxEntry === 0.85);
const retiredPilot = evaluated.find((row) => row.rule.leadHours === 24 && row.rule.minEntry === 0.60 && row.rule.maxEntry === 0.85);
const forwardResearch = evaluated.find((row) => row.rule.leadHours === 12 && row.rule.minEntry === 0.60 && row.rule.maxEntry === 0.75);
const compact = (stats) => Object.fromEntries(Object.entries(stats).map(([key, value]) => [key, Number.isFinite(value) ? +value.toFixed(5) : value]));
const candidate = (row) => ({ rule: row.rule, train: compact(row.train), validation: compact(row.validation), holdout: compact(row.holdout), passesHoldout: row.passesHoldout });
function costCurve(rule) {
if (!rule) return [];
const raw = Object.fromEntries(Object.entries(partitions).map(([name, partition]) => [name, tradesFor(partition, rule)]));
return COST_GRID.map((cost) => ({ modeledCostCents: cost * 100, ...Object.fromEntries(Object.entries(raw).map(([name, rows]) => [name,
compact(summarize(rows.map((row) => ({ ...row, netReturn: (row.won ? 1 : 0) / row.entry - 1 - cost / row.entry }))))])) }));
}
const closest = evaluated.filter((row) => row.train.events >= 25 && row.validation.events >= 10 && row.holdout.events >= 10)
.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));
@@ -176,7 +185,9 @@ console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedMar
partitionMarkets: Object.fromEntries(Object.entries(partitions).map(([key, value]) => [key, value.length])),
trainPassed: evaluated.filter((row) => row.trainPassed).length, validationSelected: selected.length,
holdoutPassed: selected.filter((row) => row.passesHoldout).length, baseline: baseline ? candidate(baseline) : null,
livePilot: livePilot ? candidate(livePilot) : null,
retiredPilot: retiredPilot ? candidate(retiredPilot) : null,
forwardResearch: forwardResearch ? candidate(forwardResearch) : null,
costSensitivity: { retiredPilot: costCurve(retiredPilot?.rule), forwardResearch: costCurve(forwardResearch?.rule) },
closestCandidates: closest.slice(0, 15).map(candidate),
candidates: selected.slice(0, 20).map(candidate),
holdoutExamples: (selected.find((row) => row.passesHoldout)?.holdoutRows || []).slice(0, 12)