diff --git a/index.html b/index.html
index dc574a1..2307292 100644
--- a/index.html
+++ b/index.html
@@ -726,7 +726,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
@@ -947,6 +947,7 @@ const W={LIQ:0.25,MOM:0.25,MIS:0.30,TIME:0.20};
const MIN_VOLUME=12000;
const MIN_LIQUIDITY=900;
const MIN_ENTRY_EDGE=0.022;
+const MIN_ENTRY_DAYS=2.5;
const EDGE_SCALE=0.11;
const MAX_STRATEGY_POSITIONS=22;
const SUGGESTION_TOTAL=180;
@@ -1186,7 +1187,7 @@ function adaptiveDecision(cfg,p,rank,total,leaderEq){
reason+=` Emotion layer: alarm keeps it from revenge trading.`;
}
if((p.cash/Math.max(eq,1))<0.12){maxNew=Math.max(1,Math.min(maxNew,2));reason+=" Cash is tight, so new entries are rationed.";}
- const maxCap=cfg.kind==="whale"?0.34:0.12;
+ const maxCap=cfg.kind==="whale"?0.16:0.12;
return {mode,reason,emotion:emo.mood,urgency:+emo.urgency.toFixed(2),minConv:Math.max(0,Math.round(minConv)),maxNew:Math.max(1,Math.round(maxNew)),maxFrac:+Math.min(maxCap,Math.max(0.01,maxFrac)).toFixed(3),reserve};
}
const stopKey=(posOrId)=>typeof posOrId==="string"?posOrId:String(posOrId.asset||posOrId.market_id||"");
@@ -1396,7 +1397,8 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
p.lastDecision=decision||null;
if((p.positions||[]).length>=MAX_STRATEGY_POSITIONS)return [];
const cands=rankedSugs.map(s=>peerAdjustedSuggestion(s,peerStats)).filter(s=>(s.side==="YES"||s.side==="NO")&&s.peer_conviction>=d.minConv
- &&Math.abs(s.edge)>=MIN_ENTRY_EDGE&&!hasPosition(p,s.market_id)&&!hasStoppedToday(p,s.market_id)&&(focus==="All"||!focus||s.category===focus))
+ &&Math.abs(s.edge)>=MIN_ENTRY_EDGE&&(s.days_to_resolution==null||s.days_to_resolution>=MIN_ENTRY_DAYS)
+ &&!hasPosition(p,s.market_id)&&!hasStoppedToday(p,s.market_id)&&(focus==="All"||!focus||s.category===focus))
.sort((a,b)=>(b.peer_conviction-a.peer_conviction)||((b.peer_boost||0)-(a.peer_boost||0)));
const avoid=avoidMarketIds||new Set();
const ordered=cands.filter(s=>!avoid.has(s.market_id)).concat(cands.filter(s=>avoid.has(s.market_id)));
@@ -1471,8 +1473,9 @@ function copycatCandidateScore(cfg,p){
}
function chooseCopycatLeader(st){
const cands=AGENTS.filter(a=>a.kind==="strategy").map(cfg=>copycatCandidateScore(cfg,st.agents[cfg.id]||defaultPortfolio()));
- cands.sort((a,b)=>b.score-a.score);
- return cands[0]||null;
+ const eligible=cands.filter(c=>c.m.last>0&&c.m.recent>-3&&c.openPnl>-300);
+ (eligible.length?eligible:cands).sort((a,b)=>b.score-a.score);
+ return (eligible.length?eligible:cands)[0]||null;
}
function copycatReason(candidate){
if(!candidate)return "No eligible strategy has enough data, so the copycat is waiting.";
@@ -1525,7 +1528,7 @@ async function discoverWhales(){
return picked;
}
async function runWhaleAgent(p,wallet,decision){
- const d=decision||{maxNew:12,maxFrac:0.34,reserve:0.05};
+ const d=decision||{maxNew:6,maxFrac:0.12,reserve:0.12};
p.lastDecision=decision||null;
// mark existing copied positions to market via CLOB token price
for(const pos of p.positions){
@@ -1547,13 +1550,14 @@ async function runWhaleAgent(p,wallet,decision){
}
}
p.positions=keep;
- const targets=[...tps].sort((a,b)=>b.value-a.value).slice(0,d.maxNew); // copy their biggest bets, adapted by stance
+ const maxNew=Math.min(d.maxNew||6,6),maxFrac=Math.min(d.maxFrac||0.12,0.16),reserve=Math.max(d.reserve||0.12,0.10);
+ const targets=[...tps].filter(t=>t.curPrice>=0.06&&t.curPrice<=0.94).sort((a,b)=>b.value-a.value).slice(0,maxNew); // copy their biggest bets, adapted by stance
const book=targets.reduce((s,t)=>s+t.value,0)||1;
for(const t of targets){
if(hasAsset(p,t.asset)||hasStoppedToday(p,t.asset))continue;
- const eq=equity(p),investable=p.cash-eq*d.reserve; if(investable<=50)break;
+ const eq=equity(p),investable=p.cash-eq*reserve; if(investable<=50)break;
const weight=t.value/book;
- const stake=Math.min(eq*Math.min(d.maxFrac,weight),investable);
+ const stake=Math.min(eq*Math.min(maxFrac,weight),investable);
if(stake<40)continue;
const entry=t.curPrice; const shares=+(stake/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
if(cost>p.cash)continue;
@@ -1571,11 +1575,20 @@ async function runWhaleAgent(p,wallet,decision){
/* ---------- Copycat: shadow the strongest momentum/MACD strategy ---------- */
function runCopycat(p,leader,priceMap,candidate=null){
const reason=candidate?copycatReason(candidate):"watching the strategy board and rotating toward the account with the best momentum/MACD setup.";
- p.lastDecision={mode:"Momentum Copy",reason,minConv:0,maxNew:leader.positions.length,maxFrac:0.2,reserve:0.05,
+ const safeToCopy=candidate&&candidate.m.last>0&&candidate.m.recent>-3&&candidate.openPnl>-300;
+ p.lastDecision={mode:safeToCopy?"Selective Momentum Copy":"Copycat Guard",reason:safeToCopy?reason:`No strategy is healthy enough to copy aggressively, so I am protecting cash instead. ${reason}`,minConv:0,maxNew:safeToCopy?leader.positions.length:0,maxFrac:0.1,reserve:0.12,
copyScore:candidate?+candidate.score.toFixed(2):null,
macd:candidate?+candidate.m.macd.toFixed(2):null,
momentum:candidate?+candidate.m.recent.toFixed(2):null};
markToMarket(p,priceMap);
+ if(!safeToCopy){
+ for(const pos of [...p.positions]){
+ if((pos.unrealized_pnl||0)<-75)closePosition(p,pos,"Copycat guard cut weak copied position","EXIT");
+ }
+ p.positions=p.positions.filter(pos=>!pos.closed_at);
+ recordSnapshot(p);
+ return;
+ }
const held=new Set(leader.positions.map(x=>x.market_id));
const keep=[];
for(const pos of p.positions){
@@ -1584,12 +1597,13 @@ function runCopycat(p,leader,priceMap,candidate=null){
}else keep.push(pos);
}
p.positions=keep;
- const book=leader.positions.reduce((s,x)=>s+(x.value||x.cost),0)||1;
- for(const lp of [...leader.positions].sort((a,b)=>(b.value||b.cost)-(a.value||a.cost))){
+ const leaderPicks=leader.positions.filter(x=>(x.unrealized_pnl||0)>=-50&&(x.current_price||0)>0.05&&(x.current_price||0)<0.95);
+ const book=leaderPicks.reduce((s,x)=>s+(x.value||x.cost),0)||1;
+ for(const lp of [...leaderPicks].sort((a,b)=>(b.value||b.cost)-(a.value||a.cost))){
if(hasPosition(p,lp.market_id)||hasStoppedToday(p,lp.market_id))continue;
const eq=equity(p),investable=p.cash-eq*0.05; if(investable<=50)break;
const weight=(lp.value||lp.cost)/book;
- const stake=Math.min(eq*Math.min(0.2,weight),investable);
+ const stake=Math.min(eq*Math.min(0.1,weight),investable);
if(stake<50)continue;
const entry=lp.current_price; if(entry<=0||entry>=1)continue;
const shares=+(stake/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
@@ -1812,11 +1826,13 @@ function renderAll(){renderOverview();renderLeaderboard();renderSuggestions();re
function renderOverview(){
const b=board(); const lead=b[0];
const avgRet=b.reduce((s,r)=>s+r.ret,0)/b.length;
+ const core=b.filter(r=>r.c.kind==="strategy"),coreAvg=core.reduce((s,r)=>s+r.ret,0)/Math.max(1,core.length);
const sugCount=(loadSuggestions().suggestions||[]).length;
const stats=[
{ic:lead.c.emoji,label:"Leader",value:lead.c.name.split(" ")[0]},
{ic:"📈",label:"Leader return",value:fmtPct(lead.ret),cls:signClass(lead.pnl)},
- {ic:"⚖️",label:"Avg return",value:fmtPct(avgRet),cls:signClass(avgRet)},
+ {ic:"⚖️",label:"All-agent avg",value:fmtPct(avgRet),cls:signClass(avgRet)},
+ {ic:"🧠",label:"Core strategy avg",value:fmtPct(coreAvg),cls:signClass(coreAvg)},
{ic:"💡",label:"Scored ideas",value:sugCount},
{ic:"🏆",label:"Agents",value:AGENTS.length},
];
@@ -2034,7 +2050,9 @@ function drawCombo(svgId,legendId){
svg.innerHTML=inner;
if($(legendId)){
const b=board();const avg=b.reduce((s,r)=>s+r.ret,0)/b.length;
- $(legendId).innerHTML=`Average ${fmtPct(avg)}`+
+ const core=b.filter(r=>r.c.kind==="strategy"),coreAvg=core.reduce((s,r)=>s+r.ret,0)/Math.max(1,core.length);
+ $(legendId).innerHTML=`All Avg ${fmtPct(avg)}`+
+ `Core Avg ${fmtPct(coreAvg)}`+
b.map(r=>`${r.c.emoji} ${r.c.name} ${fmtPct(r.ret)}`).join("");
}
}