From c61b2769017095946ccd83d7c302a79e056a4217 Mon Sep 17 00:00:00 2001 From: Theodore Song Date: Sun, 5 Jul 2026 17:15:28 -0400 Subject: [PATCH] Add return chart ranges and average line --- index.html | 175 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 60 deletions(-) diff --git a/index.html b/index.html index 503f321..5b57dfa 100644 --- a/index.html +++ b/index.html @@ -111,6 +111,11 @@ h1,h2,h3,.brand-name{font-family:'Space Grotesk','Inter',sans-serif} .legend{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:14px} .leg{display:flex;align-items:center;gap:7px;font-size:12.5px;color:#cdd6e8} .legdot{width:10px;height:10px;border-radius:3px;display:inline-block} +.rangebar{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end} +.rangebtn{appearance:none;border:1px solid var(--border);background:rgba(255,255,255,.04);color:var(--muted); + font:700 11px 'Inter',sans-serif;padding:5px 8px;border-radius:8px;cursor:pointer} +.rangebtn:hover{color:var(--text);border-color:var(--border-strong)} +.rangebtn.active{color:#fff;background:linear-gradient(135deg,var(--accent),var(--accent2));border-color:transparent} /* ---------- leaderboard ---------- */ .lb-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(225px,1fr));gap:16px;margin-bottom:20px} @@ -291,10 +296,10 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
-

The Equity Race

+

Agent Returns

-
Past week is a day-by-day backtest: agents re-ran their strategy on each day's real historical prices; whales are replayed from their on-chain trade history. (Volume/liquidity signals use current values — Polymarket doesn't expose historical volume.) Live tracking begins today.
+
Use the range buttons to zoom from one week to all history. New live points are added whenever you click "Run cycle"; the dashed line tracks the average agent return.

Top Picks Today

highest conviction
@@ -308,10 +313,10 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
🏆 Leaderboard
-

Equity Race — all agents

$10,000 start each
+

Returns — all agents

