@@ -2286,7 +2262,7 @@ function decisionSummary(p){
const d=p.lastDecision;
if(!d)return "No live adaptation yet; this agent is still using its base rulebook.";
const emotion=d.emotion?` Emotion: ${d.emotion}${d.urgency!=null?` (${Math.round(d.urgency*100)}% urgency)`:""}.`:"";
- const exposure=d.currentExposure!=null&&d.targetExposure!=null?` Exposure ${Math.round(d.currentExposure*100)}%; operating band ${Math.round(Number(d.minExposure||0)*100)}-${Math.round(d.targetExposure*100)}%.`:"";
+ const exposure=d.currentExposure!=null&&d.targetExposure!=null?` Exposure ${Math.round(d.currentExposure*100)}%; ceiling ${Math.round(d.targetExposure*100)}%.`:"";
const allocation=d.allocationStatus?` ${d.allocationStatus}`:"";
const candidates=d.tradeReadyCount!=null?` Candidate audit: ${d.tradeReadyCount} trade-ready, ${d.strategyCandidates||0} strategy matches, ${d.eligibleCandidates||0} fully eligible, ${d.opened||0} opened.`:"";
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}+`:""}.${exposure}${allocation}${candidates}`;
@@ -2296,7 +2272,7 @@ function renderAgentBrief(cfg,p,st){
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.maxNew||0} new trades max per cycle`;
- const exitRule=`Exit if edge fades, conviction weakens, <${EXIT_RESOLUTION_DAYS}d to resolve, or ${EXIT_STALE_DAYS}d stale`;
+ const exitRule=`Exit after a confirmed reversal, a 12h unconfirmed fade after 48h, a trailing-profit retrace, or ${EXIT_STALE_DAYS}d stale while losing`;
const thesis=agentPlainBlurb(cfg,st);
root.innerHTML=`
@@ -2307,7 +2283,7 @@ function renderAgentBrief(cfg,p,st){
Trade Rule
${esc(cadence)}
Stop Loss
Sell all at -18%
-
Gain Stop
Sell 25% at +75%, +150%, +300%
+
Profit Locks
Three reachable, entry-aware 25% sales
Policy Exit
${esc(exitRule)}
Best / Worst
${esc(bw.best)}
${esc(bw.worst)}
`;
}
@@ -2319,7 +2295,7 @@ function renderLeaderboard(){
#${i+1}
${MEDALS[i]||""}${r.c.emoji}
${r.c.name}
${agentBlurb(r.c,st)}
-
${fmtUSD(r.eq)}
${fmtPct(r.ret)}
24h ${format24h(r.change24h)}
+
${fmtUSD(r.eq)}
${fmtPct(r.ret)}
v${SUGGESTION_ENGINE_VERSION} ${fmtPct(r.engineRet)}
24h ${format24h(r.change24h)}
`).join("");
document.querySelectorAll("#leaderboard .lb-card").forEach(el=>{
@@ -2492,8 +2468,8 @@ function renderSuggestions(){
const scanned=Number(data.market_count||0).toLocaleString();
const analyzed=Number(data.analyzed_count||data.market_count||0).toLocaleString();
$("focusNote").textContent=focus==="All"
- ? `Loaded the ${scanned} most active markets, analyzed ${analyzed} by activity, liquidity, spread, and price direction, and kept ${all.length} ideas (${buyCount} BUY, ${watchCount} WATCH). Showing ${filtered.length}; BUY includes strict EV setups, strategy probes, and reduced-size allocation candidates for agents below their exposure floor. Avg evidence ${evAvg}.`
- : `Loaded the ${scanned} most active markets and analyzed ${analyzed}. Showing ${filtered.length} of ${pool.length} ${focus} ideas; agents may open strict EV entries, risk-capped probes, or tiny exploration trades while this is selected.`;
+ ? `Loaded the ${scanned} most active markets, analyzed ${analyzed} by activity, liquidity, spread, and independent time-window confirmation, and kept ${all.length} ideas (${buyCount} BUY, ${watchCount} WATCH). Showing ${filtered.length}; BUY requires a confirmed trend, reversal, liquid setup, or catalyst with positive margin after modeled friction. Cash is retained when no setup qualifies. Avg evidence ${evAvg}.`
+ : `Loaded the ${scanned} most active markets and analyzed ${analyzed}. Showing ${filtered.length} of ${pool.length} ${focus} ideas; agents can buy only independently confirmed setups that also fit their strategy and risk limits.`;
if(!filtered.length){root.innerHTML=`
No ${esc(focus)} suggestions in this live batch.
`;return;}
root.innerHTML=filtered.map(s=>{
const col=catColor(s.category);
@@ -2513,7 +2489,7 @@ function renderSuggestions(){
-
Net edge ${((Math.abs(s.net_edge!=null?s.net_edge:s.edge))*100).toFixed(1)}cEvidence ${Math.round((s.evidence_score||0)*100)}
+
Signal margin ${((Math.abs(s.net_edge!=null?s.net_edge:s.edge))*100).toFixed(1)}cEvidence ${Math.round((s.evidence_score||0)*100)}
Vol ${fmtUSD(s.volume)}24h ${fmtUSD(s.volume_24hr)}Resolves ${days}
${s.url?`
Open on Polymarket ↗`:""}
${PERSONAL_MODE?`
`:""}
@@ -2532,6 +2508,7 @@ function renderPortfolioTab(){
document.querySelectorAll("#agentSel .segbtn").forEach(el=>el.addEventListener("click",()=>{localStorage.setItem(VIEW_KEY,el.dataset.agent);renderPortfolioTab();}));
const p=st.agents[viewId]||defaultPortfolio();const eq=equity(p),pnl=eq-p.starting_balance;
const change24h=portfolioChange24h(p,eq);
+ const engineBase=Number(p.engine_baseline&&p.engine_baseline.version===SUGGESTION_ENGINE_VERSION?p.engine_baseline.equity:eq),enginePnl=eq-engineBase;
renderAgentBrief(cfg,p,st);
const stats=[
{ic:"💰",label:"Equity",value:fmtUSD(eq)},
@@ -2539,6 +2516,7 @@ function renderPortfolioTab(){
{ic:"📈",label:"P&L",value:fmtUSD(pnl),cls:signClass(pnl)},
{ic:"🎯",label:"Return",value:fmtPct((eq/p.starting_balance-1)*100),cls:signClass(pnl)},
{ic:"🕒",label:"24h change",value:format24h(change24h),cls:signClass(change24h.pct)},
+ {ic:"🧪",label:`v${SUGGESTION_ENGINE_VERSION}`,value:fmtPct(engineBase>0?(eq/engineBase-1)*100:0),cls:signClass(enginePnl)},
{ic:"📂",label:"Open",value:p.positions.length},
];
$("statsPf").innerHTML=stats.map(s=>`
${s.ic}
${s.label}
${s.value}
`).join("");
@@ -2591,14 +2569,14 @@ function renderAgentTechnical(agentId){
}
function agentCompetitionPlan(cfg,row,rank,leader){
if(rank===1)return "Plan: press the lead through the strongest qualifying trades while keeping stop-loss and exposure limits active.";
- if(cfg.id==="value")return "Plan: close the gap by waiting for the cleanest model-versus-market mispricings and avoiding crowded trades without enough edge.";
+ if(cfg.id==="value")return "Plan: close the gap by waiting for the cleanest confirmed movement after friction and avoiding crowded trades without enough signal margin.";
if(cfg.id==="momentum")return "Plan: attack fast-moving markets where fresh volume confirms attention, hoping speed beats slower value strategies.";
if(cfg.id==="favorite")return "Plan: grind upward through high-probability favorites, aiming to outlast more volatile agents during choppy markets.";
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==="breakout")return "Plan: stay nearly fully deployed in confirmed momentum setups and press the fastest breakouts where direction, activity, and edge agree.";
+ 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.";
return "Plan: keep following its rulebook and use fresh market data to pressure the leaderboard.";
@@ -2850,7 +2828,7 @@ function agentChatReply(agentId,question,history=[]){
return `My style is ${agentVoice(cfg).tone}.\n\n${agentPlainBlurb(cfg,st)}\n\nCurrent adaptation: ${decisionSummary(p)}`;
}
if(/risk|stop|loss|gain|take profit|sell/.test(combined)){
- return `My risk rules are mechanical:\n\nStop loss: sell the full remaining position at -18% from entry.\nGain stop: sell 25% at +75%, another 25% at +150%, and another 25% at +300%.\n\nRecent risk behavior: ${recentActionSummary(p)}.`;
+ return `My risk rules are mechanical:\n\nStop loss: sell the full remaining position at -18% from entry.\nProfit locks: sell 25% at three entry-aware targets that remain reachable below $1, then protect gains with a trailing exit. Signal exits need at least 24 hours of holding and six hours of confirmed conflict.\n\nRecent risk behavior: ${recentActionSummary(p)}.`;
}
if(/mistake|wrong|regret|fix|improve|better/.test(combined)){
return `${regretNarrative(row,p)}\n\nThe fix is not “bet harder.” The fix is stricter entries, fewer positions, and cutting trades when the model edge fades. That is the discipline I am trying to follow now.`;
@@ -3839,6 +3817,46 @@ function showTab(name){
document.querySelectorAll(".tab").forEach(t=>t.addEventListener("click",()=>showTab(t.dataset.tab)));
window.addEventListener("hashchange",()=>showTab(location.hash.slice(1)));
+window.PMA_ENGINE_DIAGNOSTICS=Object.freeze({
+ version:SUGGESTION_ENGINE_VERSION,
+ analyzeMarket,
+ gainStopTargets:(entry)=>GAIN_STOP_TIERS.map(t=>gainStopTarget({entry_price:Number(entry),cost:1,shares:1,gain_stops:{}},t)),
+ rules:Object.freeze({minimumPolicyHoldHours:MIN_POLICY_HOLD_HOURS,exitConfirmationHours:EXIT_CONFIRM_HOURS,maxAgentOverlap:2,stopLossPct:18}),
+});
+function runEngineSelfTest(){
+ const market=(overrides={})=>Object.assign({
+ id:"self-test",question:"Will the president win the election?",event:"Election",category:"Politics",tags:["election"],
+ yes_price:0.42,no_price:0.58,volume:500000,volume_24hr:60000,volume_1wk:280000,liquidity:75000,spread:0.01,
+ price_change_1h:0.004,price_change_1d:0.035,price_change_1w:0.08,days_to_resolution:30,
+ clob_token_ids:["yes","no"],url:"",closed:false,accepting_orders:true,
+ },overrides);
+ const news={"self-test":{source_count:3,latest_title:"Election polling update"}};
+ const trend=analyzeMarket(market(),news);
+ const noSignal=analyzeMarket(market({price_change_1h:0,price_change_1d:0.002,price_change_1w:-0.002}),news);
+ const reversal=analyzeMarket(market({price_change_1h:-0.01,price_change_1d:0.07,price_change_1w:0.02}),news);
+ 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};
+ const mature={entry_price:0.42,current_price:0.39,opened_at:hoursAgo(48),signal_conflict_since:hoursAgo(7),unrealized_pnl:-10,quality:"confirmed"};
+ const young={entry_price:0.42,current_price:0.39,opened_at:hoursAgo(2),signal_conflict_since:hoursAgo(1),unrealized_pnl:-10,quality:"confirmed"};
+ const trailing={entry_price:0.40,current_price:0.45,peak_price:0.55,opened_at:hoursAgo(48),unrealized_pnl:10,quality:"confirmed"};
+ const fresh=market();
+ const mock={agents:{}};AGENTS.forEach(a=>mock.agents[a.id]=defaultPortfolio());
+ ["value","momentum","favorite"].forEach((id,i)=>mock.agents[id].positions.push({market_id:"overlap-test",question:"Overlap test",side:"YES",shares:100,current_price:0.5,entry_price:0.5,cost:50,value:50,unrealized_pnl:0,conviction:70-i,opened_at:hoursAgo(24)}));
+ reduceStrategyOverlap(mock);
+ const overlapRemaining=AGENTS.reduce((sum,a)=>sum+mock.agents[a.id].positions.filter(p=>p.market_id==="overlap-test").length,0);
+ return {version:SUGGESTION_ENGINE_VERSION,
+ trend:{ready:trend.trade_ready,quality:trend.quality,side:trend.side,margin:trend.net_edge},
+ noSignal:{ready:noSignal.trade_ready,quality:noSignal.quality,signal:noSignal.signal_type,margin:noSignal.net_edge},
+ reversal:{ready:reversal.trade_ready,quality:reversal.quality,side:reversal.side,margin:reversal.net_edge},
+ highEntryTargets:targets,targetsReachable:targets.every(x=>x>0.82&&x<1),
+ exits:{youngConflict:exitReason(young,fresh,conflict,AGENTS[0]),matureConflict:exitReason(mature,fresh,conflict,AGENTS[0]),trailing:trailingProfitReason(trailing)},
+ overlapRemaining,rules:window.PMA_ENGINE_DIAGNOSTICS.rules};
+}
+if(new URLSearchParams(location.search).get("engine_test")==="1"){
+ const output=document.createElement("output");output.id="engineSelfTest";output.hidden=true;output.textContent=JSON.stringify(runEngineSelfTest());document.body.appendChild(output);
+}
+
/* ---------- Wire up ---------- */
applyPersonalMode();
initProviderConfig();
@@ -3969,7 +3987,7 @@ async function autoRunDueCycle(){
}
setInterval(()=>autoRunDueCycle(),RUN_INTERVAL_MS);
-/* On load: first visit runs a 7-day backtest; afterwards the shared portfolio advances as often as the cycle key allows. */
+/* On load: a new shared portfolio starts with an honest zero baseline; later cycles use live market snapshots. */
(async function init(){
showTab(location.hash.slice(1)||"overview");
fetchLiveBackendStatus();
@@ -3989,10 +4007,14 @@ setInterval(()=>autoRunDueCycle(),RUN_INTERVAL_MS);
return;
}
if(!st.seeded){
- btn.disabled=true;btn.textContent="Backtesting…";
- try{await backtestWeek(st);st.date=todayStr();st.last_run=nowIso();st.last_cycle_hour=currentCycleHour();saveState(st);if(!(await pushCloudState()))throw new Error("Cloud sync unavailable");renderAll();
- toast("7-day backtest complete — live tracking begins today.");}
- catch(e){setStatus("backtest error — click Run",false);}
+ btn.disabled=true;btn.textContent="Starting…";
+ try{
+ SNAP_TS=nowIso();AGENTS.forEach(a=>recordSnapshot(st.agents[a.id]));SNAP_TS=null;
+ st.seeded=true;st.date=todayStr();st.last_run=nowIso();st.last_cycle_hour=null;saveState(st);
+ if(!(await pushCloudState()))throw new Error("Cloud sync unavailable");
+ await runDailyCycle();renderAll();toast("Live paper tracking started from a clean zero baseline.");
+ }
+ catch(e){setStatus("start error — click Run",false);}
btn.disabled=false;btn.textContent="Run cycle";
}else if(st.last_cycle_hour!==currentCycleHour()){
btn.disabled=true;btn.textContent="Running…";