Promote validated 24-hour shock fade

This commit is contained in:
Theodore Song
2026-08-21 16:31:43 -04:00
parent d59f7ace03
commit 6f77c2c3bd
4 changed files with 2271 additions and 93 deletions
+14
View File
@@ -387,6 +387,20 @@ an observation. A positive 90% lower confidence bound above 0.5% is required
before 0.75%-of-equity paper positions can begin, with all concurrent shock
positions capped at 2% of equity.
Build 86 replaces that inconclusive three-hour exit with a narrower rule that
survived a corrected 2,000-market audit. Across 534,894 hourly observations,
the exact accelerating three-hour shock / 825% opposite-side longshot / 24-hour
exit rule retained positive 90% lower bounds in train, validation, chronological
holdout, and a sealed event-disjoint holdout at one-cent modeled cost. The four
partitions contained 69, 70, 104, and 56 independent events respectively. It
did not survive a two-cent stress test, so live candidate construction requires
entry friction of at most half a cent and grades the future exit at the actual
executable bid with another 0.25-cent slippage buffer. Backtest-approved paper
positions begin at 0.5% of equity and total shock exposure is capped at 3%.
Thirty positive forward events with a lower bound above 1% can raise individual
size to 1%; twelve convincingly losing events or a 2% strategy loss demote the
lane to zero capital. Strategy-1 outcomes do not contaminate Strategy-2 evidence.
Build 77 separates the directional learner's evidence lineage from the global
strategy release. Code-history verification found the same trend/reversal
generator and 24-hour/72-hour grading policy in Strategy 51 through Strategy
+121 -73
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 59 · shock research 1 · maker research 3 · build 85</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 59 · shock strategy 2 · maker research 3 · build 86</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 85 active:</b> Shock Reversion now watches exact 3-hour, 8%+ moves with aligned last-hour momentum, then tests the opposite side for three hours using executable entry and exit prices. Its first 30 independent events use zero cash; a positive 90% lower confidence bound is required before tightly capped paper positions can begin. Maker capital remains retired. Strategy 59 directional adaptation still uses only current-strategy closed trades. This remains paper trading; profits are not guaranteed.</div>
<div class="live-build-banner"><b>Build 86 active:</b> Shock Reversion now fades accelerating 4%+ three-hour moves into 825% opposite-side contracts and exits after 24 hours at an executable price. The exact rule survived train, validation, chronological holdout, and sealed event holdout at one-cent modeled cost across 299 independent partition-events. It begins at 0.5% paper positions, can adapt to 1% after 30 positive forward events, and demotes on losing evidence. Maker capital remains retired. 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 85 · Adaptive strategy 59 · Shock research 1 · Maker research 3 · Agent learning 3 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · Not financial advice ·
Build 86 · Adaptive strategy 59 · Shock strategy 2 · Maker research 3 · Agent learning 3 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · 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,10 +774,11 @@ 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 = 85;
const BUILD_VERSION = 86;
const AGENT_LEARNING_VERSION = 3;
const SUGGESTION_ENGINE_VERSION = 59;
const MAKER_STRATEGY_VERSION = 3;
const SHOCK_FADE_STRATEGY_VERSION = 2;
const PREVIOUS_STRATEGY_VERSION = 58;
const DIRECTIONAL_SIGNAL_POLICY_VERSION = 1;
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN = 51;
@@ -890,12 +891,12 @@ const AGENTS = [
rank:(s)=>[...s].sort((a,b)=>(Number(b.evidence_source_count||0)*28+Number(b.evidence_score||0)*32+effectiveEntryEdge(b)*170+b.conviction+Math.log10(Number(b.volume_24hr||0)+1)*5)-(Number(a.evidence_source_count||0)*28+Number(a.evidence_score||0)*32+effectiveEntryEdge(a)*170+a.conviction+Math.log10(Number(a.volume_24hr||0)+1)*5)),
maxNew:7, maxFrac:0.09, minConv:52, kelly:0.46, allocationFrac:0.055, maxPositions:18, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.72, drawdownExposure:0.60},
{id:"reversal", name:"Shock Reversion", emoji:"⧗", color:"#ff5f91", kind:"strategy", aggressive:true,
blurb:"A fast mean-reversion strategy built from a 157,386-observation chronological replay. It watches markets that move at least 8 percentage points in three hours while the final hour still points the same way, then tests the opposite side for exactly three hours. Entries and exits use executable book prices plus modeled slippage, independent events are clustered for confidence, and capital stays at zero until 30 fresh forward events establish a positive lower bound.",
blurb:"A fast mean-reversion strategy selected from 534,894 hourly observations across 2,000 active markets. It fades accelerating moves of at least 4 percentage points over three hours only when the opposite contract is an 825% longshot, entry friction is at most half a cent, and an executable book price exists. It exits after 24 hours, begins at 0.5% paper positions, adapts to 1% after 30 positive independent forward events, and shuts capital off when forward evidence turns negative.",
rank:(s)=>[...s].filter(x=>(x.signal_type==="shock-fade-pilot"&&x.trade_ready)
||(x.trade_ready&&x.signal_type==="trend"&&Math.abs(Number(x.price_change_1d||0))<=0.04&&Number(x.days_to_resolution||0)>=21))
.sort((a,b)=>(Number(b.signal_type==="shock-fade-pilot")-Number(a.signal_type==="shock-fade-pilot"))
||((Number(b.shock_move_3h||0)*120+Number(b.signal_confidence||0)*45+effectiveEntryEdge(b)*180+b.conviction)
-(Number(a.shock_move_3h||0)*120+Number(a.signal_confidence||0)*45+effectiveEntryEdge(a)*180+a.conviction))),
||((Math.abs(Number(b.shock_move_3h||0))*120+Number(b.signal_confidence||0)*45+effectiveEntryEdge(b)*180+b.conviction)
-(Math.abs(Number(a.shock_move_3h||0))*120+Number(a.signal_confidence||0)*45+effectiveEntryEdge(a)*180+a.conviction))),
maxNew:6, maxFrac:0.08, minConv:54, kelly:0.42, maxPositions:18, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.80, drawdownExposure:0.65},
{id:"breakout", name:"Breakout Velocity", emoji:"▲", color:"#00d5b5", kind:"strategy", aggressive:true,
blurb:"A high-tempo breakout strategy that follows confirmed price direction when momentum, fresh volume, tight spreads, and positive modeled edge line up. It builds positions quickly and keeps less idle cash so strong moves can drive the portfolio, knowing failed breakouts will create sharper drawdowns.",
@@ -1280,18 +1281,23 @@ const SPORTS_FAVORITE_SHADOW_LIMIT=20;
const SPORTS_FAVORITE_OUTCOME_LIMIT=240;
const SPORTS_FAVORITE_SLIPPAGE_BUFFER=0.0025;
const SPORTS_FAVORITE_MAX_EXECUTION_COST=0.01;
const SHOCK_FADE_MIN_MOVE=0.08;
const SHOCK_FADE_HORIZON_HOURS=3;
const SHOCK_FADE_MIN_MOVE=0.04;
const SHOCK_FADE_SIGNAL_WINDOW_HOURS=3;
const SHOCK_FADE_EXIT_HOURS=24;
const SHOCK_FADE_GRADE_TOLERANCE_HOURS=2;
const SHOCK_FADE_PILOT_POSITION_PCT=0.0075;
const SHOCK_FADE_TOTAL_CAPITAL_PCT=0.02;
const SHOCK_FADE_INITIAL_POSITION_PCT=0.005;
const SHOCK_FADE_PROMOTED_POSITION_PCT=0.01;
const SHOCK_FADE_TOTAL_CAPITAL_PCT=0.03;
const SHOCK_FADE_VERDICT_EVENTS=12;
const SHOCK_FADE_PROMOTION_EVENTS=30;
const SHOCK_FADE_SHADOW_LIMIT=30;
const SHOCK_FADE_OUTCOME_LIMIT=300;
const SHOCK_FADE_SLIPPAGE_BUFFER=0.0025;
const SHOCK_FADE_MAX_EXECUTION_COST=0.01;
const SHOCK_FADE_MAX_EXECUTION_COST=0.005;
const SHOCK_FADE_MIN_ENTRY=0.08;
const SHOCK_FADE_MAX_ENTRY=0.25;
const SHOCK_FADE_HISTORY_CACHE_MS=10*60*1000;
const SHOCK_FADE_BACKTEST_APPROVED=true;
const MAKER_MAX_QUOTES=6;
const MAKER_MAX_QUOTE_CAPITAL_PCT=0.02;
const MAKER_TOTAL_CAPITAL_PCT=0.10;
@@ -1681,7 +1687,8 @@ function compactSuggestionForSync(s){
requires_live:s.requires_live,bundle_id:s.bundle_id,bundle_side:s.bundle_side,bundle_logic:s.bundle_logic,bundle_cost_per_unit:s.bundle_cost_per_unit,
bundle_payout_per_unit:s.bundle_payout_per_unit,bundle_net_profit_per_unit:s.bundle_net_profit_per_unit,bundle_legs:s.bundle_legs,
days_to_resolution:s.days_to_resolution,event_key:s.event_key,game_start:s.game_start,hours_to_start:s.hours_to_start,
pilot_prior:s.pilot_prior,shock_move_1h:s.shock_move_1h,shock_move_3h:s.shock_move_3h,shock_observed_at:s.shock_observed_at,
pilot_prior:s.pilot_prior,shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,
shock_observed_at:s.shock_observed_at,shock_strategy_version:s.shock_strategy_version,
drivers:s.drivers,rationale:s.rationale,
};
}
@@ -2303,7 +2310,7 @@ function shockFadeExecutableEntry(m,side){
const quoted=side==="YES"?Number(m&&m.best_ask):(Number(m&&m.best_bid)>0?1-Number(m.best_bid):NaN);
if(!(midpoint>0&&midpoint<1&&quoted>=midpoint&&quoted<1))return null;
const price=clamp(quoted+SHOCK_FADE_SLIPPAGE_BUFFER,0.01,0.99),cost=price-midpoint;
return cost<=SHOCK_FADE_MAX_EXECUTION_COST+0.00001?{midpoint,quoted,price,cost}:null;
return price>=SHOCK_FADE_MIN_ENTRY&&price<=SHOCK_FADE_MAX_ENTRY&&cost<=SHOCK_FADE_MAX_EXECUTION_COST+0.00001?{midpoint,quoted,price,cost}:null;
}
function shockFadeExecutableExit(fresh,side){
if(!fresh)return null;
@@ -2313,10 +2320,10 @@ function shockFadeExecutableExit(fresh,side){
}
function shockFadeSuggestion(m,points,observedAt=Date.now()/1000){
if(!m||!(m.clob_token_ids||[])[0]||m.closed||m.accepting_orders===false)return null;
const now=Number(observedAt),p1=historyPointAtOrBefore(points,now-3600),p3=historyPointAtOrBefore(points,now-SHOCK_FADE_HORIZON_HOURS*3600);
const current=Number(m.yes_price);if(!p1||!p3||!(current>0&&current<1))return null;
const move1=current-Number(p1.p),move3=current-Number(p3.p);
if(Math.abs(move3)<SHOCK_FADE_MIN_MOVE||move1*move3<=0)return null;
const now=Number(observedAt),p1=historyPointAtOrBefore(points,now-3600),p2=historyPointAtOrBefore(points,now-2*3600),p3=historyPointAtOrBefore(points,now-SHOCK_FADE_SIGNAL_WINDOW_HOURS*3600);
const current=Number(m.yes_price);if(!p1||!p2||!p3||!(current>0&&current<1))return null;
const move1=current-Number(p1.p),priorMove1=Number(p1.p)-Number(p2.p),move3=current-Number(p3.p);
if(Math.abs(move3)<SHOCK_FADE_MIN_MOVE||move1*move3<=0||Math.abs(move1)<Math.abs(priorMove1))return null;
const side=move3>0?"NO":"YES",execution=shockFadeExecutableEntry(m,side);if(!execution)return null;
const eventKey=String(m.event_id||m.event||m.id),conviction=+clamp(72+Math.abs(move3)*75+Math.abs(move1)*35,72,88).toFixed(1);
return {market_id:String(m.id),question:m.question,event:m.event||"",event_key:eventKey,url:m.url||"",category:m.category||"Other",tags:m.tags||[],
@@ -2325,13 +2332,15 @@ function shockFadeSuggestion(m,points,observedAt=Date.now()/1000){
evidence_score:0.66,evidence_source_count:1,quality:"shock-fade-shadow",side,market_price:+execution.midpoint.toFixed(4),entry_price:+execution.price.toFixed(4),
conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,spread:m.spread,
price_change_1h:+move1.toFixed(4),price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,
shock_move_1h:+move1.toFixed(4),shock_move_3h:+move3.toFixed(4),shock_observed_at:new Date(now*1000).toISOString(),
shock_move_1h:+move1.toFixed(4),shock_prior_move_1h:+priorMove1.toFixed(4),shock_move_3h:+move3.toFixed(4),shock_observed_at:new Date(now*1000).toISOString(),
momentum_strength:+clamp(Math.abs(move3)/0.16,0,1).toFixed(3),signal_strength:0.74,signal_confidence:0.66,signal_type:"shock-fade-shadow",
trade_ready:false,entry_candidate:false,audited_observation_only:true,adaptive_promotion:false,watch_only:true,jump_risk:true,requires_live:true,
days_to_resolution:m.days_to_resolution,pilot_prior:{observations:157386,chronological_holdout_mean:0.09705,event_holdout_mean:0.03953,
event_holdout_lower_90:-0.02187,modeled_cost_cents:0.5,strictly_promoted:false},
drivers:["8%+ three-hour shock","last hour aligned","opposite-side three-hour exit","executable ask and bid","event-clustered forward gate"],
rationale:`Forward research only: YES moved ${(move3*100).toFixed(2)} points in three hours and ${(move1*100).toFixed(2)} in the aligned final hour. The pilot fades that move with ${side} at modeled ${pct(execution.price)}, ${(execution.cost*100).toFixed(2)} cents above midpoint, and targets an executable exit three hours later. Chronological replay was positive, but its event-disjoint confidence bound crossed zero, so cash remains untouched until 30 independent forward events establish a positive lower bound.`};
shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,days_to_resolution:m.days_to_resolution,
pilot_prior:{observations:534894,markets:2000,train_events:69,validation_events:70,chronological_holdout_events:104,event_holdout_events:56,
train_lower_90_at_one_cent:0.06377,validation_lower_90_at_one_cent:0.02261,chronological_holdout_lower_90_at_one_cent:0.21504,
event_holdout_lower_90_at_one_cent:0.02636,modeled_cost_cents:1,strictly_promoted:true},
drivers:["4%+ three-hour shock","accelerating final hour","825% opposite-side longshot","24-hour executable exit","one-cent stress-tested"],
rationale:`Backtest-approved paper pilot: YES moved ${(move3*100).toFixed(2)} points in three hours and ${(move1*100).toFixed(2)} in the accelerating final hour versus ${(priorMove1*100).toFixed(2)} previously. The strategy fades that move with the ${side} longshot at modeled ${pct(execution.price)}, ${(execution.cost*100).toFixed(2)} cents above midpoint, and exits at an executable price after ${SHOCK_FADE_EXIT_HOURS} hours. The exact rule retained positive 90% lower bounds in train, validation, chronological holdout, and sealed event holdout at one-cent cost; initial paper risk is still limited to 0.5% of equity.`};
}
async function fetchShockFadeSuggestions(markets){
const eligible=(markets||[]).filter(m=>m&&m.clob_token_ids&&m.clob_token_ids[0]);if(!eligible.length)return [];
@@ -2356,41 +2365,46 @@ async function fetchShockFadeSuggestions(markets){
}
function shockFadePilotProfile(p){
const grouped=new Map();
(p&&p.shock_fade_outcomes||[]).forEach((outcome,index)=>{
(p&&p.shock_fade_outcomes||[]).filter(outcome=>Number(outcome.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).forEach((outcome,index)=>{
const key=String(outcome.event_key||outcome.market_id||`shock-${index}`),rows=grouped.get(key)||[];
rows.push(clamp(Number(outcome.net_return||0),-1,2));grouped.set(key,rows);
});
(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot").forEach((trade,index)=>{
(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot"&&Number(trade.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).forEach((trade,index)=>{
const key=String(trade.event_key||trade.market_id||`shock-capital-${index}`),basis=Math.max(1,Number(trade.original_cost||trade.cost||0)),rows=grouped.get(key)||[];
rows.push(clamp(Number(trade.realized_pnl||0)/basis,-1,2));grouped.set(key,rows);
});
const returns=[...grouped.values()].map(rows=>rows.reduce((sum,value)=>sum+value,0)/rows.length),events=returns.length;
const mean=events?returns.reduce((sum,value)=>sum+value,0)/events:0,variance=events>1?returns.reduce((sum,value)=>sum+(value-mean)**2,0)/(events-1):0;
const margin=1.645*Math.sqrt(variance/Math.max(1,events)),lower=mean-margin,upper=mean+margin;
const pnl=(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot").reduce((sum,trade)=>sum+Number(trade.realized_pnl||0),0);
const pnl=(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot"&&Number(trade.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).reduce((sum,trade)=>sum+Number(trade.realized_pnl||0),0);
const demoted=(events>=SHOCK_FADE_VERDICT_EVENTS&&(mean<=-0.03||upper<=0))||pnl<=-Math.max(1,Number(p&&p.starting_balance||STARTING_BALANCE))*0.02;
const promoted=!demoted&&events>=SHOCK_FADE_PROMOTION_EVENTS&&lower>0.005;
return {state:demoted?"demoted":(promoted?"promoted":"shadow"),allowed:promoted,promoted,demoted,events,mean:+mean.toFixed(4),lower:+lower.toFixed(4),upper:+upper.toFixed(4),
const promoted=!demoted&&events>=SHOCK_FADE_PROMOTION_EVENTS&&lower>0.01,allowed=SHOCK_FADE_BACKTEST_APPROVED&&!demoted;
return {state:demoted?"demoted":(promoted?"forward-promoted":"backtest-pilot"),allowed,promoted,demoted,events,mean:+mean.toFixed(4),lower:+lower.toFixed(4),upper:+upper.toFixed(4),
pnl:+pnl.toFixed(2),shadow_pnl_per_100:+(mean*100).toFixed(2),active_shadows:(p&&p.shock_fade_shadows||[]).length,
expired_ungraded:Number(p&&p.shock_fade_expired_ungraded||0),position_pct:promoted?SHOCK_FADE_PILOT_POSITION_PCT:0};
expired_ungraded:Number(p&&p.shock_fade_expired_ungraded||0),position_pct:allowed?(promoted?SHOCK_FADE_PROMOTED_POSITION_PCT:SHOCK_FADE_INITIAL_POSITION_PCT):0};
}
function applyShockFadeForwardPromotion(s,p){
if(!s||(s.signal_type||"")!=="shock-fade-shadow")return s;const profile=shockFadePilotProfile(p);if(!profile.promoted)return s;
if(!s||(s.signal_type||"")!=="shock-fade-shadow")return s;const profile=shockFadePilotProfile(p);if(!profile.allowed)return s;
return Object.assign({},s,{quality:"shock-fade-pilot",signal_type:"shock-fade-pilot",trade_ready:true,entry_candidate:true,audited_observation_only:false,
adaptive_promotion:true,watch_only:false,rationale:`Forward promotion: ${profile.events} independent events produced a ${(profile.mean*100).toFixed(2)}% mean and ${(profile.lower*100).toFixed(2)}% 90% lower bound after executable costs. Capital begins at 0.75% of equity with a 2% total cap. ${s.rationale||""}`});
adaptive_promotion:profile.promoted,watch_only:false,rationale:profile.promoted
?`Forward size promotion: ${profile.events} independent Strategy ${SHOCK_FADE_STRATEGY_VERSION} events produced a ${(profile.mean*100).toFixed(2)}% mean and ${(profile.lower*100).toFixed(2)}% 90% lower bound after executable costs. Paper size increases to 1% with a 3% total cap. ${s.rationale||""}`
:`Independent 2,000-market audit approved a tightly capped 0.5% paper pilot; forward evidence can increase, retain, or demote that risk. ${s.rationale||""}`});
}
function stageShockFadeShadows(p,suggestions,{execute=true}={}){
p.shock_fade_shadows=Array.isArray(p.shock_fade_shadows)?p.shock_fade_shadows:[];p.shock_fade_outcomes=Array.isArray(p.shock_fade_outcomes)?p.shock_fade_outcomes:[];
if(!execute)return {active:p.shock_fade_shadows.length,staged:0,candidates:0};
const usedEvents=new Set([...p.shock_fade_shadows,...p.shock_fade_outcomes].map(row=>String(row.event_key||row.market_id))),usedMarkets=new Set([...p.shock_fade_shadows,...p.shock_fade_outcomes].map(row=>String(row.market_id)));
const usedRows=[...p.shock_fade_shadows,...p.shock_fade_outcomes,...(p.positions||[]).filter(row=>row.signal_type==="shock-fade-pilot"),
...(p.closed||[]).filter(row=>row.signal_type==="shock-fade-pilot")];
const usedEvents=new Set(usedRows.map(row=>String(row.event_key||row.market_id))),usedMarkets=new Set(usedRows.map(row=>String(row.market_id)));
const candidates=(suggestions||[]).filter(s=>(s.signal_type||"")==="shock-fade-shadow");let staged=0;
for(const s of candidates){
if(p.shock_fade_shadows.length>=SHOCK_FADE_SHADOW_LIMIT)break;const eventKey=String(s.event_key||s.market_id),marketId=String(s.market_id);
if(usedEvents.has(eventKey)||usedMarkets.has(marketId))continue;const openedAt=cycleIso();
p.shock_fade_shadows.push({id:`shock-shadow:${marketId}:${Date.now()}:${staged}`,market_id:marketId,event_key:eventKey,question:s.question,event:s.event||"",side:s.side,
entry_price:Number(s.entry_price),market_price:Number(s.market_price),current_price:Number(s.market_price),opened_at:openedAt,target_at:new Date(new Date(openedAt).getTime()+SHOCK_FADE_HORIZON_HOURS*3600000).toISOString(),
shock_move_1h:s.shock_move_1h,shock_move_3h:s.shock_move_3h,modeled_entry_cost:Number(s.friction||0),url:s.url||""});
p.history.push({date:logDay(),action:"SHOCK_SHADOW",question:s.question,side:s.side,detail:`Zero-capital ${SHOCK_FADE_HORIZON_HOURS}h shock fade recorded ${s.side} at modeled ${pct(s.entry_price)} after a ${(Number(s.shock_move_3h)*100).toFixed(2)}-point YES move · cash unchanged`});
entry_price:Number(s.entry_price),market_price:Number(s.market_price),current_price:Number(s.market_price),opened_at:openedAt,target_at:new Date(new Date(openedAt).getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString(),
shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,
modeled_entry_cost:Number(s.friction||0),url:s.url||""});
p.history.push({date:logDay(),action:"SHOCK_SHADOW",question:s.question,side:s.side,detail:`Zero-capital ${SHOCK_FADE_EXIT_HOURS}h shock fade recorded ${s.side} at modeled ${pct(s.entry_price)} after a ${(Number(s.shock_move_3h)*100).toFixed(2)}-point YES move · cash unchanged`});
usedEvents.add(eventKey);usedMarkets.add(marketId);staged++;
}
return {active:p.shock_fade_shadows.length,staged,candidates:candidates.length};
@@ -2401,16 +2415,16 @@ function manageShockFadeShadows(p,priceMap,{execute=true}={}){
const keep=[];let completed=0,expired=0;
for(const row of p.shock_fade_shadows){
const age=hoursSince(row.opened_at),fresh=priceMap&&priceMap[String(row.market_id)];
if(age<SHOCK_FADE_HORIZON_HOURS){keep.push(row);continue;}
if(age<SHOCK_FADE_EXIT_HOURS){keep.push(row);continue;}
const exit=shockFadeExecutableExit(fresh,row.side);
if(!(exit>0&&exit<1)){
if(age<=SHOCK_FADE_HORIZON_HOURS+SHOCK_FADE_GRADE_TOLERANCE_HOURS){keep.push(row);continue;}
if(age<=SHOCK_FADE_EXIT_HOURS+SHOCK_FADE_GRADE_TOLERANCE_HOURS){keep.push(row);continue;}
p.shock_fade_expired_ungraded=Number(p.shock_fade_expired_ungraded||0)+1;expired++;
p.history.push({date:logDay(),action:"SHOCK_EXPIRED",question:row.question,side:row.side,detail:"Three-hour shock observation expired without an executable exit inside the allowed window; no return was invented"});continue;
p.history.push({date:logDay(),action:"SHOCK_EXPIRED",question:row.question,side:row.side,detail:"24-hour shock observation expired without an executable exit inside the allowed window; no return was invented"});continue;
}
const entry=Math.max(0.01,Number(row.entry_price||0)),netReturn=clamp(exit/entry-1,-1,2);
if(!p.shock_fade_outcomes.some(outcome=>outcome.id===row.id))p.shock_fade_outcomes.push(Object.assign({},row,{closed_at:cycleIso(),exit_price:+exit.toFixed(4),net_return:+netReturn.toFixed(4),pnl_per_100:+(netReturn*100).toFixed(2),horizon_hours:+age.toFixed(2)}));
p.history.push({date:logDay(),action:"SHOCK_GRADE",question:row.question,side:row.side,detail:`Three-hour shock fade exited at executable ${pct(exit)} · modeled net return ${(netReturn*100).toFixed(2)}% · cash unchanged`});completed++;
p.history.push({date:logDay(),action:"SHOCK_GRADE",question:row.question,side:row.side,detail:`24-hour shock fade exited at executable ${pct(exit)} · modeled net return ${(netReturn*100).toFixed(2)}% · cash unchanged`});completed++;
}
p.shock_fade_shadows=keep;p.shock_fade_outcomes=p.shock_fade_outcomes.slice(-SHOCK_FADE_OUTCOME_LIMIT);
return {active:keep.length,completed,expired};
@@ -2812,11 +2826,11 @@ function markToMarket(p,priceMap,cfg=null,{policyExits=false,executeTrades=true}
if(!executeTrades){stillOpen.push(pos);continue;}
if(pos.signal_type==="shock-fade-pilot"){
if(marketIsSettled(fresh)){closePosition(p,pos,"Shock fade market settled before its timed exit","CLOSE");continue;}
if(hoursSince(pos.opened_at)<SHOCK_FADE_HORIZON_HOURS){stillOpen.push(pos);continue;}
if(hoursSince(pos.opened_at)<SHOCK_FADE_EXIT_HOURS){stillOpen.push(pos);continue;}
const executableExit=shockFadeExecutableExit(fresh,pos.side);
if(executableExit>0&&executableExit<1){
pos.current_price=+executableExit.toFixed(4);pos.value=+(pos.shares*executableExit).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
closePosition(p,pos,`Fixed ${SHOCK_FADE_HORIZON_HOURS}h shock-fade exit at executable book price`,"EXIT");continue;
closePosition(p,pos,`Fixed ${SHOCK_FADE_EXIT_HOURS}h shock-fade exit at executable book price`,"EXIT");continue;
}
pos.price_status="awaiting-executable-exit";stillOpen.push(pos);continue;
}
@@ -3476,8 +3490,8 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
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),game_start:s.game_start,hours_to_start:s.hours_to_start,pilot_prior:s.pilot_prior,
shock_move_1h:s.shock_move_1h,shock_move_3h:s.shock_move_3h,
target_exit_at:s.signal_type==="shock-fade-pilot"?new Date(new Date(cycleIso()).getTime()+SHOCK_FADE_HORIZON_HOURS*3600000).toISOString():null,
shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,shock_strategy_version:s.shock_strategy_version,
target_exit_at:s.signal_type==="shock-fade-pilot"?new Date(new Date(cycleIso()).getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString():null,
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,
@@ -3488,7 +3502,7 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
const openDetail=s.signal_type==="sports-favorite-pilot"
?`${decision?decision.mode+" mode — ":""}Forward-promoted sports position bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ modeled ${pct(entry)} for ${fmtUSD(cost)} (${pct(Number(s.market_price||entry))} midpoint + ${((entry-Number(s.market_price||entry))*100).toFixed(2)}c executable friction) · one position for event ${s.event_key||s.event||s.market_id} · learner state ${pilotProfile.state} · ${(Number(s.hours_to_start)||0).toFixed(1)}h before start`
:s.signal_type==="shock-fade-pilot"
?`${decision?decision.mode+" mode — ":""}Forward-promoted three-hour shock fade bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ executable modeled ${pct(entry)} for ${fmtUSD(cost)} after a ${(Number(s.shock_move_3h||0)*100).toFixed(2)}-point move · learner state ${shockProfile.state} · fixed executable exit target in ${SHOCK_FADE_HORIZON_HOURS}h`
?`${decision?decision.mode+" mode — ":""}${shockProfile.promoted?"Forward-promoted":"Backtest-approved"} 24-hour shock fade bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ executable modeled ${pct(entry)} for ${fmtUSD(cost)} after a ${(Number(s.shock_move_3h||0)*100).toFixed(2)}-point move · learner state ${shockProfile.state} · fixed executable exit target in ${SHOCK_FADE_EXIT_HOURS}h`
:`${decision?decision.mode+" mode — ":""}Bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ ${pct(entry)} for ${fmtUSD(cost)} · max binary loss budget ${(riskBudgetPct*100).toFixed(1)}% · signal margin ${((Math.abs(s.net_edge!=null?s.net_edge:s.edge))*100).toFixed(1)}c · learned weight ${Number(s.learning_multiplier||1).toFixed(2)}x (${s.learning_state||"observing"}/${s.market_learning_state||"observing"})${(s.historical_prior_features||[]).length?` · history prior ${(s.historical_prior_features||[]).join("+")}`:""} · evidence ${Math.round((s.evidence_score||0)*100)}${s.peer_note?` (${s.peer_note})`:""}`;
p.history.push({date:logDay(),action:"OPEN",question:s.question,side:s.side,detail:openDetail});
if(["sports-favorite-pilot","shock-fade-pilot"].includes(s.signal_type))heldEventKeys.add(String(s.event_key||s.event||s.market_id||""));
@@ -3630,7 +3644,7 @@ async function runDailyCycle(){
try{makerAuditCandidates=await fetchRewardMakerCandidates();}catch(e){makerAuditCandidates=[];}
setStatus("checking real-world context…",true);
const realWorldSignals=await fetchRealWorldSignals(analysisMarkets);
setStatus("scanning exact three-hour shock regimes…",true);
setStatus("scanning accelerating three-hour shocks for 24-hour fades…",true);
try{shockSugs=await fetchShockFadeSuggestions(analysisMarkets);}catch(e){shockSugs=[];}
setStatus("analyzing expected value…",true);
sugs=[...bundleSugs,...shockSugs,...generateSuggestions(analysisMarkets,SUGGESTION_TOTAL,SUGGESTION_PER_CATEGORY,realWorldSignals)]
@@ -3740,7 +3754,7 @@ async function runDailyCycle(){
p.lastDecision=Object.assign({},p.lastDecision,{shockShadowActive:shockStage.active,shockShadowStaged:shockStage.staged,
shockShadowCandidates:shockStage.candidates,shockShadowCompleted:shockShadowActivity.completed,shockShadowExpired:shockShadowActivity.expired,
shockFadePilot:shockProfile});
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Shock forward learner: ${shockStage.active} zero-capital observation${shockStage.active===1?"":"s"} active, ${shockShadowActivity.completed} three-hour outcome${shockShadowActivity.completed===1?"":"s"} graded this cycle, ${shockProfile.events}/${SHOCK_FADE_PROMOTION_EVENTS} independent events complete; capital remains zero until the positive confidence gate promotes.`.trim();
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Shock strategy 2: ${shockStage.active} zero-capital observation${shockStage.active===1?"":"s"} active, ${shockShadowActivity.completed} 24-hour outcome${shockShadowActivity.completed===1?"":"s"} graded this cycle, ${shockProfile.events}/${SHOCK_FADE_PROMOTION_EVENTS} independent forward events complete; current paper position cap ${(shockProfile.position_pct*100).toFixed(2)}% and losses can demote it to zero.`.trim();
}
if(runMode==="live"&&entriesAllowed){
const agentIndex=AGENTS.findIndex(agent=>agent.id===cfg.id),makerActivity=makerActivityByAgent[cfg.id];
@@ -3954,15 +3968,15 @@ function decisionSummary(p){
const makerStats=d.makerProfile&&d.makerProfile.global;
const maker=d.makerQuotes!=null?` Maker research: ${d.makerShadowActive||0} zero-capital lock-or-exit observations active; ${d.makerFills||0} verified touches and ${d.makerShadowCompleted||0} outcomes completed this cycle.${makerStats?` Event-clustered ledger: ${makerStats.attempts} attempts / ${makerStats.events} events, ${makerStats.locked} locked pairs, ${makerStats.adverse} immediate adverse exits, ${makerStats.unfilled} unfilled, and ${fmtUSD(makerStats.shadow_pnl)} simulated shadow net.`:""} Capital is disabled because neither the 6,048-rule chronological maker audit nor the reward stress test produced a validated holdout winner. Hypothetical rewards are excluded.`:"";
const sportsPilot=d.sportsFavoritePilot?` Sports forward learner: ${d.sportsFavoritePilot.state}, ${d.sportsFavoritePilot.active_shadows||0} zero-capital observations active and ${d.sportsFavoritePilot.events} independent closed events, ${d.sportsFavoritePilot.events?`${(d.sportsFavoritePilot.mean*100).toFixed(2)}% mean with ${d.sportsFavoritePilot.lower>=0?"+":""}${(d.sportsFavoritePilot.lower*100).toFixed(2)}% to ${d.sportsFavoritePilot.upper>=0?"+":""}${(d.sportsFavoritePilot.upper*100).toFixed(2)}% 90% interval; `:""}${fmtUSD(d.sportsFavoritePilot.shadow_pnl_per_100||0)} simulated per-$100 net and ${fmtUSD(d.sportsFavoritePilot.pnl)} realized capital P&L. Position cap ${(d.sportsFavoritePilot.position_pct*100).toFixed(2)}%; capital remains zero until ${SPORTS_FAVORITE_PROMOTION_EVENTS} new independent events establish a positive lower bound.`:"";
const shockPilot=d.shockFadePilot?` Shock forward learner: ${d.shockFadePilot.state}, ${d.shockFadePilot.active_shadows||0} exact three-hour observations active and ${d.shockFadePilot.events}/${SHOCK_FADE_PROMOTION_EVENTS} independent events complete. Mean ${(d.shockFadePilot.mean*100).toFixed(2)}%; 90% interval ${(d.shockFadePilot.lower*100).toFixed(2)}% to ${(d.shockFadePilot.upper*100).toFixed(2)}%; ${d.shockFadePilot.expired_ungraded||0} missed windows were discarded. Position cap ${(d.shockFadePilot.position_pct*100).toFixed(2)}%, total cap ${SHOCK_FADE_TOTAL_CAPITAL_PCT*100}%; capital remains zero until the lower bound exceeds 0.50%.`:"";
const shockPilot=d.shockFadePilot?` Shock strategy 2: ${d.shockFadePilot.state}, ${d.shockFadePilot.active_shadows||0} 24-hour observations active and ${d.shockFadePilot.events}/${SHOCK_FADE_PROMOTION_EVENTS} independent forward events complete. Mean ${(d.shockFadePilot.mean*100).toFixed(2)}%; 90% interval ${(d.shockFadePilot.lower*100).toFixed(2)}% to ${(d.shockFadePilot.upper*100).toFixed(2)}%; ${d.shockFadePilot.expired_ungraded||0} missed windows were discarded. Position cap ${(d.shockFadePilot.position_pct*100).toFixed(2)}%, total cap ${SHOCK_FADE_TOTAL_CAPITAL_PCT*100}%; 30 positive events can raise size to ${SHOCK_FADE_PROMOTED_POSITION_PCT*100}%, while losing evidence disables capital.`:"";
return `${d.mode} mode: ${d.reason}${emotion} Limits now: ${d.maxNew} new trade${d.maxNew===1?"":"s"}, max ${(d.maxFrac*100).toFixed(1)}% per position${d.minConv?`, conviction ${d.minConv}+`:""}.${learning}${calibration}${maker}${sportsPilot}${shockPilot}${exposure}${allocation}${candidates}${blockers}`;
}
function renderAgentBrief(cfg,p,st){
const root=$("agentBrief"); if(!root)return;
const bw=bestAndWorst(p);
const risk=cfg.aggressive?"Extreme risk":cfg.maxFrac>=0.055?"Active":cfg.maxFrac<=0.04?"Broad active":"Disciplined";
const cadence=cfg.id==="favorite"?"Zero-capital 12h sports observations until 30 forward events promote 1.25% positions":cfg.id==="reversal"?"Exact 3h shock observations; 30 independent positive events required before 0.75% positions":`${cfg.maxNew||0} new trades max per cycle`;
const exitRule=cfg.id==="favorite"?"Grade shadow observations only when Polymarket closes the market; after promotion, capital positions retain the 18% stop and profit locks":cfg.id==="reversal"?"Exit at the executable bid exactly three hours after entry; missed grading windows expire without using a later price"
const cadence=cfg.id==="favorite"?"Zero-capital 12h sports observations until 30 forward events promote 1.25% positions":cfg.id==="reversal"?"Accelerating 3h longshot shocks; 0.5% initial positions and 1% only after 30 positive forward events":`${cfg.maxNew||0} new trades max per cycle`;
const exitRule=cfg.id==="favorite"?"Grade shadow observations only when Polymarket closes the market; after promotion, capital positions retain the 18% stop and profit locks":cfg.id==="reversal"?"Exit at the executable bid after 24 hours; missed grading windows expire without using a later price"
:`Exit after a confirmed reversal, a 12h unconfirmed fade after 48h, a trailing-profit retrace, or ${EXIT_STALE_DAYS}d stale while losing; Politics trends get 72h before ordinary signal exits`;
const thesis=agentPlainBlurb(cfg,st);
const quoteSummary=(p.maker_quotes||[]).slice(0,3).map(quote=>`${quote.shadow_only?"Shadow":"Capital"} · ${quote.question.slice(0,34)}${quote.question.length>34?"...":""}: YES ${pct(quote.yes_quote)} + NO ${pct(quote.no_quote)}${quote.yes_filled_at||quote.no_filled_at?" (one leg touched)":""}`).join("; ");
@@ -5621,11 +5635,14 @@ window.PMA_ENGINE_DIAGNOSTICS=Object.freeze({
verdictEvents:SPORTS_FAVORITE_VERDICT_EVENTS,promotionEvents:SPORTS_FAVORITE_PROMOTION_EVENTS,
slippageBufferCents:SPORTS_FAVORITE_SLIPPAGE_BUFFER*100,maximumExecutionCostCents:SPORTS_FAVORITE_MAX_EXECUTION_COST*100,
capitalRequiresForwardPromotion:true,old24HourCapitalRuleRetired:true},
shockFadePilot:{minimumMovePct:SHOCK_FADE_MIN_MOVE*100,horizonHours:SHOCK_FADE_HORIZON_HOURS,gradeToleranceHours:SHOCK_FADE_GRADE_TOLERANCE_HOURS,
pilotPositionPct:SHOCK_FADE_PILOT_POSITION_PCT*100,totalCapitalPct:SHOCK_FADE_TOTAL_CAPITAL_PCT*100,shadowLimit:SHOCK_FADE_SHADOW_LIMIT,
shockFadePilot:{strategyVersion:SHOCK_FADE_STRATEGY_VERSION,minimumMovePct:SHOCK_FADE_MIN_MOVE*100,signalWindowHours:SHOCK_FADE_SIGNAL_WINDOW_HOURS,
exitHours:SHOCK_FADE_EXIT_HOURS,gradeToleranceHours:SHOCK_FADE_GRADE_TOLERANCE_HOURS,initialPositionPct:SHOCK_FADE_INITIAL_POSITION_PCT*100,
promotedPositionPct:SHOCK_FADE_PROMOTED_POSITION_PCT*100,totalCapitalPct:SHOCK_FADE_TOTAL_CAPITAL_PCT*100,shadowLimit:SHOCK_FADE_SHADOW_LIMIT,
promotionEvents:SHOCK_FADE_PROMOTION_EVENTS,slippageBufferCents:SHOCK_FADE_SLIPPAGE_BUFFER*100,maximumEntryCostCents:SHOCK_FADE_MAX_EXECUTION_COST*100,
exactThreeHourHistory:true,alignedFinalHour:true,oppositeSide:true,executableEntryAndExit:true,missedWindowsExpire:true,capitalRequiresForwardPromotion:true},
historicalPrior:"No broad directional rule survived the 300-market 60/20/20 adaptive holdout at both 24h and 72h, and no settlement rule survived validation across 5,000 resolved markets. A new exact three-hour shock-fade replay produced positive chronological results across 157,386 observations, but its event-disjoint 90% lower confidence bound crossed zero; it therefore begins as zero-capital forward research. Favorite-priced trend following remains hard-blocked. Live-priced complete negative-risk bundles may trade when positive after costs. Maker capital remains disabled because 6,048 wait and immediate-hedge rules produced no validated winner"}),
entryPriceRange:[SHOCK_FADE_MIN_ENTRY,SHOCK_FADE_MAX_ENTRY],
exactThreeHourHistory:true,acceleratingFinalHour:true,oppositeSide:true,executableEntryAndExit:true,missedWindowsExpire:true,
backtestApproved:SHOCK_FADE_BACKTEST_APPROVED,capitalAdaptsFromForwardEvidence:true},
historicalPrior:"No broad directional rule survived the 300-market 60/20/20 adaptive holdout at both 24h and 72h. Shock strategy 2 is narrower: an accelerating 4% three-hour move, 825% opposite-side entry, and 24-hour exit. Across 534,894 observations from 2,000 active markets, its one-cent-cost 90% lower bounds were +6.38% train, +2.26% validation, +21.50% chronological holdout, and +2.64% sealed event holdout across 299 partition-events. It begins as a 0.5% paper-capital pilot because resolved archive coverage was unavailable, increases only on positive forward evidence, and demotes on losses. Favorite-priced trend remains hard-blocked. Maker capital remains disabled"}),
});
function runEngineSelfTest(){
const market=(overrides={})=>Object.assign({
@@ -5679,28 +5696,45 @@ function runEngineSelfTest(){
openPositions(promotedSportsPilotBook,favoriteAgent,favoriteAgent.rank([promotedSportsSuggestion,Object.assign({},promotedSportsSuggestion,{market_id:"sports-pilot-second"})]),"All",promotedSportsPilotDecision,new Set(),{});
const sportsPilotExit=exitReason({signal_type:"sports-favorite-pilot",entry_price:0.77,current_price:0.72,opened_at:nowIso(),unrealized_pnl:-5,quality:"sports-favorite-pilot"},
market({category:"Sports",question:"Will Harbor FC win tomorrow?",game_start:sportsPilotStart,days_to_resolution:1}),null,favoriteAgent);
const shockNow=Math.floor(Date.now()/1000),shockPoints=[{t:shockNow-3*3600,p:0.50},{t:shockNow-3600,p:0.58}];
const shockMarket=market({id:"shock-up",event:"Shock event",event_id:"shock-event",yes_price:0.62,no_price:0.38,best_bid:0.615,best_ask:0.625,spread:0.01});
const shockNow=Math.floor(Date.now()/1000),shockPoints=[{t:shockNow-3*3600,p:0.77},{t:shockNow-2*3600,p:0.78},{t:shockNow-3600,p:0.79}];
const shockMarket=market({id:"shock-up",event:"Shock event",event_id:"shock-event",yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,spread:0.004});
const shockSuggestion=shockFadeSuggestion(shockMarket,shockPoints,shockNow);
const opposedShock=shockFadeSuggestion(shockMarket,[{t:shockNow-3*3600,p:0.50},{t:shockNow-3600,p:0.65}],shockNow);
const costlyShock=shockFadeSuggestion(market({id:"shock-costly",event_id:"shock-costly-event",yes_price:0.62,no_price:0.38,best_bid:0.605,best_ask:0.635,spread:0.03}),shockPoints,shockNow);
const downwardShock=shockFadeSuggestion(market({id:"shock-down",event:"Down shock event",event_id:"shock-down-event",yes_price:0.18,no_price:0.82,best_bid:0.178,best_ask:0.182,spread:0.004}),
[{t:shockNow-3*3600,p:0.23},{t:shockNow-2*3600,p:0.22},{t:shockNow-3600,p:0.21}],shockNow);
const opposedShock=shockFadeSuggestion(shockMarket,[{t:shockNow-3*3600,p:0.77},{t:shockNow-2*3600,p:0.80},{t:shockNow-3600,p:0.84}],shockNow);
const deceleratingShock=shockFadeSuggestion(shockMarket,[{t:shockNow-3*3600,p:0.75},{t:shockNow-2*3600,p:0.75},{t:shockNow-3600,p:0.79}],shockNow);
const costlyShock=shockFadeSuggestion(market({id:"shock-costly",event_id:"shock-costly-event",yes_price:0.82,no_price:0.18,best_bid:0.81,best_ask:0.83,spread:0.02}),shockPoints,shockNow);
const extremeShock=shockFadeSuggestion(market({id:"shock-extreme",event_id:"shock-extreme-event",yes_price:0.95,no_price:0.05,best_bid:0.945,best_ask:0.955,spread:0.01}),
[{t:shockNow-3*3600,p:0.80},{t:shockNow-2*3600,p:0.85},{t:shockNow-3600,p:0.90}],shockNow);
const shockSecond=Object.assign({},shockSuggestion,{market_id:"shock-second",question:"Second outcome in the same event"});
const shockBook=defaultPortfolio(),shockCash=shockBook.cash,shockAgent=AGENTS.find(agent=>agent.id==="reversal");
const shockStage=stageShockFadeShadows(shockBook,[shockSuggestion,shockSecond]);
const earlyShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.56,no_price:0.44,best_bid:0.555,best_ask:0.565})});
shockBook.shock_fade_shadows[0].opened_at=new Date(Date.now()-3.5*3600000).toISOString();
const completedShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.55,no_price:0.45,best_bid:0.545,best_ask:0.55})});
const earlyShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.78,no_price:0.22,best_bid:0.775,best_ask:0.785})});
shockBook.shock_fade_shadows[0].opened_at=new Date(Date.now()-24.5*3600000).toISOString();
const completedShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})});
const offlineShockStageBook=defaultPortfolio(),offlineShockStage=stageShockFadeShadows(offlineShockStageBook,[shockSuggestion],{execute:false});
const offlineShockBook=defaultPortfolio();stageShockFadeShadows(offlineShockBook,[shockSuggestion]);
const offlineShockGrade=manageShockFadeShadows(offlineShockBook,{"shock-up":market({id:"shock-up",yes_price:0.55,no_price:0.45,best_bid:0.545,best_ask:0.55})},{execute:false});
const expiredShockBook=defaultPortfolio();stageShockFadeShadows(expiredShockBook,[shockSuggestion]);expiredShockBook.shock_fade_shadows[0].opened_at=new Date(Date.now()-6*3600000).toISOString();
const expiredShockGrade=manageShockFadeShadows(expiredShockBook,{"shock-up":market({id:"shock-up",yes_price:0.55,no_price:0.45,best_bid:0,best_ask:0})});
const offlineShockGrade=manageShockFadeShadows(offlineShockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})},{execute:false});
const expiredShockBook=defaultPortfolio();stageShockFadeShadows(expiredShockBook,[shockSuggestion]);expiredShockBook.shock_fade_shadows[0].opened_at=new Date(Date.now()-27*3600000).toISOString();
const expiredShockGrade=manageShockFadeShadows(expiredShockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0,best_ask:0})});
const compactedShockBook=compactPortfolioForSync(Object.assign({},offlineShockBook,{shock_fade_outcomes:[...shockBook.shock_fade_outcomes]}));
const initialShockBook=defaultPortfolio(),initialShockProfile=shockFadePilotProfile(initialShockBook),initialShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,initialShockBook);
const initialShockDecision=adaptiveDecision(shockAgent,initialShockBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
openPositions(initialShockBook,shockAgent,shockAgent.rank([initialShockSuggestion]),"All",initialShockDecision,new Set(),{});
const capitalPositionBlocksShadow=stageShockFadeShadows(initialShockBook,[shockSuggestion]);
const earlyShockCapitalBook=defaultPortfolio();openPositions(earlyShockCapitalBook,shockAgent,shockAgent.rank([initialShockSuggestion]),"All",initialShockDecision,new Set(),{});
markToMarket(earlyShockCapitalBook,{"shock-up":market({id:"shock-up",yes_price:0.90,no_price:0.10,best_bid:0.895,best_ask:0.905})},shockAgent,{policyExits:true,executeTrades:true});
const timedShockCapitalBook=defaultPortfolio();openPositions(timedShockCapitalBook,shockAgent,shockAgent.rank([initialShockSuggestion]),"All",initialShockDecision,new Set(),{});
timedShockCapitalBook.positions[0].opened_at=new Date(Date.now()-24.5*3600000).toISOString();
markToMarket(timedShockCapitalBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})},shockAgent,{policyExits:true,executeTrades:true});
const promotedShockBook=defaultPortfolio();
for(let i=0;i<SHOCK_FADE_PROMOTION_EVENTS;i++)promotedShockBook.shock_fade_outcomes.push({event_key:`winning-shock-event-${i}`,market_id:`winning-shock-${i}`,net_return:0.08,pnl_per_100:8});
for(let i=0;i<SHOCK_FADE_PROMOTION_EVENTS;i++)promotedShockBook.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`winning-shock-event-${i}`,market_id:`winning-shock-${i}`,net_return:0.08,pnl_per_100:8});
const promotedShockProfile=shockFadePilotProfile(promotedShockBook),promotedShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,promotedShockBook);
const promotedShockDecision=adaptiveDecision(shockAgent,promotedShockBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
openPositions(promotedShockBook,shockAgent,shockAgent.rank([promotedShockSuggestion]),"All",promotedShockDecision,new Set(),{});
const losingShockBook=defaultPortfolio();
for(let i=0;i<SHOCK_FADE_VERDICT_EVENTS;i++)losingShockBook.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`losing-shock-event-${i}`,market_id:`losing-shock-${i}`,net_return:-0.10,pnl_per_100:-10});
const losingShockProfile=shockFadePilotProfile(losingShockBook),demotedShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,losingShockBook);
const targets=window.PMA_ENGINE_DIAGNOSTICS.gainStopTargets(0.82);
const hoursAgo=h=>new Date(Date.now()-h*3600000).toISOString();
const conflict={trade_ready:true,side:"NO",net_edge:-0.03,conviction:72};
@@ -6325,23 +6359,37 @@ function runEngineSelfTest(){
ordinaryGapExitDoesNotPreemptPilot:sportsPilotExit===null,
},
shockFadePilot:{
detectsExactAlignedThreeHourShock:Boolean(shockSuggestion&&shockSuggestion.side==="NO"&&shockSuggestion.shock_move_3h===0.12&&shockSuggestion.shock_move_1h===0.04),
usesExecutableOppositeAskPlusSlippage:Boolean(shockSuggestion&&shockSuggestion.market_price===0.38&&shockSuggestion.entry_price===0.3875&&shockSuggestion.friction===0.0075),
detectsAcceleratingThreeHourShock:Boolean(shockSuggestion&&shockSuggestion.side==="NO"&&shockSuggestion.shock_move_3h===0.05
&&shockSuggestion.shock_move_1h===0.03&&shockSuggestion.shock_prior_move_1h===0.01),
handlesDownwardShockSymmetrically:Boolean(downwardShock&&downwardShock.side==="YES"&&downwardShock.entry_price===0.1845
&&downwardShock.shock_move_3h===-0.05),
usesExecutableLongshotAskPlusSlippage:Boolean(shockSuggestion&&shockSuggestion.market_price===0.18&&shockSuggestion.entry_price===0.1845&&shockSuggestion.friction===0.0045),
rejectsOpposedLastHour:opposedShock===null,
rejectsMoreThanOneCentFriction:costlyShock===null,
rejectsDeceleratingLastHour:deceleratingShock===null,
rejectsMoreThanHalfCentEntryFriction:costlyShock===null,
rejectsSubEightCentEntry:extremeShock===null,
startsWatchOnly:Boolean(shockSuggestion&&!shockSuggestion.trade_ready&&shockSuggestion.audited_observation_only&&shockSuggestion.requires_live),
onlyShockAgentCanAcceptAfterPromotion:promotedShockSuggestion&&agentAcceptsSuggestion(shockAgent,promotedShockSuggestion)
&&!agentAcceptsSuggestion(AGENTS.find(agent=>agent.id==="momentum"),promotedShockSuggestion),
backtestApprovalStartsTinyCapital:initialShockProfile.allowed&&!initialShockProfile.promoted&&initialShockSuggestion.trade_ready
&&initialShockBook.positions.length===1&&initialShockBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_INITIAL_POSITION_PCT+0.01,
capitalPositionPreventsDuplicateShadow:capitalPositionBlocksShadow.staged===0&&initialShockBook.shock_fade_shadows.length===0,
onlyShockAgentCanAcceptPilot:initialShockSuggestion&&agentAcceptsSuggestion(shockAgent,initialShockSuggestion)
&&!agentAcceptsSuggestion(AGENTS.find(agent=>agent.id==="momentum"),initialShockSuggestion),
stagesOneShadowPerEvent:shockStage.staged===1,
shadowNeverChangesCash:shockBook.cash===shockCash&&shockBook.positions.length===0,
doesNotGradeBeforeThreeHours:earlyShockGrade.completed===0,
gradesAtExecutableExit:completedShockGrade.completed===1&&shockBook.shock_fade_outcomes.length===1&&shockBook.shock_fade_outcomes[0].exit_price===0.4475,
doesNotGradeBeforeTwentyFourHours:earlyShockGrade.completed===0,
gradesAtExecutableExit:completedShockGrade.completed===1&&shockBook.shock_fade_outcomes.length===1&&shockBook.shock_fade_outcomes[0].exit_price===0.2425,
capitalIgnoresUnauditedEarlyStop:earlyShockCapitalBook.positions.length===1&&earlyShockCapitalBook.closed.length===0,
capitalClosesAtTwentyFourHourExecutableBid:timedShockCapitalBook.positions.length===0&&timedShockCapitalBook.closed.length===1
&&timedShockCapitalBook.closed[0].current_price===0.2425,
offlineCannotStageOrGrade:offlineShockStage.staged===0&&offlineShockStageBook.shock_fade_shadows.length===0
&&offlineShockGrade.completed===0&&offlineShockBook.shock_fade_shadows.length===1&&offlineShockBook.shock_fade_outcomes.length===0,
missedWindowExpiresWithoutInventedReturn:expiredShockGrade.expired===1&&expiredShockBook.shock_fade_outcomes.length===0&&expiredShockBook.shock_fade_expired_ungraded===1,
forwardLedgerSurvivesCompaction:compactedShockBook.shock_fade_shadows.length===1&&compactedShockBook.shock_fade_outcomes.length===1,
thirtyPositiveEventsPromote:promotedShockProfile.promoted&&promotedShockProfile.lower>0.005&&promotedShockSuggestion.trade_ready,
promotedCapitalIsTightlyCapped:promotedShockBook.positions.length===1&&promotedShockBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_PILOT_POSITION_PCT+0.01,
thirtyPositiveEventsIncreaseSize:promotedShockProfile.promoted&&promotedShockProfile.lower>0.01&&promotedShockSuggestion.trade_ready
&&promotedShockProfile.position_pct===SHOCK_FADE_PROMOTED_POSITION_PCT,
promotedCapitalIsTightlyCapped:promotedShockBook.positions.length===1&&promotedShockBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_PROMOTED_POSITION_PCT+0.01,
losingForwardEvidenceDemotes:losingShockProfile.demoted&&!losingShockProfile.allowed&&losingShockProfile.position_pct===0
&&!demotedShockSuggestion.trade_ready,
},
riskBudget:{core:coreRiskBudget,aggressiveGap:aggressiveGapBudget,blocksExactRange:intervalContract.jump_risk&&!intervalContract.trade_ready,
blocksPathDependentBarrier:pathBarrierContract.jump_risk&&!pathBarrierContract.trade_ready,
File diff suppressed because it is too large Load Diff
+67 -20
View File
@@ -1,11 +1,18 @@
import fs from "node:fs";
const GAMMA = "https://gamma-api.polymarket.com";
const CLOB = "https://clob.polymarket.com";
const MARKET_LIMIT = Math.max(100, Math.min(500, Number(process.env.SHOCK_MARKETS || 500)));
const MARKET_LIMIT = Math.max(100, Math.min(2000, Number(process.env.SHOCK_MARKETS || 500)));
const EXTERNAL_MARKET_LIMIT = Math.max(100, Math.min(2000, Number(process.env.SHOCK_EXTERNAL_MARKETS || 1000)));
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 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 HOUR = 3600;
const HORIZONS = [3, 6, 12, 24];
const WINDOWS = [1, 3, 6, 24];
@@ -82,10 +89,12 @@ async function fetchMarkets(limit, universe = "active") {
}
return markets;
}
for (let offset = 0; markets.length < limit && offset < limit * 5; offset += 100) {
let cursor = "";
while (markets.length < limit) {
const params = new URLSearchParams({ active: "true", closed: "false", archived: "false", include_tag: "true",
limit: "100", offset: String(offset), order: "volume24hr", ascending: "false" });
const page = await fetchJson(`${GAMMA}/markets?${params}`);
limit: "100", 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 raw of page) {
const labels = parseJson(raw.outcomes).map((value) => String(value).trim().toLowerCase());
@@ -96,7 +105,8 @@ async function fetchMarkets(limit, universe = "active") {
eventKey: String(raw.events?.[0]?.id || raw.events?.[0]?.slug || raw.eventId || id) });
if (markets.length >= limit) break;
}
if (page.length < 100) break;
if (page.length < 100 || !payload.next_cursor || payload.next_cursor === cursor) break;
cursor = payload.next_cursor;
}
return markets;
}
@@ -107,19 +117,29 @@ function chunks(items, size) {
return output;
}
async function fetchHistories(markets) {
const responses = await mapLimit(chunks(markets, 20), CONCURRENCY, async (chunk) => fetchJson(`${CLOB}/batch-prices-history`, {
async function fetchHistories(markets, { lookbackDays = HISTORY_DAYS } = {}) {
const now = Math.floor(Date.now() / 1000);
const windows = [];
if (lookbackDays == null) windows.push(null);
else for (let end = now; end > now - lookbackDays * 86400; end -= 15 * 86400)
windows.push({ start: Math.max(now - lookbackDays * 86400, end - 15 * 86400), end });
const jobs = windows.flatMap((window) => chunks(markets, 20).map((chunk) => ({ window, chunk })));
const responses = await mapLimit(jobs, CONCURRENCY, async ({ window, chunk }) => fetchJson(`${CLOB}/batch-prices-history`, {
method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ markets: chunk.map((market) => market.token), interval: "1m", fidelity: 60 })
body: JSON.stringify({ markets: chunk.map((market) => market.token), fidelity: 60,
...(window == null ? { interval: "max" } : { start_ts: window.start, end_ts: window.end }) })
}));
const history = {};
responses.forEach((response) => {
if (!response?.history) return;
Object.entries(response.history).forEach(([token, points]) => {
history[token] = (points || []).map((point) => ({ t: Number(point.t), p: Number(point.p) }))
.filter((point) => Number.isFinite(point.t) && Number.isFinite(point.p)).sort((a, b) => a.t - b.t);
history[token] = [...(history[token] || []), ...(points || []).map((point) => ({ t: Number(point.t), p: Number(point.p) }))
.filter((point) => Number.isFinite(point.t) && Number.isFinite(point.p))];
});
});
Object.entries(history).forEach(([token, points]) => {
history[token] = [...new Map(points.sort((a, b) => a.t - b.t).map((point) => [point.t, point])).values()];
});
return { history, failures: responses.filter((response) => response?.error).length };
}
@@ -206,7 +226,7 @@ function simulate(row, rule) {
const followedSide = Math.sign(move) > 0 ? "YES" : "NO";
const side = rule.direction === "continue" ? followedSide : followedSide === "YES" ? "NO" : "YES";
const entry = side === "YES" ? row.price : 1 - row.price, exit = side === "YES" ? row.futurePrice : 1 - row.futurePrice;
if (entry <= 0.02 || entry >= 0.98) return null;
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 netReturn = exit / entry - 1 - COST / entry;
@@ -230,10 +250,18 @@ function summary(rows) {
const baseRules = [];
for (const horizon of HORIZONS) for (const window of WINDOWS) for (const minMove of MIN_MOVES)
for (const direction of ["continue", "fade"]) for (const confirmation of CONFIRMATIONS) for (const maxVol of MAX_VOLS)
baseRules.push({ id: `${direction}_h${horizon}_w${window}_m${minMove}_${confirmation}_v${maxVol}`, horizon, window, minMove,
baseRules.push({ id: `${direction}_h${horizon}_w${window}_m${minMove}_${confirmation}_v${maxVol}`,
baseId: `${direction}_h${horizon}_w${window}_m${minMove}_${confirmation}_v${maxVol}`, horizon, window, minMove,
direction, confirmation, maxVol, category: "All", band: "All" });
const markets = await fetchMarkets(MARKET_LIMIT), fetched = await fetchHistories(markets);
let markets, fetched;
if (CACHE_FILE && fs.existsSync(CACHE_FILE)) {
const cached = JSON.parse(fs.readFileSync(CACHE_FILE, "utf8"));
markets = cached.markets || []; fetched = { history: cached.history || {}, failures: Number(cached.failures || 0) };
} else {
markets = await fetchMarkets(MARKET_LIMIT); fetched = await fetchHistories(markets);
if (CACHE_FILE) fs.writeFileSync(CACHE_FILE, JSON.stringify({ markets, history: fetched.history, failures: fetched.failures }));
}
const rows = markets.flatMap((market) => observations(market, fetched.history[market.token] || []));
const eventHoldoutRows = rows.filter((row) => stableHash(row.eventKey) % 4 === 0);
const developmentRows = rows.filter((row) => stableHash(row.eventKey) % 4 !== 0);
@@ -243,7 +271,19 @@ const partitions = { train: developmentRows.filter((row) => row.observedAt < tra
validation: developmentRows.filter((row) => row.observedAt >= trainCut && row.evaluatedAt < validationCut),
holdout: developmentRows.filter((row) => row.observedAt >= validationCut) };
function evaluate(rule, partition) { return summary(partitions[partition].map((row) => simulate(row, rule)).filter(Boolean)); }
const partitionByHorizon = Object.fromEntries(Object.entries(partitions).map(([name, partitionRows]) => [name,
Object.fromEntries(HORIZONS.map((horizon) => [horizon, partitionRows.filter((row) => row.horizon === horizon)]))]));
const simulationCache = Object.fromEntries(Object.keys(partitions).map((name) => [name, new Map()]));
function simulatedRows(rule, partition) {
const key = rule.baseId || rule.id;
if (!simulationCache[partition].has(key)) {
const baseRule = { ...rule, category: "All", band: "All" };
simulationCache[partition].set(key, (partitionByHorizon[partition][rule.horizon] || []).map((row) => simulate(row, baseRule)).filter(Boolean));
}
return simulationCache[partition].get(key).filter((row) => (rule.category === "All" || row.category === rule.category)
&& (rule.band === "All" || row.band === rule.band));
}
function evaluate(rule, partition) { return summary(simulatedRows(rule, partition)); }
const trainWinners = baseRules.map((rule) => ({ rule, train: evaluate(rule, "train") }))
.filter((candidate) => candidate.train.attempts >= 100 && candidate.train.events >= 20 && candidate.train.lower > 0);
@@ -271,7 +311,7 @@ let external = { markets: [], histories: {}, failures: 0, rows: [] };
if (finalists.length) {
const activeEvents = new Set(markets.map((market) => market.eventKey));
external.markets = (await fetchMarkets(EXTERNAL_MARKET_LIMIT, "closed")).filter((market) => !activeEvents.has(market.eventKey));
const externalFetched = await fetchHistories(external.markets);
const externalFetched = await fetchHistories(external.markets, { lookbackDays: null });
external.histories = externalFetched.history;
external.failures = externalFetched.failures;
external.rows = external.markets.flatMap((market) => observations(market, external.histories[market.token] || []));
@@ -283,12 +323,15 @@ if (finalists.length) {
}
const compact = (stats) => Object.fromEntries(Object.entries(stats).map(([key, value]) => [key, Number.isFinite(value) ? +value.toFixed(5) : value]));
const coverageDays = Object.values(fetched.history).map((points) => points.length > 1 ? (points.at(-1).t - points[0].t) / 86400 : 0).sort((a, b) => a - b);
const medianCoverageDays = coverageDays.length ? coverageDays[Math.floor(coverageDays.length / 2)] : 0;
const candidateRow = (candidate) => ({ rule: candidate.rule, passesHoldout: candidate.passesHoldout,
passesEventHoldout: Boolean(candidate.passesEventHoldout), passesArchive: Boolean(candidate.passesArchive),
train: compact(candidate.train), validation: compact(candidate.validation), holdout: compact(candidate.holdout),
eventHoldout: candidate.eventHoldout ? compact(candidate.eventHoldout) : null, archive: candidate.archive ? compact(candidate.archive) : null });
const report = { generatedAt: new Date().toISOString(), requestedMarkets: MARKET_LIMIT, markets: markets.length,
marketsWithHistory: markets.filter((market) => fetched.history[market.token]?.length).length, fetchFailures: fetched.failures,
medianHistoryCoverageDays: +medianCoverageDays.toFixed(2), maximumHistoryCoverageDays: +(coverageDays.at(-1) || 0).toFixed(2),
observations: rows.length, testedBaseRules: baseRules.length, trainWinners: trainWinners.length, testedRefinements: uniqueRefined.length,
validationWinners: validationWinners.length, holdoutPassed: holdout.filter((candidate) => candidate.passesHoldout).length,
eventHoldoutObservations: eventHoldoutRows.length, eventHoldoutEvents: new Set(eventHoldoutRows.map((row) => row.eventKey)).size,
@@ -297,16 +340,18 @@ const report = { generatedAt: new Date().toISOString(), requestedMarkets: MARKET
externalMarketsWithHistory: external.markets.filter((market) => external.histories[market.token]?.length).length,
externalFetchFailures: external.failures, externalObservations: external.rows.length,
archivePassed: finalists.filter((candidate) => candidate.passesArchive).length,
methodology: { historyDays: HISTORY_DAYS, observationSpacingHours: 3, horizons: HORIZONS, windows: WINDOWS,
costCents: COST * 100, eventSplit: "25% deterministic event-disjoint holdout reserved before search",
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], 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",
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])),
candidates: holdout.slice(0, 30).map(candidateRow) };
if (SUMMARY_ONLY) console.log(JSON.stringify({ generatedAt: report.generatedAt, markets: report.markets,
const output = SUMMARY_ONLY ? { generatedAt: report.generatedAt, markets: report.markets,
marketsWithHistory: report.marketsWithHistory, fetchFailures: report.fetchFailures, observations: report.observations,
medianHistoryCoverageDays: report.medianHistoryCoverageDays, maximumHistoryCoverageDays: report.maximumHistoryCoverageDays,
testedBaseRules: report.testedBaseRules, trainWinners: report.trainWinners, testedRefinements: report.testedRefinements,
validationWinners: report.validationWinners, holdoutPassed: report.holdoutPassed,
eventHoldoutObservations: report.eventHoldoutObservations, eventHoldoutEvents: report.eventHoldoutEvents,
@@ -314,5 +359,7 @@ if (SUMMARY_ONLY) console.log(JSON.stringify({ generatedAt: report.generatedAt,
externalMarkets: report.externalMarkets, externalMarketsWithHistory: report.externalMarketsWithHistory,
externalFetchFailures: report.externalFetchFailures, externalObservations: report.externalObservations,
archivePassed: report.archivePassed, partitions: report.partitions,
candidates: report.candidates.slice(0, 10) }, null, 2));
else console.log(JSON.stringify(report, null, 2));
candidates: report.candidates.slice(0, SUMMARY_CANDIDATES) } : report;
const serialized = JSON.stringify(output, null, 2);
if (OUTPUT_FILE) fs.writeFileSync(OUTPUT_FILE, serialized + "\n");
else console.log(serialized);