diff --git a/README.md b/README.md index e762cdc..774374b 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,9 @@ Each Build 121 cycle also scans the 1,000 most-active Polymarket events for complete negative-risk bundles and logically nested threshold or deadline pairs. Gamma's market-specific fee flag replaces the old blanket 0.5-cent fee reserve for markets declared fee-free. The closest 60 structures are then -repriced from batched CLOB asks from the equal-unit size needed for at least a $50 +ranked by locked-capital efficiency, with the best structure from each independent +event checked before alternates from events already represented. They are repriced +from batched CLOB asks from the equal-unit size needed for at least a $50 paper order up to a $400 verified-notional ceiling. The scanner applies each market's Gamma fee schedule at every consumed ask level and checks the CLOB fee-rate endpoint for a matching enabled or fee-free state. Any opened position is capped to the exact equal-unit size @@ -102,8 +104,12 @@ favorite trends all had 90% upper bounds below zero at 12 hours. A separate rules and found zero robust positive rule. Strategy 64 retains online directional evidence under fee policy 2. A separate probation gate can now use capital after at least 12 current-policy independent events produce a net-of-cost -six-hour lower confidence bound above 1% for every required cohort feature. It -uses at most 0.5% of equity per position, 1% per-agent total capital, one new +six-hour lower confidence bound above 1% for every required cohort feature. +The corresponding 500-market, 1,920-rule chronological audit selected zero +rules in validation at 6, 24, or 72 hours. No directional rule is preapproved; +probation and full sizing must be earned from new event-deduplicated forward +observations. Probation uses at most 0.5% of equity per position, 1% per-agent +total capital, one new position per agent cycle, and one owner per Polymarket event across all agents. Every probation position exits at the matching six-hour executable bid and keeps the 18% stop policy active. Normal directional sizing remains locked until the diff --git a/index.html b/index.html index 5bc42b8..f8614b2 100644 --- a/index.html +++ b/index.html @@ -1320,6 +1320,15 @@ function compareBundleOpportunities(a,b){ ||Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity) ||Number(b.bundle_net_profit_per_unit||-Infinity)-Number(a.bundle_net_profit_per_unit||-Infinity); } +function prioritizeIndependentBundles(candidates,limit=Infinity){ + const ranked=(candidates||[]).slice().sort(compareBundleOpportunities),firstByEvent=[],alternates=[],seenEvents=new Set(); + ranked.forEach(candidate=>{ + const eventKey=bundleEventKey(candidate)||String(candidate.bundle_id||candidate.market_id||""); + if(eventKey&&!seenEvents.has(eventKey)){seenEvents.add(eventKey);firstByEvent.push(candidate);} + else alternates.push(candidate); + }); + return [...firstByEvent,...alternates].slice(0,Math.max(0,Number(limit)||0)); +} function bundleEventExposure(portfolio,eventKey){ const key=String(eventKey||"");if(!key)return 0; return (portfolio&&portfolio.positions||[]).filter(pos=>pos.requires_complete_bundle&&bundleEventKey(pos)===key) @@ -1367,7 +1376,7 @@ async function fetchBundleFeeRates(tokenIds){ await Promise.all(workers);return results; } async function verifyExecutableBundles(candidates){ - const shortlist=(candidates||[]).slice().sort(compareBundleOpportunities).slice(0,BUNDLE_DEPTH_CANDIDATE_LIMIT); + const shortlist=prioritizeIndependentBundles(candidates,BUNDLE_DEPTH_CANDIDATE_LIMIT); const tokenIds=shortlist.flatMap(candidate=>(candidate.bundle_legs||[]).map(leg=>leg.token_id)); const books=await fetchBundleBooks(tokenIds),depthChecked=shortlist.map(candidate=>{ const estimatedCost=Number(candidate.bundle_cost_per_unit),requiredUnits=bundleMinimumExecutionUnits(Math.max(estimatedCost,0.01)); @@ -1421,10 +1430,12 @@ async function fetchNegativeRiskBundles(limit=NEG_RISK_EVENT_SCAN_LIMIT){ const candidates=executableAudit.filter(candidate=>candidate.opportunity_actionable); const seen=new Set(); const unique=candidates.filter(candidate=>{const id=String(candidate.bundle_id||candidate.market_id);if(!id||seen.has(id))return false;seen.add(id);return true;}); + const prioritized=prioritizeIndependentBundles(unique,20); const closest=executableAudit.find(candidate=>candidate.depth_verified)||executableAudit[0]||null; - return {candidates:unique.slice(0,20),audit:{status:"ok",scanned_at:nowIso(),event_scan_limit:limit,events_scanned:events.length, + return {candidates:prioritized,audit:{status:"ok",scanned_at:nowIso(),event_scan_limit:limit,events_scanned:events.length, evaluated_structures:audited.length,depth_checked_structures:executableAudit.length,depth_verified_structures:executableAudit.filter(candidate=>candidate.depth_verified).length, fee_verified_structures:executableAudit.filter(candidate=>candidate.fees_verified).length,actionable_bundles:unique.length, + actionable_events:new Set(unique.map(bundleEventKey).filter(Boolean)).size,returned_events:new Set(prioritized.map(bundleEventKey).filter(Boolean)).size, closest_margin_cents:closest?+(Number(closest.bundle_net_profit_per_unit)*100).toFixed(2):null, closest_return_pct:closest?+(Number(closest.net_edge)*100).toFixed(3):null, closest_title:closest?closest.question:null,closest_url:closest?closest.url:null}}; @@ -7215,6 +7226,11 @@ function runEngineSelfTest(){ Object.assign({},dominanceSuggestion,{bundle_id:"dominance:slow-event:a:b",bundle_event_id:"slow-event",net_edge:0.03,days_to_resolution:30}), Object.assign({},dominanceSuggestion,{bundle_id:"dominance:fast-event:a:b",bundle_event_id:"fast-event",net_edge:0.02,days_to_resolution:5}), ].sort(compareBundleOpportunities); + const independentBundleOrder=prioritizeIndependentBundles([ + Object.assign({},dominanceSuggestion,{bundle_id:"dominance:event-a:a:b",bundle_event_id:"event-a",net_edge:0.05,days_to_resolution:5}), + Object.assign({},dominanceSuggestion,{bundle_id:"dominance:event-a:c:d",bundle_event_id:"event-a",net_edge:0.04,days_to_resolution:5}), + Object.assign({},dominanceSuggestion,{bundle_id:"dominance:event-b:a:b",bundle_event_id:"event-b",net_edge:0.02,days_to_resolution:5}), + ],3); const eventCapBook=defaultPortfolio(); eventCapBook.cash=8800; eventCapBook.positions=[ @@ -7734,6 +7750,7 @@ function runEngineSelfTest(){ }, bundleArbitrage:{ prioritizesReturnPerLockedDay:capitalEfficiencyOrder[0].bundle_event_id==="fast-event", + prioritizesIndependentEventsBeforeAlternates:independentBundleOrder.map(row=>row.bundle_event_id).join(",")==="event-a,event-b,event-a", derivesLegacyBundleEventKey:bundleEventKey({bundle_id:"dominance:legacy-event:market-a:market-b"})==="legacy-event", capsUnderlyingEventExposure:eventCapBook.positions.length===2&&eventCapBook.lastDecision.rejectionCounts.bundle_event_cap===1, blocksLowReturnPerLockedDay:slowCapitalBook.positions.length===0&&slowCapitalBook.lastDecision.rejectionCounts.bundle_capital_efficiency===1, diff --git a/research/adaptive-strategy-64-audit.json b/research/adaptive-strategy-64-audit.json new file mode 100644 index 0000000..3c96c41 --- /dev/null +++ b/research/adaptive-strategy-64-audit.json @@ -0,0 +1,31 @@ +{ + "strategy": 64, + "generated_at": "2026-08-22T12:17:23.498Z", + "requested_markets": 500, + "fetched_markets": 500, + "histories_with_data": 500, + "failures": 0, + "observations": 6115, + "methodology": { + "cost_cents": 0.5, + "observation_spacing_hours": 12, + "split": "60% train / 20% validation / 20% untouched holdout", + "cluster_unit": "Polymarket event", + "candidate_rules": 1920, + "promotion_gate": "Positive event-clustered 90% lower bound with minimum support in train, validation, and holdout" + }, + "probation_rule_ids": [], + "durable_rule_ids": [], + "horizons": [ + {"hours": 6, "train_rows": 3640, "validation_rows": 1226, "test_rows": 1223, "validation_selected": 0, "holdout_passed": 0}, + {"hours": 24, "train_rows": 3482, "validation_rows": 875, "test_rows": 1223, "validation_selected": 0, "holdout_passed": 0}, + {"hours": 72, "train_rows": 3043, "validation_rows": 215, "test_rows": 1223, "validation_selected": 0, "holdout_passed": 0} + ], + "production_decision": { + "preapproved_directional_rules": [], + "six_hour_probation_requires_new_forward_evidence": true, + "full_directional_promotion_requires_new_forward_evidence": true, + "note": "No tested directional rule survived validation at any horizon. This active-market audit can still contain survivorship bias, so it cannot justify relaxing the live evidence gates." + }, + "reproduce": "ADAPTIVE_MARKETS=500 ADAPTIVE_CONCURRENCY=12 npm run evaluate:adaptive" +} diff --git a/scripts/evaluate-adaptive.mjs b/scripts/evaluate-adaptive.mjs index e4a7182..73ba8af 100644 --- a/scripts/evaluate-adaptive.mjs +++ b/scripts/evaluate-adaptive.mjs @@ -103,7 +103,7 @@ function observations(market, points) { } if (!complete) continue; const future = {}; - for (const hours of [24, 72]) { + for (const hours of [6, 24, 72]) { const next = atOrAfter(points, current.t + hours * HOUR); if (next && next.t - (current.t + hours * HOUR) <= 3 * HOUR) future[hours] = next.p; } @@ -203,11 +203,13 @@ function evaluateHorizon(horizon) { } const compact = (stats) => Object.fromEntries(Object.entries(stats).map(([key, value]) => [key, Number.isFinite(value) ? +value.toFixed(5) : value])); -const horizons = [24, 72].map(evaluateHorizon).map((result) => ({ ...result, +const horizons = [6, 24, 72].map(evaluateHorizon).map((result) => ({ ...result, candidates: result.candidates.map((candidate) => ({ rule: candidate.rule, passesHoldout: candidate.passesHoldout, train: compact(candidate.train), validation: compact(candidate.validation), test: compact(candidate.test) })) })); -const passedByHorizon = horizons.map((result) => new Set(result.candidates.filter((candidate) => candidate.passesHoldout).map((candidate) => candidate.rule.id))); -const durableRuleIds = [...passedByHorizon[0]].filter((id) => passedByHorizon[1].has(id)); +const passedByHorizon = Object.fromEntries(horizons.map((result) => [result.horizon, + new Set(result.candidates.filter((candidate) => candidate.passesHoldout).map((candidate) => candidate.rule.id))])); +const probationRuleIds = [...passedByHorizon[6]]; +const durableRuleIds = [...passedByHorizon[24]].filter((id) => passedByHorizon[72].has(id)); console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedMarkets: MARKET_LIMIT, fetchedMarkets: markets.length, historiesWithData: histories.filter((result) => result && !result.error && result.points).length, @@ -215,4 +217,4 @@ console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedMar methodology: { costCents: COST * 100, observationSpacingHours: 12, split: "60% train / 20% validation / 20% untouched holdout", clusterUnit: "Polymarket event", candidateRules: rules.length, promotionGate: "positive event-clustered 90% lower bound with minimum support in train, validation, and holdout" }, - durableRuleIds, horizons }, null, 2)); + probationRuleIds, durableRuleIds, horizons }, null, 2)); diff --git a/scripts/test-autonomous-runtime.mjs b/scripts/test-autonomous-runtime.mjs index 77a32cd..be2f2ea 100644 --- a/scripts/test-autonomous-runtime.mjs +++ b/scripts/test-autonomous-runtime.mjs @@ -8,6 +8,7 @@ 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 adaptiveAudit = JSON.parse(fs.readFileSync(new URL("../research/adaptive-strategy-64-audit.json", import.meta.url), "utf8")); const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]); assert.equal(build, 121); @@ -79,6 +80,8 @@ assert.match(index, /const BUNDLE_MAX_VERIFIED_NOTIONAL=400;/); assert.match(index, /const BUNDLE_EVENT_CAP_PCT=0\.12;/); assert.match(index, /const BUNDLE_MIN_DAILY_RETURN=0\.0002;/); assert.match(index, /function compareBundleOpportunities\(a,b\)/); +assert.match(index, /function prioritizeIndependentBundles\(candidates,limit=Infinity\)/); +assert.match(index, /prioritizesIndependentEventsBeforeAlternates:/); assert.match(index, /function bundleEventExposure\(portfolio,eventKey\)/); assert.match(index, /prioritizesReturnPerLockedDay:/); assert.match(index, /capsUnderlyingEventExposure:/); @@ -108,6 +111,11 @@ 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(adaptiveAudit.strategy, 64); +assert.equal(adaptiveAudit.fetched_markets, 500); +assert.deepEqual(adaptiveAudit.probation_rule_ids, []); +assert.deepEqual(adaptiveAudit.durable_rule_ids, []); +assert.ok(adaptiveAudit.horizons.every((row) => row.validation_selected === 0 && row.holdout_passed === 0)); assert.match(index, /function sportsContestKey\(m\)/); assert.match(index, /function sportsContestNoSuggestions\(markets\)/); assert.match(index, /const SPORTS_FAVORITE_MAX_NEW_PER_CYCLE=1;/);