Seed a real trailing-week backtest: mark current holdings against Polymarket price history

This commit is contained in:
theodore-song
2026-07-05 15:09:52 -04:00
parent 1265e2dde9
commit 31afd37b34
+64 -3
View File
@@ -257,6 +257,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<div class="card-h"><h3>The Equity Race</h3><span class="small muted" id="lastRun"></span></div>
<svg id="chart" viewBox="0 0 600 240" preserveAspectRatio="none"></svg>
<div class="legend" id="comboLegend"></div>
<div class="small muted" style="margin-top:10px">Past week is a backtest — current holdings marked against real Polymarket price history. Live tracking takes over from today.</div>
</div>
<div class="card hoverable">
<div class="card-h"><h3>Top Picks Today</h3><span class="small muted">highest conviction</span></div>
@@ -273,6 +274,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<div class="card-h"><h3>Equity Race — all agents</h3><span class="small muted">$10,000 start each</span></div>
<svg id="chart2" viewBox="0 0 600 240" preserveAspectRatio="none"></svg>
<div class="legend" id="comboLegend2"></div>
<div class="small muted" style="margin-top:10px">Past week is a backtest — current holdings marked against real Polymarket price history. Live tracking takes over from today.</div>
</div>
</section>
@@ -421,6 +423,7 @@ function normalizeMarket(raw){
const tags=Array.isArray(raw.tags)?raw.tags:[];
return {category:classifyCategory(tags),tags:tags.map(t=>t.label).filter(Boolean).slice(0,4),
id:String(raw.id),question:(raw.question||"").trim(),event:(ev.title||"").trim(),
clob_token_ids:parseJsonField(raw.clobTokenIds).map(String),
yes_price:+yes.toFixed(4),no_price:+prices[1].toFixed(4),
volume:toNum(raw.volumeNum||raw.volume),volume_24hr:toNum(raw.volume24hr),volume_1wk:toNum(raw.volume1wk),
liquidity:toNum(raw.liquidityNum||raw.liquidity),end_date:raw.endDateIso||raw.endDate,
@@ -471,6 +474,7 @@ function analyzeMarket(m){
if(mis>0.4)drivers.push("notable price/value gap");
if(!drivers.length)drivers.push("thin signal");
return {market_id:m.id,question:m.question,event:m.event,url:m.url,category:m.category,tags:m.tags,
clob_yes:(m.clob_token_ids||[])[0]||null,clob_no:(m.clob_token_ids||[])[1]||null,
yes_price:p,no_price:m.no_price,fair_value:+fair.toFixed(4),edge:+edgeYes.toFixed(4),
side,entry_price:+entry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,
days_to_resolution:m.days_to_resolution!=null?+m.days_to_resolution.toFixed(1):null,drivers,rationale};
@@ -485,7 +489,7 @@ function generateSuggestions(markets,perCategory=12){
/* ---------- Multi-agent store ---------- */
function defaultPortfolio(){return {cash:STARTING_BALANCE,starting_balance:STARTING_BALANCE,positions:[],closed:[],history:[],snapshots:[]};}
function defaultState(){const agents={};AGENTS.forEach(a=>agents[a.id]=defaultPortfolio());return {date:null,last_run:null,agents,whales:{},copycatLeader:null};}
function defaultState(){const agents={};AGENTS.forEach(a=>agents[a.id]=defaultPortfolio());return {date:null,last_run:null,agents,whales:{},copycatLeader:null,seeded:false};}
function loadState(){
let st; try{st=JSON.parse(localStorage.getItem(AGENTS_KEY));}catch(e){st=null;}
if(!st||!st.agents)st=defaultState();
@@ -537,6 +541,7 @@ function openPositions(p,cfg,rankedSugs,focus){
if(cost>p.cash)continue;
p.cash=+(p.cash-cost).toFixed(2);
p.positions.push({market_id:s.market_id,question:s.question,side:s.side,shares,
token_id:(s.side==="YES"?s.clob_yes:s.clob_no)||null,
entry_price:+entry.toFixed(4),current_price:+entry.toFixed(4),cost,value:cost,
unrealized_pnl:0,conviction:s.conviction,category:s.category,opened_at:nowIso(),url:s.url||""});
p.history.push({date:todayStr(),action:"OPEN",question:s.question,side:s.side,
@@ -615,7 +620,7 @@ async function runWhaleAgent(p,wallet){
const entry=t.curPrice; const shares=+(stake/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
if(cost>p.cash)continue;
p.cash=+(p.cash-cost).toFixed(2);
p.positions.push({asset:t.asset,market_id:t.conditionId,question:t.title,side:t.side,category:"Copy",
p.positions.push({asset:t.asset,token_id:t.asset,market_id:t.conditionId,question:t.title,side:t.side,category:"Copy",
shares,entry_price:+entry.toFixed(4),current_price:+entry.toFixed(4),cost,value:cost,unrealized_pnl:0,conviction:null});
p.history.push({date:todayStr(),action:"OPEN",question:t.title,side:t.side,
detail:`Copied trade — ${shares} ${t.side} '${t.title.slice(0,36)}' @ ${pct(entry)} for ${fmtUSD(cost)}`});
@@ -648,7 +653,7 @@ function runCopycat(p,leader,priceMap){
const shares=+(stake/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
if(cost>p.cash)continue;
p.cash=+(p.cash-cost).toFixed(2);
p.positions.push({market_id:lp.market_id,question:lp.question,side:lp.side,category:lp.category,
p.positions.push({market_id:lp.market_id,token_id:lp.token_id||null,question:lp.question,side:lp.side,category:lp.category,
shares,entry_price:+entry.toFixed(4),current_price:+entry.toFixed(4),cost,value:cost,unrealized_pnl:0,conviction:lp.conviction});
p.history.push({date:todayStr(),action:"OPEN",question:lp.question,side:lp.side,
detail:`Copied leader — ${shares} ${lp.side} '${lp.question.slice(0,36)}' @ ${pct(entry)}`});
@@ -656,6 +661,54 @@ function runCopycat(p,leader,priceMap){
recordSnapshot(p);
}
/* ---------- Trailing-week backtest seeding ----------
Marks each agent's CURRENT holdings against real Polymarket price history
so the equity curves show a plausible past week instead of a flat line. */
function lastNDates(n){const out=[];const now=new Date();for(let i=n-1;i>=0;i--){const x=new Date(now);x.setUTCDate(now.getUTCDate()-i);out.push(x.toISOString().slice(0,10));}return out;}
async function fetchPriceHistory(tokenId){
try{const r=await fetch(`${CLOB}/prices-history?market=${tokenId}&interval=1w&fidelity=1440`);
if(!r.ok)return null;const d=await r.json();const map={};
(d.history||[]).forEach(h=>{map[new Date(h.t*1000).toISOString().slice(0,10)]=h.p;});
return Object.keys(map).length?map:null;}catch(e){return null;}
}
async function fetchClobTokens(marketId){
try{const r=await fetch(`${GAMMA}/markets/${marketId}`);if(!r.ok)return null;const d=await r.json();
const t=parseJsonField(d.clobTokenIds).map(String);return t.length===2?t:null;}catch(e){return null;}
}
function priceOnDay(map,day,fallback){
if(!map)return fallback;let best=null,bd=null;
for(const dt in map){if(dt<=day&&(bd===null||dt>bd)){bd=dt;best=map[dt];}}
if(best!=null)return best;
const ks=Object.keys(map).sort();return ks.length?map[ks[0]]:fallback;
}
async function seedWeekInto(st){
// 1) make sure every open position knows its CLOB token id
for(const a of AGENTS)for(const pos of st.agents[a.id].positions){
if(pos.token_id)continue;
if(pos.asset){pos.token_id=pos.asset;continue;}
if(pos.market_id){const t=await fetchClobTokens(pos.market_id);if(t)pos.token_id=(pos.side==="YES")?t[0]:t[1];}
}
// 2) pull 7-day price history for each unique token once
const ids=new Set();
for(const a of AGENTS)for(const pos of st.agents[a.id].positions)if(pos.token_id)ids.add(pos.token_id);
const hist={};
for(const id of ids)hist[id]=await fetchPriceHistory(id);
// 3) rebuild each agent's snapshots across the past week
const dates=lastNDates(7);
for(const a of AGENTS){
const p=st.agents[a.id];
p.snapshots=dates.map(day=>{
let pv=0;
for(const pos of p.positions){pv+=pos.shares*priceOnDay(hist[pos.token_id],day,pos.entry_price);}
const eq=p.cash+pv;
return {date:day,timestamp:new Date(day+"T12:00:00Z").toISOString(),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,seeded:true};
});
}
st.seeded=true;
}
async function runDailyCycle(){
setStatus("fetching markets…",true);
const markets=await fetchMarkets(4,100);
@@ -697,6 +750,7 @@ async function runDailyCycle(){
try{await runWhaleAgent(st.agents[a.id],wh.wallet);}
catch(e){recordSnapshot(st.agents[a.id]);}
}
if(!st.seeded){setStatus("reconstructing past week…",true);await seedWeekInto(st);}
st.date=todayStr();st.last_run=nowIso();
saveState(st);
setStatus("up to date",false);
@@ -912,6 +966,13 @@ $("resetBtn").addEventListener("click",()=>{
catch(e){setStatus("error — click Run to retry",false);}
btn.disabled=false;btn.textContent="Run cycle";
}else{setStatus("up to date",false);}
// If a cycle already ran today but we haven't seeded the past week yet, do it now.
const stx=loadState();
if(!stx.seeded && AGENTS.some(a=>stx.agents[a.id].positions.length)){
setStatus("reconstructing past week…",true);
try{await seedWeekInto(stx);saveState(stx);renderAll();toast("Backfilled the past week of history.");}catch(e){}
setStatus("up to date",false);
}
})();
</script>
</body>