Coordinate high-risk agent exposure

This commit is contained in:
Theodore Song
2026-08-18 10:32:15 -04:00
parent 0db53c46f8
commit 7b81dbfa4b
3 changed files with 77 additions and 37 deletions
+60 -26
View File
@@ -341,7 +341,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<nav class="topnav">
<div class="brand">
<div class="logo">🏆</div>
<div><div class="brand-name">Polymarket Arena</div><div class="brand-sub">10 agents · 5 core + 5 aggressive</div><div class="build-badge">Adaptive strategy 40 · build 43</div></div>
<div><div class="brand-name">Polymarket Arena</div><div class="brand-sub">10 agents · 5 core + 5 aggressive</div><div class="build-badge">Adaptive strategy 42 · build 44</div></div>
</div>
<div class="tabs" id="tabs">
<button class="tab" data-tab="overview">Overview</button>
@@ -363,7 +363,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<div class="personal-banner" id="personalBanner">
<b>Personal research mode.</b> This copy is for your own analysis, paper tracking, and manual trade research only. It does not pool money, onboard investors, custody funds, bypass eligibility rules, or place orders without your manual approval.
</div>
<div class="live-build-banner"><b>Build 43 active:</b> matured signals are repriced even after their markets leave the top-500 activity scan, removing a survivorship bias from adaptive learning. Strategy lineage 40 and its return baseline continue unchanged. This remains paper trading; profits are not guaranteed.</div>
<div class="live-build-banner"><b>Build 44 active:</b> gap-prone and crypto positions are limited to one agent, and each historically blocked exploration market has one designated explorer. This prevents agents from duplicating the same high-risk loss. Strategy 42 starts a clean return baseline. This remains paper trading; profits are not guaranteed.</div>
<!-- ============ OVERVIEW ============ -->
<section class="tabpanel" data-tab="overview">
@@ -745,7 +745,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
</section>
<footer>
Build 43 · Adaptive strategy 40 · Paper trading only · Live prices from Polymarket's public Gamma API · Not financial advice ·
Build 44 · Adaptive strategy 42 · Paper trading only · Live prices from Polymarket's public Gamma API · Not financial advice ·
<a class="market-link" href="https://github.com/theodore-song/polymarket-analyst" target="_blank" rel="noopener">Source on GitHub</a>
</footer>
</div>
@@ -774,8 +774,9 @@ const POLITICS_TREND_MIN_HOLD_HOURS = 72;
const EXIT_CONFIRM_HOURS = 6;
const AGENTS_KEY = "pma_agents_v2";
const SUG_KEY = "pma_suggestions_v5";
const BUILD_VERSION = 43;
const SUGGESTION_ENGINE_VERSION = 40;
const BUILD_VERSION = 44;
const SUGGESTION_ENGINE_VERSION = 42;
const PREVIOUS_STRATEGY_VERSION = 40;
const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40});
function normalizedStrategyVersion(value){
const version=Number(value||0);
@@ -1187,12 +1188,16 @@ function effectiveEntryEdge(s){
const net=Math.abs(Number(s&&s.net_edge));
return Number.isFinite(net)?net:0;
}
function isGapProneTrade(s={}){
const days=Number(s.days_to_resolution),entry=Number(s.entry_price),dayMove=Math.abs(Number(s.price_change_1d||0));
return (Number.isFinite(days)&&days<=14)||(Number.isFinite(entry)&&(entry<=0.22||entry>=0.80))
||dayMove>=0.045||s.signal_type==="reversal"||Boolean(s.jump_risk);
}
function maxAgentOverlapForTrade(s={}){return (isGapProneTrade(s)||(s.category||"Other")==="Crypto")?1:2;}
function tradeLossBudgetPct(cfg,s={}){
const aggressive=Boolean(cfg&&cfg.aggressive);
let cap=aggressive?MAX_AGGRESSIVE_TRADE_LOSS_PCT:MAX_CORE_TRADE_LOSS_PCT;
const days=Number(s.days_to_resolution),entry=Number(s.entry_price),dayMove=Math.abs(Number(s.price_change_1d||0));
const gapProne=(Number.isFinite(days)&&days<=14)||(Number.isFinite(entry)&&(entry<=0.22||entry>=0.80))
||dayMove>=0.045||s.signal_type==="reversal"||Boolean(s.jump_risk);
const gapProne=isGapProneTrade(s);
if(gapProne)cap=Math.min(cap,aggressive?MAX_AGGRESSIVE_GAP_TRADE_LOSS_PCT:MAX_CORE_GAP_TRADE_LOSS_PCT);
return +cap.toFixed(4);
}
@@ -1672,7 +1677,7 @@ function buildSignalCalibration(ledger){
const buckets={};
for(const outcome of (ledger&&ledger.outcomes)||[]){
const age=Math.max(0,Date.now()-new Date(outcome.evaluated_at||0).getTime()),version=normalizedStrategyVersion(outcome.strategy_version);
const versionWeight=version===SUGGESTION_ENGINE_VERSION?1:(version===SUGGESTION_ENGINE_VERSION-1?0.55:0.25);
const versionWeight=version===SUGGESTION_ENGINE_VERSION?1:(version===PREVIOUS_STRATEGY_VERSION?0.55:0.25);
const weight=Math.exp(-age/(30*86400000))*versionWeight;
const ret=clamp(Number(outcome.return||0),-1,2);if(!Number.isFinite(ret))continue;
learningFeatures(outcome).forEach(key=>{const b=buckets[key]||(buckets[key]={weight:0,sum:0,sumSq:0,wins:0,count:0});
@@ -1713,7 +1718,7 @@ function buildAdaptiveProfile(p){
const version=normalizedStrategyVersion(trade.strategy_version);if(version<34)return;
const age=Math.max(0,Date.now()-new Date(trade.closed_at||trade.opened_at||0).getTime());
const recency=Number.isFinite(age)?Math.exp(-age/(45*86400000)):0.4;
const versionWeight=version===SUGGESTION_ENGINE_VERSION?1:(version===SUGGESTION_ENGINE_VERSION-1?0.45:0.20);
const versionWeight=version===SUGGESTION_ENGINE_VERSION?1:(version===PREVIOUS_STRATEGY_VERSION?0.45:0.20);
const weight=recency*versionWeight*(closed?1:Math.min(0.30,Math.max(0.08,daysHeld(trade)/20)));
const ret=tradeReturnForLearning(trade,closed);
if(!Number.isFinite(ret)||weight<=0)return;
@@ -1742,10 +1747,16 @@ function buildAdaptiveProfile(p){
best:ranked[0]?{feature:ranked[0][0],score:ranked[0][1].score}:null,
worst:ranked.length?{feature:ranked[ranked.length-1][0],score:ranked[ranked.length-1][1].score}:null};
}
function stableExploration(agentId,marketId){
const text=`${agentId}:${marketId}`;let h=2166136261;
function stableHash(text){
let h=2166136261;
for(let i=0;i<text.length;i++){h^=text.charCodeAt(i);h=Math.imul(h,16777619);}
return (h>>>0)%100<15;
return h>>>0;
}
function stableExploration(agentId,marketId){
const id=String(marketId||"");
if(stableHash(`lane:${id}`)%100>=15)return false;
const owner=AGENTS[stableHash(`owner:${id}`)%AGENTS.length];
return Boolean(owner&&owner.id===agentId);
}
function historicalOpportunityPrior(s){
const rows=[];
@@ -2157,7 +2168,8 @@ function reduceStrategyOverlap(st){
});
Object.values(groups).filter(g=>g.length>1).forEach(group=>{
group.sort((a,b)=>(b.score-a.score)||((b.pos.value||0)-(a.pos.value||0)));
group.slice(2).forEach(({p,pos})=>closePosition(p,pos,"Strict overlap guard limited this market to two agents","EXIT"));
const cap=Math.min(...group.map(({pos})=>maxAgentOverlapForTrade(pos)));
group.slice(cap).forEach(({p,pos})=>closePosition(p,pos,`Overlap guard limited this ${cap===1?"high-risk ":""}market to ${cap===1?"one agent":"two agents"}`,"EXIT"));
});
AGENTS.filter(a=>a.kind==="strategy").forEach(cfg=>{
const p=st.agents&&st.agents[cfg.id]; if(p)p.positions=(p.positions||[]).filter(pos=>!pos.closed_at);
@@ -2229,7 +2241,8 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
if(s.volume_24hr<500&&s.conviction<62)return reject("activity");
if(s.evidence_score!=null&&s.evidence_score<0.46)return reject("evidence");
const peer=(peerStats&&peerStats[`${s.market_id}:${s.side}`])||{};
if(avoid.has(String(s.market_id))&&!(Number(peer.same||0)<2&&Number(peer.opposite||0)===0))return reject("overlap");
const overlapCap=maxAgentOverlapForTrade(s);
if(avoid.has(String(s.market_id))&&!(Number(peer.same||0)<overlapCap&&Number(peer.opposite||0)===0))return reject("overlap");
if(hasPosition(p,s.market_id))return reject("already_held");
if(hasRecentStop(p,s.market_id))return reject("cooldown");
if(focus!=="All"&&focus&&s.category!==focus)return reject("focus");
@@ -2272,6 +2285,7 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
strategy_version:SUGGESTION_ENGINE_VERSION,
build_version:BUILD_VERSION,
momentum_strength:s.momentum_strength,signal_strength:s.signal_strength,signal_confidence:s.signal_confidence,signal_type:s.signal_type,price_change_1d:s.price_change_1d,price_change_1w:s.price_change_1w,
days_to_resolution:s.days_to_resolution,jump_risk:Boolean(s.jump_risk),
learning_score:s.learning_score,learning_confidence:s.learning_confidence,market_learning_score:s.market_learning_score,market_learning_confidence:s.market_learning_confidence,
learning_state:s.learning_state,market_learning_state:s.market_learning_state,
historical_prior_score:s.historical_prior_score,historical_prior_confidence:s.historical_prior_confidence,historical_prior_features:s.historical_prior_features,
@@ -4229,7 +4243,7 @@ window.PMA_ENGINE_DIAGNOSTICS=Object.freeze({
historicalPriceFeatures,
offlineCachePolicy,
gainStopTargets:(entry)=>GAIN_STOP_TIERS.map(t=>gainStopTarget({entry_price:Number(entry),cost:1,shares:1,gain_stops:{}},t)),
rules:Object.freeze({minimumPolicyHoldHours:MIN_POLICY_HOLD_HOURS,politicsTrendHoldHours:POLITICS_TREND_MIN_HOLD_HOURS,exitConfirmationHours:EXIT_CONFIRM_HOURS,maxAgentOverlap:2,materialOverlapPct:1.25,stopLossPct:18,
rules:Object.freeze({minimumPolicyHoldHours:MIN_POLICY_HOLD_HOURS,politicsTrendHoldHours:POLITICS_TREND_MIN_HOLD_HOURS,exitConfirmationHours:EXIT_CONFIRM_HOURS,maxAgentOverlap:2,gapProneMaxAgentOverlap:1,globalExplorationOwner:true,materialOverlapPct:1.25,stopLossPct:18,
coreTradeLossPct:MAX_CORE_TRADE_LOSS_PCT*100,aggressiveTradeLossPct:MAX_AGGRESSIVE_TRADE_LOSS_PCT*100,
coreGapTradeLossPct:MAX_CORE_GAP_TRADE_LOSS_PCT*100,aggressiveGapTradeLossPct:MAX_AGGRESSIVE_GAP_TRADE_LOSS_PCT*100,
offlineEntryMaxAgeMinutes:OFFLINE_ENTRY_MAX_AGE_MS/60000,offlineCacheMaxAgeHours:OFFLINE_CACHE_MAX_AGE_MS/3600000,explorationPct:15,
@@ -4301,6 +4315,8 @@ function runEngineSelfTest(){
const noisyCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array.from({length:24},(_,i)=>i%2?0.12:-0.12)));
const legacyBuildCalibration=buildSignalCalibration({pending:[],outcomes:[Object.assign({},calibrationCandidate,
{strategy_version:41,return:0.10,evaluated_at:closedAt})]});
const currentStrategyCalibration=buildSignalCalibration({pending:[],outcomes:[Object.assign({},calibrationCandidate,
{strategy_version:SUGGESTION_ENGINE_VERSION,return:0.10,evaluated_at:closedAt})]});
const lossLearner=defaultPortfolio();
for(let i=0;i<6;i++)lossLearner.closed.push({strategy_version:SUGGESTION_ENGINE_VERSION,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
realized_pnl:-50,opened_at:hoursAgo(72+i),closed_at:closedAt});
@@ -4310,7 +4326,7 @@ function runEngineSelfTest(){
const lossOpportunity=learnedOpportunity(AGENTS[0],lossLearner,{market_id:lossMarketId,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42},lossProfile);
const lossDecision=adaptiveDecision(AGENTS[0],lossLearner,5,10,STARTING_BALANCE);
const legacyLossLearner=defaultPortfolio();
for(let i=0;i<4;i++)legacyLossLearner.closed.push({strategy_version:SUGGESTION_ENGINE_VERSION-1,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
for(let i=0;i<4;i++)legacyLossLearner.closed.push({strategy_version:PREVIOUS_STRATEGY_VERSION,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
realized_pnl:-50,opened_at:hoursAgo(72+i),closed_at:closedAt});
const legacyLossDecision=adaptiveDecision(AGENTS[0],legacyLossLearner,5,10,STARTING_BALANCE);
const coreRiskBudget=tradeLossBudgetPct(AGENTS[0],{entry_price:0.50,days_to_resolution:40,price_change_1d:0.01,signal_type:"trend"});
@@ -4325,6 +4341,16 @@ function runEngineSelfTest(){
["value","momentum","favorite"].forEach((id,i)=>mock.agents[id].positions.push({market_id:"overlap-test",question:"Overlap test",side:"YES",shares:1000,current_price:0.5,entry_price:0.5,cost:500,value:500,unrealized_pnl:0,conviction:70-i,opened_at:hoursAgo(24)}));
reduceStrategyOverlap(mock);
const overlapRemaining=AGENTS.reduce((sum,a)=>sum+mock.agents[a.id].positions.filter(p=>p.market_id==="overlap-test").length,0);
const gapMock={agents:{}};AGENTS.forEach(a=>gapMock.agents[a.id]=defaultPortfolio());
["reversal","conviction"].forEach((id,i)=>gapMock.agents[id].positions.push({market_id:"gap-overlap-test",question:"Gap overlap test",side:"NO",category:"Crypto",signal_type:"reversal",
shares:2000,current_price:0.21,entry_price:0.21,cost:420,value:420,original_shares:2000,original_cost:420,unrealized_pnl:0,conviction:75-i,opened_at:hoursAgo(24)}));
reduceStrategyOverlap(gapMock);
const gapOverlapRemaining=AGENTS.reduce((sum,a)=>sum+gapMock.agents[a.id].positions.filter(p=>p.market_id==="gap-overlap-test").length,0);
let explorationMarketId="single-explorer-0",explorationIndex=0,explorationOwners=[];
while(!explorationOwners.length){
explorationOwners=AGENTS.filter(a=>stableExploration(a.id,explorationMarketId));
if(!explorationOwners.length)explorationMarketId=`single-explorer-${++explorationIndex}`;
}
mock.agents.tailalpha.positions.push({market_id:"runner-test",question:"Runner test",side:"YES",shares:100,current_price:0.5,entry_price:0.5,cost:50,value:50,unrealized_pnl:0,conviction:70,opened_at:hoursAgo(24)});
const immaterialRunnerDoesNotBlock=!occupiedStrategyMarkets(mock).has("runner-test");
const rejectBook=defaultPortfolio();rejectBook.cash=9900;
@@ -4343,10 +4369,15 @@ function runEngineSelfTest(){
const executableBook=JSON.parse(JSON.stringify(markOnlyBook));
delete executableBook.positions[0].price_status;
markToMarket(executableBook,{"offline-mark":market({id:"offline-mark",yes_price:0.8,no_price:0.2})},AGENTS[0],{policyExits:true,executeTrades:true});
const lineageMigrationState=defaultState();
lineageMigrationState.engine_version=41;delete lineageMigrationState.strategy_version;
lineageMigrationState.agents.value.engine_baseline={version:41,started_at:hoursAgo(2),equity:9876.54};
reconcileStateVersions(lineageMigrationState);
const buildMigrationState=defaultState();
buildMigrationState.engine_version=43;buildMigrationState.strategy_version=SUGGESTION_ENGINE_VERSION;
buildMigrationState.agents.value.engine_baseline={version:SUGGESTION_ENGINE_VERSION,started_at:hoursAgo(2),equity:9876.54};
reconcileStateVersions(buildMigrationState);
const strategyMigrationState=defaultState();
strategyMigrationState.engine_version=43;strategyMigrationState.strategy_version=PREVIOUS_STRATEGY_VERSION;
strategyMigrationState.agents.value.cash=9876.54;
strategyMigrationState.agents.value.engine_baseline={version:PREVIOUS_STRATEGY_VERSION,started_at:hoursAgo(2),equity:10000};
reconcileStateVersions(strategyMigrationState);
return {buildVersion:BUILD_VERSION,version:SUGGESTION_ENGINE_VERSION,
trend:{ready:trend.trade_ready,quality:trend.quality,side:trend.side,margin:trend.net_edge},
noSignal:{ready:noSignal.trade_ready,quality:noSignal.quality,signal:noSignal.signal_type,margin:noSignal.net_edge},
@@ -4364,10 +4395,12 @@ function runEngineSelfTest(){
expiresOnlyAfterRetryWindow:expiredLedgerState.signal_ledger.pending.length===0&&expiredLedgerState.signal_ledger.expired_ungraded===1,
ledgerIsNetOfCosts:ledgerState.signal_ledger.outcomes[0].gross_return===0.25&&ledgerState.signal_ledger.outcomes[0].estimated_cost_return===0.0125,
independentCalibrationConfidence:singleCalibration.confidence<0.06,
legacyBuildSharesStrategyLineage:normalizedStrategyVersion(41)===SUGGESTION_ENGINE_VERSION,
sameStrategyBuildKeepsFullWeight:legacyBuildCalibration.current_samples===1&&legacyBuildCalibration.buckets["signal:trend"].weight===1,
baselineSurvivesBuildMigration:lineageMigrationState.agents.value.engine_baseline.equity===9876.54&&lineageMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
buildMetadataAdvancesWithoutStrategyReset:lineageMigrationState.engine_version===BUILD_VERSION&&lineageMigrationState.strategy_version===SUGGESTION_ENGINE_VERSION,
legacyBuildMapsToPreviousStrategy:normalizedStrategyVersion(41)===PREVIOUS_STRATEGY_VERSION,
previousStrategyIsDownWeighted:legacyBuildCalibration.current_samples===0&&legacyBuildCalibration.buckets["signal:trend"].weight>0.54&&legacyBuildCalibration.buckets["signal:trend"].weight<=0.55,
currentStrategyKeepsFullWeight:currentStrategyCalibration.current_samples===1&&currentStrategyCalibration.buckets["signal:trend"].weight>0.99,
baselineSurvivesBuildMigration:buildMigrationState.agents.value.engine_baseline.equity===9876.54&&buildMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
buildMetadataAdvancesWithoutStrategyReset:buildMigrationState.engine_version===BUILD_VERSION&&buildMigrationState.strategy_version===SUGGESTION_ENGINE_VERSION,
strategyChangeStartsCleanBaseline:strategyMigrationState.agents.value.engine_baseline.equity===9876.54&&strategyMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
singleOutcomeStaysObserving:singleCalibration.state==="observing"&&singleCalibration.multiplier<=1.01,
stablePositiveIsPromoted:stablePositiveCalibration.state==="promoted"&&stablePositiveCalibration.multiplier>1.02&&stablePositiveCalibration.allowed,
stableNegativeIsBlocked:stableNegativeCalibration.state==="demoted"&&!stableNegativeCalibration.allowed,
@@ -4383,7 +4416,8 @@ function runEngineSelfTest(){
exits:{youngConflict:exitReason(young,fresh,conflict,AGENTS[0]),matureConflict:exitReason(mature,fresh,conflict,AGENTS[0]),
politicsEarlyConflict:exitReason(politicsEarly,fresh,conflict,AGENTS[0]),politicsMatureConflict:exitReason(politicsMature,fresh,conflict,AGENTS[0]),trailing:trailingProfitReason(trailing)},
backtest:{noLookahead:backtestNoLookahead,features:historicalFeatures},
overlapRemaining,immaterialRunnerDoesNotBlock,rejectionAccounting,convictionCapacityCoversTarget,rules:window.PMA_ENGINE_DIAGNOSTICS.rules};
overlapRemaining,gapOverlapRemaining,singleGlobalExplorer:explorationOwners.length===1,
immaterialRunnerDoesNotBlock,rejectionAccounting,convictionCapacityCoversTarget,rules:window.PMA_ENGINE_DIAGNOSTICS.rules};
}
if(new URLSearchParams(location.search).get("engine_test")==="1"){
const output=document.createElement("output");output.id="engineSelfTest";output.hidden=true;output.textContent=JSON.stringify(runEngineSelfTest());document.body.appendChild(output);