$10,000 start each
-
Past week is a backtest — current holdings marked against real Polymarket price history. Live tracking takes over from today.
+
Past week is a backtest. After that, each manual or daily run adds another return snapshot for every agent.
@@ -383,6 +388,7 @@ const SUG_KEY = "pma_suggestions_v2"; const FOCUS_KEY = "pma_focus_v1"; const VIEW_KEY = "pma_view_v1"; const PF_SORT_KEY = "pma_portfolio_sort_v1"; +const CHART_RANGE_KEY = "pma_chart_range_v1"; const CATEGORIES = ["All","Politics","Sports","Crypto","Economy","Pop Culture","Other"]; const CAT_COLORS = {Politics:"#fb7185",Sports:"#34d399",Crypto:"#fbbf24",Economy:"#7c8cff", "Pop Culture":"#c77dff",Other:"#94a1bb",All:"#7c8cff",Copy:"#22d3ee"}; @@ -437,6 +443,7 @@ const signClass = (n) => (n>0?"pos-val":n<0?"neg-val":""); const todayStr = () => new Date().toISOString().slice(0,10); const nowIso = () => new Date().toISOString(); let SIM_DAY = null; // set during backtest so logs/snapshots use the simulated date +let SNAP_TS = null; // set during a live cycle so every agent gets the same chart x-value const logDay = () => SIM_DAY || todayStr(); function toast(msg){ let t=document.querySelector(".toast"); @@ -645,11 +652,11 @@ function openPositions(p,cfg,rankedSugs,focus){ } function recordSnapshot(p){ const eq=equity(p); - const snap={date:logDay(),timestamp:(SIM_DAY?new Date(SIM_DAY+"T12:00:00Z").toISOString():nowIso()),cash:+p.cash.toFixed(2), + const snap={date:logDay(),timestamp:(SIM_DAY?new Date(SIM_DAY+"T12:00:00Z").toISOString():(SNAP_TS||nowIso())),cash:+p.cash.toFixed(2), equity:+eq.toFixed(2),pnl:+(eq-p.starting_balance).toFixed(2), return_pct:+((eq/p.starting_balance-1)*100).toFixed(2),open_positions:p.positions.length}; - p.snapshots=p.snapshots.filter(s=>s.date!==snap.date); - p.snapshots.push(snap);p.snapshots.sort((a,b)=>a.dates.date!==snap.date):p.snapshots.filter(s=>s.timestamp!==snap.timestamp); + p.snapshots.push(snap);p.snapshots.sort((a,b)=>new Date(a.timestamp||a.date)-new Date(b.timestamp||b.date)); } /* ---------- Copy-trading: Polymarket leaders ---------- */ const hasAsset=(p,a)=>p.positions.some(x=>x.asset===a); @@ -882,50 +889,53 @@ async function backtestWeek(st){ } async function runDailyCycle(){ + SNAP_TS=nowIso(); setStatus("fetching markets…",true); - const markets=await fetchMarkets(4,100); - setStatus("analyzing…",true); - const sugs=generateSuggestions(markets,12); - saveSuggestions(sugs); - const st=loadState(); - const focus=getFocus(); - // Price map for suggestion-market positions (strategy + copycat only). - const ids=new Set(); - AGENTS.filter(a=>a.kind==="strategy"||a.kind==="copycat").forEach(a=> - (st.agents[a.id].positions||[]).forEach(pos=>{if(pos.market_id)ids.add(pos.market_id);})); - setStatus("marking positions…",true); - const priceMap={}; - for(const id of ids){priceMap[id]=await fetchMarketPrice(id);} - // 1) in-house strategy agents - setStatus("strategy agents trading…",true); - for(const cfg of AGENTS.filter(a=>a.kind==="strategy")){ - const p=st.agents[cfg.id]; - markToMarket(p,priceMap); - openPositions(p,cfg,cfg.rank(sugs),focus); - recordSnapshot(p); - } - // 2) copycat shadows the leading strategy agent - const stratBoard=AGENTS.filter(a=>a.kind==="strategy") - .map(a=>({id:a.id,eq:equity(st.agents[a.id])})).sort((x,y)=>y.eq-x.eq); - st.copycatLeader=stratBoard[0].id; - runCopycat(st.agents["copycat"],st.agents[st.copycatLeader],priceMap); - // 3) whale-copy agents mirror top Polymarket traders - if(AGENTS.some(a=>a.kind==="whale"&&!st.whales[a.id])){ - setStatus("finding top Polymarket traders…",true); - const picked=await discoverWhales(); - AGENTS.filter(a=>a.kind==="whale").forEach(a=>{if(!st.whales[a.id]&&picked[a.slot])st.whales[a.id]=picked[a.slot];}); - } - for(const a of AGENTS.filter(x=>x.kind==="whale")){ - const wh=st.whales[a.id]; - if(!wh){recordSnapshot(st.agents[a.id]);continue;} - setStatus(`copying ${wh.name}…`,true); - try{await runWhaleAgent(st.agents[a.id],wh.wallet);} - catch(e){recordSnapshot(st.agents[a.id]);} - } - st.date=todayStr();st.last_run=nowIso(); - saveState(st); - setStatus("up to date",false); - return {suggestions:sugs.length}; + try{ + const markets=await fetchMarkets(4,100); + setStatus("analyzing…",true); + const sugs=generateSuggestions(markets,12); + saveSuggestions(sugs); + const st=loadState(); + const focus=getFocus(); + // Price map for suggestion-market positions (strategy + copycat only). + const ids=new Set(); + AGENTS.filter(a=>a.kind==="strategy"||a.kind==="copycat").forEach(a=> + (st.agents[a.id].positions||[]).forEach(pos=>{if(pos.market_id)ids.add(pos.market_id);})); + setStatus("marking positions…",true); + const priceMap={}; + for(const id of ids){priceMap[id]=await fetchMarketPrice(id);} + // 1) in-house strategy agents + setStatus("strategy agents trading…",true); + for(const cfg of AGENTS.filter(a=>a.kind==="strategy")){ + const p=st.agents[cfg.id]; + markToMarket(p,priceMap); + openPositions(p,cfg,cfg.rank(sugs),focus); + recordSnapshot(p); + } + // 2) copycat shadows the leading strategy agent + const stratBoard=AGENTS.filter(a=>a.kind==="strategy") + .map(a=>({id:a.id,eq:equity(st.agents[a.id])})).sort((x,y)=>y.eq-x.eq); + st.copycatLeader=stratBoard[0].id; + runCopycat(st.agents["copycat"],st.agents[st.copycatLeader],priceMap); + // 3) whale-copy agents mirror top Polymarket traders + if(AGENTS.some(a=>a.kind==="whale"&&!st.whales[a.id])){ + setStatus("finding top Polymarket traders…",true); + const picked=await discoverWhales(); + AGENTS.filter(a=>a.kind==="whale").forEach(a=>{if(!st.whales[a.id]&&picked[a.slot])st.whales[a.id]=picked[a.slot];}); + } + for(const a of AGENTS.filter(x=>x.kind==="whale")){ + const wh=st.whales[a.id]; + if(!wh){recordSnapshot(st.agents[a.id]);continue;} + setStatus(`copying ${wh.name}…`,true); + try{await runWhaleAgent(st.agents[a.id],wh.wallet);} + catch(e){recordSnapshot(st.agents[a.id]);} + } + st.date=todayStr();st.last_run=SNAP_TS; + saveState(st); + setStatus("up to date",false); + return {suggestions:sugs.length}; + }finally{SNAP_TS=null;} } /* ---------- Leaderboard helpers ---------- */ @@ -1022,26 +1032,71 @@ function renderLeaderboard(){ }); drawCombo("chart2","comboLegend2"); } +const CHART_RANGES=[ + {id:"1w",label:"1W",days:7}, + {id:"1m",label:"1M",days:30}, + {id:"3m",label:"3M",days:90}, + {id:"6m",label:"6M",days:180}, + {id:"1y",label:"1Y",days:365}, + {id:"5y",label:"5Y",days:1825}, + {id:"all",label:"ALL",days:null}, +]; +function chartRange(){const r=localStorage.getItem(CHART_RANGE_KEY)||"1w";return CHART_RANGES.some(x=>x.id===r)?r:"1w";} +function renderChartRanges(){ + const active=chartRange(); + document.querySelectorAll("[data-chart-ranges]").forEach(root=>{ + root.innerHTML=CHART_RANGES.map(r=>``).join(""); + }); + document.querySelectorAll(".rangebtn").forEach(btn=>btn.onclick=()=>{localStorage.setItem(CHART_RANGE_KEY,btn.dataset.range);renderAll();}); +} +const snapTime=(s)=>new Date(s.timestamp||`${s.date}T12:00:00Z`).getTime(); +function rangeFilteredTimes(times){ + const range=CHART_RANGES.find(r=>r.id===chartRange())||CHART_RANGES[0]; + if(!range.days)return times; + const newest=Math.max(...times); + const cutoff=newest-range.days*86400000; + const filtered=times.filter(t=>t>=cutoff); + return filtered.length?filtered:times.slice(-1); +} function drawCombo(svgId,legendId){ const st=loadState(); - const series=AGENTS.map(c=>({c,snaps:(st.agents[c.id]&&st.agents[c.id].snapshots)||[]})); - const allDates=[...new Set(series.flatMap(s=>s.snaps.map(x=>x.date)))].sort(); + renderChartRanges(); + const series=AGENTS.map(c=>({c,snaps:((st.agents[c.id]&&st.agents[c.id].snapshots)||[]) + .filter(s=>Number.isFinite(snapTime(s))).sort((a,b)=>snapTime(a)-snapTime(b))})); + const allTimes=rangeFilteredTimes([...new Set(series.flatMap(s=>s.snaps.map(snapTime)))].sort((a,b)=>a-b)); const svg=$(svgId); if(!svg)return; - if(!allDates.length){svg.innerHTML="";if($(legendId))$(legendId).innerHTML=`No history yet — click "Run cycle".`;return;} - const W=600,H=240,pad=10; const vals=[STARTING_BALANCE]; - const lines=series.map(s=>{let last=STARTING_BALANCE;const pts=allDates.map(d=>{const snap=s.snaps.find(x=>x.date===d);if(snap)last=snap.equity;vals.push(last);return last;});return {c:s.c,pts};}); - const min=Math.min(...vals)*0.999,max=Math.max(...vals)*1.001,span=(max-min)||1; - const x=i=>allDates.length===1?W/2:pad+(i/(allDates.length-1))*(W-2*pad); + if(!allTimes.length){svg.innerHTML="";if($(legendId))$(legendId).innerHTML=`No history yet — click "Run cycle".`;return;} + const W=600,H=240,pad=14; const vals=[0]; + const lines=series.map(s=>{ + let last=0,idx=0; + const pts=allTimes.map(t=>{ + while(idx+(lines.reduce((sum,l)=>sum+l.pts[i],0)/Math.max(1,lines.length)).toFixed(2)); + vals.push(...avgPts); + const min=Math.min(...vals,0),max=Math.max(...vals,0),span=(max-min)||1; + const x=i=>allTimes.length===1?W/2:pad+(i/(allTimes.length-1))*(W-2*pad); const y=v=>H-pad-((v-min)/span)*(H-2*pad); - let inner=""; + const zeroY=y(0).toFixed(1); + let inner=``; for(const ln of lines){ const d=ln.pts.map((v,i)=>`${i===0?"M":"L"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" "); - const dot=allDates.length===1?``:""; + const dot=allTimes.length===1?``:""; inner+=`${dot}`; } + const avgD=avgPts.map((v,i)=>`${i===0?"M":"L"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" "); + inner+=``; svg.innerHTML=inner; if($(legendId)){ - $(legendId).innerHTML=board().map(r=>`${r.c.emoji} ${r.c.name} ${fmtPct(r.ret)}`).join(""); + const b=board();const avg=b.reduce((s,r)=>s+r.ret,0)/b.length; + $(legendId).innerHTML=`Average ${fmtPct(avg)}`+ + b.map(r=>`${r.c.emoji} ${r.c.name} ${fmtPct(r.ret)}`).join(""); } } function renderFilterBar(all){