mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-19 18:48:10 +00:00
Align adaptation with audited outcomes
This commit is contained in:
@@ -15,7 +15,7 @@ https://polymarket-site-eta.vercel.app/personal.html
|
||||
|
||||
The site fetches live Polymarket markets, generates agent suggestions, lets you
|
||||
run frequent paper cycles, and syncs the shared arena state through Neon or
|
||||
Vercel Blob. Build 44 also installs an offline app shell and caches timestamped
|
||||
Vercel Blob. Build 45 also installs an offline app shell and caches timestamped
|
||||
market snapshots. During an outage, cycles continue locally; cached entries are
|
||||
allowed for 90 minutes, older snapshots become mark-only, and all cached data
|
||||
expires after 24 hours.
|
||||
@@ -26,7 +26,7 @@ small samples toward neutral, caps sizing changes to 0.68x-1.30x, and reserves
|
||||
15% of candidates for deterministic exploration so a stale regime cannot become
|
||||
permanent.
|
||||
|
||||
Strategy 42 treats each binary stake as capable of falling to zero even when the
|
||||
Strategy 43 treats each binary stake as capable of falling to zero even when the
|
||||
18% stop cannot fill. New core positions are capped at 2.5%-4% of equity and
|
||||
aggressive positions at 3%-5%, with lower limits for near-term, extreme-price,
|
||||
reversal, and fast-moving setups. Oversized positions inherited from older
|
||||
@@ -35,9 +35,11 @@ The two-agent overlap guard counts only positions worth at least 1.25% of an
|
||||
agent's equity, so tiny profit-lock runners do not block a new material trade.
|
||||
|
||||
A separate walk-forward ledger records each trade-ready signal before its future
|
||||
price is known, grades it at least 12 hours later, and combines that broad market
|
||||
price is known, grades it at least 24 hours later, and combines that broad market
|
||||
calibration with each agent's personal outcomes. This expands the learning sample
|
||||
without backfilling future information into old decisions.
|
||||
without backfilling future information into old decisions. The 24-hour horizon
|
||||
matches the engine's minimum ordinary holding policy; stops and profit locks still
|
||||
act immediately from fresh prices.
|
||||
|
||||
The initial seven-day chart seed is an approximate replay, not a live return.
|
||||
It uses only prices available on each simulated date, computes daily and weekly
|
||||
@@ -52,13 +54,18 @@ applies a conservative half-cent cost estimate, and reports a chronological
|
||||
70/30 split plus three consecutive time segments. Results are also clustered by
|
||||
market so repeated observations from one contract cannot masquerade as broad
|
||||
evidence. Set `EVAL_MARKETS`, `EVAL_CONCURRENCY`, `EVAL_HORIZONS`, or
|
||||
`EVAL_COST_CENTS` to change the audit.
|
||||
The first 80-market audit found that reversal signals lost 4.34% on average in
|
||||
both chronological partitions, while crypto and longshot samples were also
|
||||
negative overall. Strategy 42 therefore blocks reversal and sports-trend entries outside the fixed
|
||||
15% exploration lane and applies modest sizing penalties to crypto and longshots.
|
||||
It does not boost any rule from this audit because no positive rule was robust
|
||||
across the chronological split.
|
||||
`EVAL_COST_CENTS` to change the audit. Set `EVAL_SUMMARY=1` for the compact,
|
||||
decision-focused report.
|
||||
|
||||
The latest 120-active-market audit produced 1,241 twelve-hour observations from
|
||||
41 markets with no fetch failures. The broad rule averaged -1.35% net and was
|
||||
negative in all three chronological segments. Reversals averaged -3.69%, with a
|
||||
market-clustered 90% interval entirely below zero. Crypto and Sports were also
|
||||
negative but covered only three and five markets. The 24-hour cohort improved to
|
||||
-0.82% row mean and +1.31% market mean, with no rule robustly negative across all
|
||||
segments. Strategy 43 therefore disables reversal entries, retains their signals
|
||||
for paper grading, and evaluates adaptation at 24 hours. It does not promote any
|
||||
rule because no positive cohort passed the same robustness checks.
|
||||
|
||||
A corrected 200-market audit paged through 197 markets with usable history and
|
||||
1,912 twelve-hour outcomes. Reversals remained negative in every chronological
|
||||
@@ -66,23 +73,23 @@ segment and averaged -4.13%. Sports trends were negative in train and test and
|
||||
averaged -3.53% at 72 hours. Politics trends were the sole cohort with positive
|
||||
row-level returns in all three 72-hour segments, but its market-cluster interval
|
||||
still crossed zero; that supports a longer hold test, not a larger entry bet.
|
||||
Strategy 42 gives Politics trend positions that 72-hour observation window before
|
||||
Strategy 43 gives Politics trend positions that 72-hour observation window before
|
||||
ordinary signal exits. Stops, profit locks, settlement handling, and risk-budget
|
||||
reductions remain immediate.
|
||||
|
||||
Strategy 42 also subtracts a half-cent round-trip cost when grading each live
|
||||
Strategy 43 also subtracts a half-cent round-trip cost when grading each live
|
||||
walk-forward signal. Confidence uses the largest independent matching bucket,
|
||||
not the sum of five overlapping feature buckets, and evidence from older engine
|
||||
versions is down-weighted. This prevents a handful of duplicated observations
|
||||
from authorizing larger positions or hiding a modest negative regime.
|
||||
|
||||
Strategy 42 adds uncertainty-aware promotion and demotion. A matching setup must
|
||||
Strategy 43 adds uncertainty-aware promotion and demotion. A matching setup must
|
||||
accumulate at least eight effective observations and agree across at least two
|
||||
feature views before repeatable positive evidence can increase size or repeatable
|
||||
negative evidence can block a new entry. Mixed evidence stays close to neutral
|
||||
instead of being mistaken for an edge.
|
||||
|
||||
Build 44 enforces the documented offline boundary end to end. Cached snapshots
|
||||
Build 45 enforces the documented offline boundary end to end. Cached snapshots
|
||||
under 90 minutes old may continue paper execution. Older snapshots remain usable
|
||||
for valuation and chart snapshots for up to 24 hours, but cannot trigger entries,
|
||||
stop-losses, gain-stops, risk rebalances, settlements, or policy exits. Network
|
||||
@@ -95,16 +102,17 @@ adaptive baselines, pending signal grades, and trade evidence remain in one stra
|
||||
lineage until the actual entry, sizing, or exit logic changes. Legacy build 40 and 41
|
||||
records are migrated into the same strategy lineage without losing evidence.
|
||||
|
||||
Build 44 independently refreshes markets for matured pending signals that have
|
||||
Build 45 independently refreshes markets for matured pending signals that have
|
||||
left the current top-500 activity scan. Unavailable markets remain queued for a
|
||||
bounded retry window. This prevents activity-rank survivorship from deciding
|
||||
which wins and losses reach the adaptive calibration ledger.
|
||||
|
||||
Strategy 42 coordinates high-risk exploration globally. Crypto, reversal,
|
||||
near-term, extreme-price, and other gap-prone positions may be held materially by
|
||||
Strategy 43 coordinates high-risk exploration globally. Crypto, near-term,
|
||||
extreme-price, legacy reversal, and other gap-prone positions may be held materially by
|
||||
only one agent, while ordinary independently confirmed markets retain the
|
||||
two-agent cap. A historically blocked setup can enter the exploration lane for
|
||||
only one designated agent, preventing duplicated speculative losses.
|
||||
two-agent cap. A historically blocked Sports trend can enter the exploration lane
|
||||
for only one designated agent, preventing duplicated speculative losses. Reversal
|
||||
signals cannot enter that lane and remain observation-only.
|
||||
|
||||
Run `npm run evaluate:settlements` to evaluate fixed decisions made 1, 3, 7,
|
||||
14, 30, and 90 days before known binary settlements. The audit uses one
|
||||
@@ -113,7 +121,8 @@ applies the same half-cent cost assumption, clusters related contracts by event,
|
||||
and requires positive event-clustered confidence bounds in train and test plus
|
||||
positive results in three chronological segments before it calls a settlement
|
||||
cohort robust. Environment variables beginning with
|
||||
`SETTLEMENT_` control its market count, concurrency, horizons, and cost.
|
||||
`SETTLEMENT_` control its market count, concurrency, horizons, and cost. Set
|
||||
`SETTLEMENT_SUMMARY=1` for the compact report.
|
||||
|
||||
The first event-clustered run loaded 498 of the 500 highest-volume resolved
|
||||
markets. No side, price band, category, or 1-90 day holding rule passed the
|
||||
@@ -121,6 +130,13 @@ required train/test confidence checks. In particular, older YES/underdog gains
|
||||
reversed in the recent test segment. The engine therefore does not install a
|
||||
static settlement-direction boost from this audit.
|
||||
|
||||
The latest 200-resolved-market audit loaded history for 199 markets with no
|
||||
fetch failures. No positive rule passed the robustness gate. Buying NO with
|
||||
7-14 days remaining was robustly negative in pooled, train, test, and
|
||||
event-clustered results: the 14-day cohort averaged -37.33% by observation and
|
||||
-46.84% by event. Strategy 43 therefore blocks new NO entries with 21 days or
|
||||
less to resolution while continuing to record their signals for future evidence.
|
||||
|
||||
Paper accounts created with a password are also saved through the backend, so a
|
||||
user can log in from another device and see the same paper portfolio, activity,
|
||||
and value history. Passwordless paper accounts remain local-only.
|
||||
|
||||
+44
-31
@@ -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 42 · build 44</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 43 · build 45</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 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>
|
||||
<div class="live-build-banner"><b>Build 45 active:</b> adaptation now grades signals after the same 24-hour minimum used by ordinary trade policy. Reversal and short-dated NO entries are disabled after independent price-history and settlement audits found repeatable net losses; Trend Endurance replaces the reversal agent. This remains paper trading; profits are not guaranteed.</div>
|
||||
|
||||
<!-- ============ OVERVIEW ============ -->
|
||||
<section class="tabpanel" data-tab="overview">
|
||||
@@ -714,7 +714,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
|
||||
<!-- ============ ABOUT ============ -->
|
||||
<section class="tabpanel" data-tab="about">
|
||||
<div class="section-title">ℹ️ The competition</div>
|
||||
<p class="muted" style="margin-top:-6px;max-width:820px">Every agent starts with <b>$10,000</b> in paper money. Five core strategies use disciplined entry rules, while five aggressive strategies pursue catalysts, reversals, breakouts, asymmetric tails, and concentrated conviction. Aggressive agents may size confirmed setups more heavily, but cash is retained whenever their strategy has no qualifying signal.</p>
|
||||
<p class="muted" style="margin-top:-6px;max-width:820px">Every agent starts with <b>$10,000</b> in paper money. Five core strategies use disciplined entry rules, while five aggressive strategies pursue catalysts, durable trends, breakouts, asymmetric tails, and concentrated conviction. Aggressive agents may size confirmed setups more heavily, but cash is retained whenever their strategy has no qualifying signal.</p>
|
||||
<div class="about-grid" id="aboutAgents"></div>
|
||||
<div class="card" style="margin-top:16px">
|
||||
<div class="card-h"><h3>The live cycle</h3></div>
|
||||
@@ -745,7 +745,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
Build 44 · Adaptive strategy 42 · Paper trading only · Live prices from Polymarket's public Gamma API · Not financial advice ·
|
||||
Build 45 · Adaptive strategy 43 · 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,9 +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 = 44;
|
||||
const SUGGESTION_ENGINE_VERSION = 42;
|
||||
const PREVIOUS_STRATEGY_VERSION = 40;
|
||||
const BUILD_VERSION = 45;
|
||||
const SUGGESTION_ENGINE_VERSION = 43;
|
||||
const PREVIOUS_STRATEGY_VERSION = 42;
|
||||
const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40});
|
||||
function normalizedStrategyVersion(value){
|
||||
const version=Number(value||0);
|
||||
@@ -884,9 +884,11 @@ const AGENTS = [
|
||||
blurb:"An aggressive event-driven strategy that concentrates on markets with fresh outside context, multiple evidence signals, and enough activity for a fast repricing. It is willing to enter earlier than the core agents when a measurable catalyst and positive post-cost edge agree, while accepting larger swings to capture the move.",
|
||||
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:"Reversal Edge", emoji:"↻", color:"#ff5f91", kind:"strategy", aggressive:true,
|
||||
blurb:"A contrarian mean-reversion strategy that waits for an hourly reversal after a sharp one-day move. It buys against the earlier direction only when that snapback is confirmed by liquidity, evidence, and a positive post-friction signal margin.",
|
||||
rank:(s)=>[...s].map(reversalCandidate).filter(Boolean).sort((a,b)=>(Math.abs(Number(b.price_change_1d||0))*95+effectiveEntryEdge(b)*190+Number(b.evidence_score||0)*24+b.conviction)-(Math.abs(Number(a.price_change_1d||0))*95+effectiveEntryEdge(a)*190+Number(a.evidence_score||0)*24+a.conviction)),
|
||||
{id:"reversal", name:"Trend Endurance", emoji:"⧗", color:"#ff5f91", kind:"strategy", aggressive:true,
|
||||
blurb:"A patient aggressive trend strategy built from the failure of short-horizon reversal trading. It favors confirmed, non-chased trends with at least three weeks to resolve, gives extra rank to Politics or Economy markets, and allows the move time to develop without assuming either contract side is inherently superior.",
|
||||
rank:(s)=>[...s].filter(x=>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)=>((["Politics","Economy"].includes(b.category)?8:0)+Number(b.signal_confidence||0)*45+effectiveEntryEdge(b)*180+Number(b.evidence_score||0)*24+b.conviction)
|
||||
-((["Politics","Economy"].includes(a.category)?8:0)+Number(a.signal_confidence||0)*45+effectiveEntryEdge(a)*180+Number(a.evidence_score||0)*24+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.",
|
||||
@@ -921,7 +923,7 @@ const OFFLINE_ENTRY_MAX_AGE_MS = 90*60*1000;
|
||||
const OFFLINE_CACHE_MAX_AGE_MS = 24*60*60*1000;
|
||||
const NETWORK_REQUEST_TIMEOUT_MS = 8000;
|
||||
const PRICE_REQUEST_TIMEOUT_MS = 4000;
|
||||
const SIGNAL_EVAL_HOURS = 12;
|
||||
const SIGNAL_EVAL_HOURS = 24;
|
||||
const SIGNAL_LEDGER_PENDING_LIMIT = 300;
|
||||
const SIGNAL_LEDGER_OUTCOME_LIMIT = 500;
|
||||
const SIGNAL_LEDGER_RETRY_HOURS = 168;
|
||||
@@ -1204,9 +1206,6 @@ function tradeLossBudgetPct(cfg,s={}){
|
||||
function boundedStakeForRisk(eq,stake,cfg,s){
|
||||
return Math.max(0,Math.min(Number(stake||0),Number(eq||0)*tradeLossBudgetPct(cfg,s)));
|
||||
}
|
||||
function reversalCandidate(s){
|
||||
return s&&s.signal_type==="reversal"&&s.trade_ready?s:null;
|
||||
}
|
||||
function analyzeMarket(m,realWorldSignals={}){
|
||||
if(m.volume<MIN_SCOUT_VOLUME||m.liquidity<MIN_SCOUT_LIQUIDITY)return null;
|
||||
const external=realWorldSignals[m.id]||{},p=m.yes_price,policy=categoryPolicy(m.category);
|
||||
@@ -1760,7 +1759,8 @@ function stableExploration(agentId,marketId){
|
||||
}
|
||||
function historicalOpportunityPrior(s){
|
||||
const rows=[];
|
||||
if((s.signal_type||"")==="reversal")rows.push({feature:"reversal",score:-0.0413,samples:287,blocked:true});
|
||||
if((s.signal_type||"")==="reversal")rows.push({feature:"reversal",score:-0.0369,samples:207,blocked:true,hardBlocked:true});
|
||||
if((s.side||"")==="NO"&&Number(s.days_to_resolution)<=21)rows.push({feature:"short-no",score:-0.3733,samples:64,blocked:true,hardBlocked:true});
|
||||
if((s.signal_type||"")==="trend"&&(s.category||"Other")==="Sports")rows.push({feature:"sports-trend",score:-0.0353,samples:162,blocked:true});
|
||||
if((s.category||"Other")==="Crypto")rows.push({feature:"crypto",score:-0.0344,samples:140,blocked:false});
|
||||
if(entryBand(s.entry_price)==="longshot")rows.push({feature:"longshot",score:-0.0327,samples:277,blocked:false});
|
||||
@@ -1769,7 +1769,8 @@ function historicalOpportunityPrior(s){
|
||||
const score=rows.reduce((sum,row)=>sum+row.score,0)/rows.length;
|
||||
const samples=Math.max(...rows.map(row=>row.samples)),confidence=samples/(samples+200);
|
||||
return {score:+score.toFixed(4),confidence:+confidence.toFixed(3),
|
||||
multiplier:+clamp(1+score*confidence*2.5,0.82,1).toFixed(3),blocked:rows.some(row=>row.blocked),features:rows.map(row=>row.feature)};
|
||||
multiplier:+clamp(1+score*confidence*2.5,0.82,1).toFixed(3),blocked:rows.some(row=>row.blocked),
|
||||
hardBlocked:rows.some(row=>row.hardBlocked),features:rows.map(row=>row.feature)};
|
||||
}
|
||||
function learnedOpportunity(cfg,p,s,profile=null,calibration=null){
|
||||
const model=profile||buildAdaptiveProfile(p),features=learningFeatures(s),rows=features.map(k=>model.buckets[k]).filter(Boolean);
|
||||
@@ -1785,7 +1786,7 @@ function learnedOpportunity(cfg,p,s,profile=null,calibration=null){
|
||||
const personalEvidence=personalPromoted?positiveRows.reduce((sum,r)=>sum+Number(r.lower_bound||0),0)/positiveRows.length
|
||||
:(personalBlocked?negativeRows.reduce((sum,r)=>sum+Number(r.upper_bound||0),0)/negativeRows.length:score*0.10);
|
||||
const blockedBy=historical.blocked?"historical":(personalBlocked?"personal":(!market.allowed?"walk-forward":null));
|
||||
const blocked=Boolean(blockedBy)&&!exploration;
|
||||
const blocked=Boolean(blockedBy)&&(historical.hardBlocked||!exploration);
|
||||
return {score:+score.toFixed(4),confidence:+confidence.toFixed(3),market_score:market.score,market_confidence:market.confidence,
|
||||
personal_state:personalPromoted?"promoted":(personalBlocked?"demoted":"observing"),market_state:market.state,
|
||||
historical_score:historical.score,historical_confidence:historical.confidence,historical_features:historical.features,
|
||||
@@ -2190,7 +2191,8 @@ function agentAcceptsSuggestion(cfg,s){
|
||||
&&edge>=MIN_LIQUIDITY_EDGE&&evidence>=0.50;
|
||||
if(cfg.id==="catalyst")return ["catalyst","confirmed"].includes(quality)&&edge>=MIN_SELECTIVE_ENTRY_EDGE
|
||||
&&Number(s.evidence_source_count||0)>=1&&Number(s.signal_confidence||0)>=0.62;
|
||||
if(cfg.id==="reversal")return ["reversal","confirmed"].includes(quality)&&s.signal_type==="reversal"
|
||||
if(cfg.id==="reversal")return ["confirmed","trend","liquid-trend"].includes(quality)&&s.signal_type==="trend"
|
||||
&&Math.abs(Number(s.price_change_1d||0))<=0.04&&Number(s.days_to_resolution||0)>=21
|
||||
&&edge>=MIN_LIQUIDITY_EDGE&&Number(s.signal_confidence||0)>=0.64;
|
||||
if(cfg.id==="breakout")return ["confirmed","trend","liquid-trend","catalyst"].includes(quality)
|
||||
&&s.signal_type==="trend"&&edge>=MIN_TREND_EDGE&&Number(s.signal_strength||0)>=0.64&&evidence>=0.48;
|
||||
@@ -2675,7 +2677,7 @@ function decisionSummary(p){
|
||||
const blockerRows=Object.entries(d.rejectionCounts||{}).filter(([,count])=>count>0).sort((a,b)=>b[1]-a[1]);
|
||||
const blockers=blockerRows.length?` Blocks: ${blockerRows.slice(0,4).map(([key,count])=>`${blockerLabels[key]||key} ${count}`).join(", ")}.`:"";
|
||||
const learning=d.learning?` Learning: ${d.learning.samples} completed trades retained with older strategies down-weighted, ${(d.learning.global_score*100).toFixed(2)}% shrunk expectancy; ${d.learning.current_samples||0} completed under adaptive strategy ${SUGGESTION_ENGINE_VERSION}${d.learning.best?`; strongest ${d.learning.best.feature.replace(":"," ")}`:""}${d.learning.worst?`; weakest ${d.learning.worst.feature.replace(":"," ")}`:""}.`:"";
|
||||
const calibration=d.marketLearning?` Walk-forward calibration: ${d.marketLearning.samples||0} net-of-cost graded signals (${d.marketLearning.current_samples||0} under adaptive strategy ${SUGGESTION_ENGINE_VERSION}), ${d.marketLearning.pending||0} awaiting a future price${d.marketLearning.expired_ungraded?`, ${d.marketLearning.expired_ungraded} expired ungraded`:""}; ${d.marketLearning.promoted_buckets||0} feature cohorts promoted and ${d.marketLearning.demoted_buckets||0} demoted. Matured markets are repriced even after leaving the active scan. Confidence counts independent outcomes once and uncertainty gates sizing. Historical prior: reversal and sports-trend entries require the fixed 15% exploration lane; crypto, longshots, and YES entries are sized down, not forbidden. Politics trends receive 72 hours before ordinary signal exits.`:"";
|
||||
const calibration=d.marketLearning?` Walk-forward calibration: ${d.marketLearning.samples||0} net-of-cost signals graded after ${SIGNAL_EVAL_HOURS} hours (${d.marketLearning.current_samples||0} under adaptive strategy ${SUGGESTION_ENGINE_VERSION}), ${d.marketLearning.pending||0} awaiting a future price${d.marketLearning.expired_ungraded?`, ${d.marketLearning.expired_ungraded} expired ungraded`:""}; ${d.marketLearning.promoted_buckets||0} feature cohorts promoted and ${d.marketLearning.demoted_buckets||0} demoted. Matured markets are repriced even after leaving the active scan. Confidence counts independent outcomes once and uncertainty gates sizing. Historical prior: reversal and NO entries with 21 days or less are disabled; sports trends require the fixed exploration lane; crypto, longshots, and YES entries are sized down. Politics trends receive 72 hours before ordinary signal exits.`:"";
|
||||
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}${exposure}${allocation}${candidates}${blockers}`;
|
||||
}
|
||||
function renderAgentBrief(cfg,p,st){
|
||||
@@ -2986,7 +2988,7 @@ function agentCompetitionPlan(cfg,row,rank,leader){
|
||||
if(cfg.id==="longshot")return "Plan: keep risk small but search for one underpriced outsider that can reprice sharply and leapfrog the leaderboard.";
|
||||
if(cfg.id==="diversifier")return "Plan: spread bets broadly, reduce single-market damage, and try to win through consistency rather than one heroic call.";
|
||||
if(cfg.id==="catalyst")return "Plan: deploy quickly when fresh real-world context and measured edge align, then concentrate enough capital for a catalyst repricing to move the account.";
|
||||
if(cfg.id==="reversal")return "Plan: attack overextended one-day moves from the opposite side, looking for liquid mean-reversion setups with enough evidence to justify the contrarian risk.";
|
||||
if(cfg.id==="reversal")return "Plan: hold confirmed, non-chased trends long enough to develop, favoring Politics or Economy markets with at least three weeks left instead of repeating the reversal rule that failed the audit.";
|
||||
if(cfg.id==="breakout")return "Plan: enter only confirmed momentum setups and press the cleanest breakouts where direction, activity, and signal margin agree.";
|
||||
if(cfg.id==="tailalpha")return "Plan: hunt underpriced low-cost contracts with asymmetric upside, accepting a volatile hit rate while keeping each tail position survivable.";
|
||||
if(cfg.id==="conviction")return "Plan: ignore marginal trades and commit the largest allowed stakes to a compact book of the strongest edge-and-evidence combinations.";
|
||||
@@ -3117,7 +3119,7 @@ function agentVoice(cfg){
|
||||
longshot:{tone:"opportunistic but risk-aware",hello:"I hunt asymmetric mispricing, but I keep the bet size honest.",signoff:"One repricing can matter, but only if I survive the misses."},
|
||||
diversifier:{tone:"calm, portfolio-minded, and broad",hello:"I care about the whole basket more than one perfect call.",signoff:"Many small edges can beat one loud opinion."},
|
||||
catalyst:{tone:"urgent, evidence-driven, and decisive",hello:"I am hunting the next event that can force a fast repricing.",signoff:"A catalyst only matters when the numbers agree."},
|
||||
reversal:{tone:"contrarian, sharp, and skeptical",hello:"I am looking for a crowd move that has gone too far.",signoff:"The best snapbacks begin where consensus gets stretched."},
|
||||
reversal:{tone:"patient, evidence-led, and persistent",hello:"I am looking for a confirmed trend with enough time to develop.",signoff:"I want durable direction, not a reflexive snapback bet."},
|
||||
breakout:{tone:"fast, aggressive, and momentum-led",hello:"I am scanning for direction that is strengthening right now.",signoff:"I press confirmed speed and cut failed momentum."},
|
||||
tailalpha:{tone:"bold, asymmetric, and probability-aware",hello:"I am searching for cheap contracts with mispriced upside.",signoff:"A low hit rate can work when the payoff is truly asymmetric."},
|
||||
conviction:{tone:"concentrated, demanding, and direct",hello:"I only want the few trades strong enough to deserve real weight.",signoff:"When evidence and edge align, I size with conviction."},
|
||||
@@ -4248,10 +4250,10 @@ window.PMA_ENGINE_DIAGNOSTICS=Object.freeze({
|
||||
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,
|
||||
networkTimeoutSeconds:NETWORK_REQUEST_TIMEOUT_MS/1000,priceTimeoutSeconds:PRICE_REQUEST_TIMEOUT_MS/1000,
|
||||
staleCacheIsMarkOnly:true,strategyEvidenceSurvivesBuilds:true,signalRetryHours:SIGNAL_LEDGER_RETRY_HOURS,
|
||||
staleCacheIsMarkOnly:true,strategyEvidenceSurvivesBuilds:true,signalEvaluationHours:SIGNAL_EVAL_HOURS,signalRetryHours:SIGNAL_LEDGER_RETRY_HOURS,
|
||||
signalDueFetchLimit:SIGNAL_LEDGER_DUE_FETCH_LIMIT,survivorshipSafeSignalGrading:true,
|
||||
signalRoundTripCostCents:SIGNAL_ROUND_TRIP_COST*100,independentConfidence:true,uncertaintyGatedCalibration:true,
|
||||
historicalPrior:"reversal and sports trends blocked outside exploration; crypto, longshots, and YES sized down"}),
|
||||
historicalPrior:"reversal and short-dated NO entries disabled; sports trends blocked outside exploration; crypto, longshots, and YES sized down"}),
|
||||
});
|
||||
function runEngineSelfTest(){
|
||||
const market=(overrides={})=>Object.assign({
|
||||
@@ -4293,13 +4295,18 @@ function runEngineSelfTest(){
|
||||
while(stableExploration(AGENTS[0].id,sportsPriorId))sportsPriorId=`prior-sports-${++sportsPriorIndex}`;
|
||||
const priorSportsTrend=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:sportsPriorId,signal_type:"trend",quality:"confirmed",category:"Sports",side:"NO",entry_price:0.58});
|
||||
const priorPoliticsTrend=historicalOpportunityPrior({signal_type:"trend",category:"Politics",side:"NO",entry_price:0.58});
|
||||
const ledgerState={signal_ledger:{pending:[{key:"ledger-test",market_id:"ledger-test",observed_at:hoursAgo(13),side:"YES",entry_price:0.40,
|
||||
const shortNoPrior=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:"short-no",signal_type:"trend",quality:"confirmed",category:"Politics",side:"NO",entry_price:0.58,days_to_resolution:14});
|
||||
const longNoPrior=historicalOpportunityPrior({signal_type:"trend",category:"Politics",side:"NO",entry_price:0.58,days_to_resolution:30});
|
||||
const ledgerState={signal_ledger:{pending:[{key:"ledger-test",market_id:"ledger-test",observed_at:hoursAgo(25),side:"YES",entry_price:0.40,
|
||||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||||
updateSignalLedger(ledgerState,[market({id:"ledger-test",yes_price:0.50,no_price:0.50})],[]);
|
||||
const earlyLedgerState={signal_ledger:{pending:[{key:"ledger-early",market_id:"ledger-early",observed_at:hoursAgo(13),side:"YES",entry_price:0.40,
|
||||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||||
updateSignalLedger(earlyLedgerState,[market({id:"ledger-early",yes_price:0.50,no_price:0.50})],[]);
|
||||
const dueFetchLedger={pending:[
|
||||
{key:"known",market_id:"known-active",observed_at:hoursAgo(13)},
|
||||
{key:"outside-a",market_id:"outside-active-scan",observed_at:hoursAgo(14)},
|
||||
{key:"outside-b",market_id:"outside-active-scan",observed_at:hoursAgo(13)},
|
||||
{key:"known",market_id:"known-active",observed_at:hoursAgo(25)},
|
||||
{key:"outside-a",market_id:"outside-active-scan",observed_at:hoursAgo(26)},
|
||||
{key:"outside-b",market_id:"outside-active-scan",observed_at:hoursAgo(25)},
|
||||
{key:"young",market_id:"too-young",observed_at:hoursAgo(2)}],outcomes:[]};
|
||||
const dueFetchIds=pendingSignalMarketIds(dueFetchLedger,new Set(["known-active"]));
|
||||
const retryLedgerState={signal_ledger:{pending:[{key:"retry",market_id:"temporarily-unavailable",observed_at:hoursAgo(100),side:"YES",entry_price:0.4}],outcomes:[]}};
|
||||
@@ -4314,7 +4321,7 @@ function runEngineSelfTest(){
|
||||
const stableNegativeCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array(24).fill(-0.12)));
|
||||
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})]});
|
||||
{strategy_version:PREVIOUS_STRATEGY_VERSION,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();
|
||||
@@ -4351,6 +4358,7 @@ function runEngineSelfTest(){
|
||||
explorationOwners=AGENTS.filter(a=>stableExploration(a.id,explorationMarketId));
|
||||
if(!explorationOwners.length)explorationMarketId=`single-explorer-${++explorationIndex}`;
|
||||
}
|
||||
const explorerReversal=learnedOpportunity(explorationOwners[0],defaultPortfolio(),{market_id:explorationMarketId,signal_type:"reversal",quality:"reversal",category:"Politics",side:"NO",entry_price:0.42});
|
||||
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;
|
||||
@@ -4370,11 +4378,11 @@ function runEngineSelfTest(){
|
||||
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 buildMigrationState=defaultState();
|
||||
buildMigrationState.engine_version=43;buildMigrationState.strategy_version=SUGGESTION_ENGINE_VERSION;
|
||||
buildMigrationState.engine_version=44;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.engine_version=44;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);
|
||||
@@ -4386,16 +4394,21 @@ function runEngineSelfTest(){
|
||||
adaptation:{samples:learningProfile.samples,trendMultiplier:learnedTrend.multiplier,reversalMultiplier:learnedReversal.multiplier,learnsDirection:learnedTrend.multiplier>learnedReversal.multiplier,
|
||||
calibrationSamples:calibrationProfile.samples,trendMarketScore:learnedTrend.market_score,reversalMarketScore:learnedReversal.market_score,
|
||||
historicalPriorBlocksReversal:!priorReversal.allowed&&priorReversal.blocked_by==="historical",
|
||||
reversalRemainsBlockedForExplorer:!explorerReversal.allowed&&explorerReversal.blocked_by==="historical",
|
||||
enduranceReplacesReversal:agentAcceptsSuggestion(AGENTS.find(a=>a.id==="reversal"),trend)&&!agentAcceptsSuggestion(AGENTS.find(a=>a.id==="reversal"),reversal),
|
||||
historicalPriorSizesRisk:!priorCryptoLongshot.blocked&&priorCryptoLongshot.multiplier<1&&priorCryptoLongshot.features.length===2,
|
||||
historicalPriorBlocksSportsTrend:!priorSportsTrend.allowed&&priorSportsTrend.blocked_by==="historical",
|
||||
historicalPriorAllowsPoliticsTrend:!priorPoliticsTrend.blocked,
|
||||
blocksShortDatedNo:!shortNoPrior.allowed&&shortNoPrior.blocked_by==="historical",
|
||||
allowsLongDatedNo:!longNoPrior.blocked,
|
||||
ledgerMaturesWithoutLookahead:ledgerState.signal_ledger.pending.length===0&&ledgerState.signal_ledger.outcomes.length===1&&ledgerState.signal_ledger.outcomes[0].return===0.2375,
|
||||
holdsSignalsUntilPolicyHorizon:earlyLedgerState.signal_ledger.pending.length===1&&earlyLedgerState.signal_ledger.outcomes.length===0,
|
||||
fetchesMaturedMarketsOutsideActiveScan:dueFetchIds.length===1&&dueFetchIds[0]==="outside-active-scan",
|
||||
keepsUnavailableGradesQueued:retryLedgerState.signal_ledger.pending.length===1&&retryLedgerState.signal_ledger.outcomes.length===0,
|
||||
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,
|
||||
legacyBuildMapsToPreviousStrategy:normalizedStrategyVersion(41)===PREVIOUS_STRATEGY_VERSION,
|
||||
legacyBuildLineageRemainsHistorical:normalizedStrategyVersion(41)===40,
|
||||
previousStrategyIsDownWeighted:legacyBuildCalibration.current_samples===0&&legacyBuildCalibration.buckets["signal:trend"].weight>0.54&&legacyBuildCalibration.buckets["signal:trend"].weight<=0.55,
|
||||
currentStrategyKeepsFullWeight:currentStrategyCalibration.current_samples===1&¤tStrategyCalibration.buckets["signal:trend"].weight>0.99,
|
||||
baselineSurvivesBuildMigration:buildMigrationState.agents.value.engine_baseline.equity===9876.54&&buildMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
|
||||
|
||||
@@ -208,4 +208,24 @@ const report = {
|
||||
return [horizon, { observations: horizonRows.length / 2, chronological: chronologicalEvaluation(horizonRows) }];
|
||||
})),
|
||||
};
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
const compact = process.env.SETTLEMENT_SUMMARY === "1";
|
||||
const compactStats = (stats = {}) => ({ count: stats.count || 0, events: stats.events || 0,
|
||||
mean: stats.mean || 0, eventMean: stats.eventMean || 0, eventLower90: stats.eventLower90 || 0,
|
||||
eventUpper90: stats.eventUpper90 || 0, winRate: stats.winRate || 0 });
|
||||
const compactRules = (rules = {}) => Object.fromEntries(Object.entries(rules)
|
||||
.filter(([, result]) => result.enoughData && (result.allPositive || result.allNegative))
|
||||
.map(([name, result]) => [name, { direction: result.allPositive ? "positive" : "negative",
|
||||
pooled: compactStats(result.pooled), train: compactStats(result.train), test: compactStats(result.test) }]));
|
||||
const summary = {
|
||||
generatedAt: report.generatedAt, requestedMarkets: report.requestedMarkets, resolvedMarkets: report.resolvedMarkets,
|
||||
marketsWithHistory: report.marketsWithHistory, failures: report.failures,
|
||||
horizons: Object.fromEntries(Object.entries(report.horizons).map(([days, value]) => [days, {
|
||||
observations: value.observations,
|
||||
favorite: compactStats(value.chronological.train.buy_favorite),
|
||||
favoriteTest: compactStats(value.chronological.test.buy_favorite),
|
||||
underdog: compactStats(value.chronological.train.buy_underdog),
|
||||
underdogTest: compactStats(value.chronological.test.buy_underdog),
|
||||
robustRules: compactRules(value.chronological.robustRules),
|
||||
}])),
|
||||
};
|
||||
console.log(JSON.stringify(compact ? summary : report, null, 2));
|
||||
|
||||
@@ -254,4 +254,28 @@ const report = {
|
||||
})),
|
||||
failures: histories.filter((result) => result?.error).length,
|
||||
};
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
const compact = process.env.EVAL_SUMMARY === "1";
|
||||
const compactStats = (stats = {}) => ({ count: stats.count || 0, markets: stats.markets || 0,
|
||||
mean: stats.mean || 0, marketMean: stats.marketMean || 0, lower90: stats.lower90 || 0, upper90: stats.upper90 || 0,
|
||||
winRate: stats.winRate || 0 });
|
||||
const compactRules = (rules = {}) => Object.fromEntries(Object.entries(rules)
|
||||
.filter(([, result]) => result.enoughData && (result.allPositive || result.allNegative))
|
||||
.map(([name, result]) => [name, { direction: result.allPositive ? "positive" : "negative",
|
||||
minimumSegmentMean: result.minimumSegmentMean, maximumSegmentMean: result.maximumSegmentMean,
|
||||
pooled: compactStats(result.pooled) }]));
|
||||
const summary = {
|
||||
generatedAt: report.generatedAt, marketLimit: report.marketLimit, marketsWithHistory: report.marketsWithHistory,
|
||||
primaryHorizon: report.methodology.primaryHorizon, failures: report.failures,
|
||||
overall: compactStats(report.overall),
|
||||
byType: Object.fromEntries(Object.entries(report.byType).map(([key, value]) => [key, compactStats(value)])),
|
||||
byCategory: Object.fromEntries(Object.entries(report.byCategory).map(([key, value]) => [key, compactStats(value)])),
|
||||
byBand: Object.fromEntries(Object.entries(report.byBand).map(([key, value]) => [key, compactStats(value)])),
|
||||
bySide: Object.fromEntries(Object.entries(report.bySide).map(([key, value]) => [key, compactStats(value)])),
|
||||
train: compactStats(report.chronologicalSplit.train.follow_all),
|
||||
test: compactStats(report.chronologicalSplit.test.follow_all),
|
||||
robustRules: compactRules(report.chronologicalSplit.robustRules),
|
||||
horizons: Object.fromEntries(Object.entries(report.horizons).map(([hours, value]) => [hours, {
|
||||
overall: compactStats(value.overall), robustRules: compactRules(value.chronological.robustRules),
|
||||
}])),
|
||||
};
|
||||
console.log(JSON.stringify(compact ? summary : report, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user