diff --git a/index.html b/index.html
index cf27843..157aa80 100644
--- a/index.html
+++ b/index.html
@@ -230,7 +230,8 @@ a.market-title:hover{color:#fff;text-decoration:underline;text-decoration-color:
.agent-brief{grid-template-columns:1fr}
.invest-grid{grid-template-columns:1fr}
.invest-agent{grid-template-columns:auto 1fr}
- .invest-agent input{grid-column:1 / -1;width:100%}
+.invest-agent input{grid-column:1 / -1;width:100%}
+ .paper-grid{grid-template-columns:1fr}
.pos{align-items:flex-start;gap:12px}
.pos .pq{max-width:none}
.right{min-width:92px}
@@ -263,6 +264,18 @@ a.market-title:hover{color:#fff;text-decoration:underline;text-decoration-color:
.exposure-row{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);font-size:13px}
.exposure-row:last-child{border-bottom:none}
.risk-note{border:1px solid rgba(251,113,133,.28);background:rgba(251,113,133,.08);border-radius:14px;padding:14px 16px;font-size:13px;color:#f4c6cf}
+.paper-grid{display:grid;grid-template-columns:.8fr 1.2fr;gap:18px}
+.account-row{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
+.input{border:1px solid var(--border);border-radius:9px;background:rgba(255,255,255,.05);color:var(--text);font:600 13px 'Inter',sans-serif;padding:9px 10px;min-width:0}
+.input:focus{outline:1px solid var(--accent)}
+.trade-list{display:grid;gap:12px}
+.trade-card{border:1px solid var(--border);border-radius:14px;background:rgba(255,255,255,.035);padding:14px}
+.trade-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:8px}
+.trade-title{font-weight:700;font-size:13.5px;line-height:1.35}
+.trade-actions{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:10px}
+.trade-actions .input{width:98px}
+.trade-meta{display:flex;gap:10px;flex-wrap:wrap;color:var(--muted);font-size:11.5px}
+.exact-table{display:grid;gap:10px}
/* ---------- misc ---------- */
.toast{position:fixed;bottom:24px;right:24px;z-index:100;background:rgba(18,24,40,.92);border:1px solid var(--accent);padding:13px 19px;border-radius:13px;font-size:14px;opacity:0;transform:translateY(12px);transition:.28s;pointer-events:none;max-width:360px;backdrop-filter:blur(12px);box-shadow:var(--shadow)}
@@ -298,6 +311,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
+
@@ -433,6 +447,48 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
+
+
Exact trades agents are holding
side, size, price, link
+
+
+
+
+
+
+ ๐งพ Paper Trading Accounts
+
+
+
+
Account
browser paper money
+
+
+
+
+
+
+
+
+
+
+
Copying an agent replaces this paper account with that agent's current cash and open positions. No real orders are placed.
+
+
+
+
+
+
Browse Polymarket ideas
place paper bets
+
+
+
+
+
+
@@ -491,8 +547,9 @@ const VIEW_KEY = "pma_view_v1";
const PF_SORT_KEY = "pma_portfolio_sort_v1";
const CHART_RANGE_KEY = "pma_chart_range_v1";
const INVEST_KEY = "pma_invest_allocations_v1";
+const PAPER_KEY = "pma_paper_accounts_v1";
const CATEGORIES = ["All","Politics","Sports","Crypto","Economy","Pop Culture","Other"];
-const SYNC_KEYS=[AGENTS_KEY,SUG_KEY,FOCUS_KEY,VIEW_KEY,PF_SORT_KEY,CHART_RANGE_KEY,INVEST_KEY];
+const SYNC_KEYS=[AGENTS_KEY,SUG_KEY,FOCUS_KEY,VIEW_KEY,PF_SORT_KEY,CHART_RANGE_KEY,INVEST_KEY,PAPER_KEY];
const CAT_COLORS = {Politics:"#fb7185",Sports:"#34d399",Crypto:"#fbbf24",Economy:"#7c8cff",
"Pop Culture":"#c77dff",Other:"#94a1bb",All:"#7c8cff",Copy:"#22d3ee"};
const DATA_API = "https://data-api.polymarket.com";
@@ -543,6 +600,8 @@ const $ = (id) => document.getElementById(id);
const fmtUSD = (n) => "$" + Number(n).toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2});
const fmtPct = (n) => (n>=0?"+":"") + Number(n).toFixed(2) + "%";
const signClass = (n) => (n>0?"pos-val":n<0?"neg-val":"");
+const marketSearchUrl=(q)=>`https://polymarket.com/search?query=${encodeURIComponent(q||"")}`;
+const positionUrl=(pos)=>pos&&pos.url?pos.url:marketSearchUrl(pos&&pos.question);
function partsInLocalTime(d=new Date()){
const parts=new Intl.DateTimeFormat("en-CA",{timeZone:"America/Toronto",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",hourCycle:"h23"}).formatToParts(d);
return Object.fromEntries(parts.map(p=>[p.type,p.value]));
@@ -1001,13 +1060,17 @@ async function fetchTraderPositions(wallet){
const d=await r.json();
return d.filter(p=>(p.outcome==="Yes"||p.outcome==="No")&&p.curPrice>0.02&&p.curPrice<0.98&&!p.redeemable&&p.size*p.curPrice>=1)
.map(p=>({asset:String(p.asset),conditionId:p.conditionId,outcome:p.outcome,side:String(p.outcome).toUpperCase(),
- curPrice:+p.curPrice,title:p.title||"market",value:p.size*p.curPrice}));
+ curPrice:+p.curPrice,title:p.title||"market",value:p.size*p.curPrice,url:rawPositionUrl(p)}));
}catch(e){return [];}
}
function prettyName(n,w){
if(n&&!/^0x/i.test(n))return n; // real display name / pseudonym
return "0x"+String(w).slice(2,6)+"โฆ"+String(w).slice(-4); // anonymous โ short wallet
}
+function rawPositionUrl(p){
+ const slug=p.eventSlug||p.event_slug||p.slug||p.marketSlug||p.market_slug;
+ return slug?`https://polymarket.com/event/${slug}`:marketSearchUrl(p.title||p.question||"");
+}
async function discoverWhales(){
const picked=[];
try{
@@ -1058,7 +1121,8 @@ async function runWhaleAgent(p,wallet,decision){
p.cash=+(p.cash-cost).toFixed(2);
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,
- original_shares:shares,original_cost:cost,unrealized_pnl:0,conviction:null,opened_at:cycleIso(),gain_stops:{},stop_losses:{}});
+ original_shares:shares,original_cost:cost,unrealized_pnl:0,conviction:null,opened_at:cycleIso(),url:t.url||marketSearchUrl(t.title),
+ gain_stops:{},stop_losses:{}});
p.history.push({date:logDay(),action:"OPEN",question:t.title,side:t.side,
detail:`Copied trade โ ${shares} ${t.side} '${t.title.slice(0,36)}' @ ${pct(entry)} for ${fmtUSD(cost)}`});
}
@@ -1090,7 +1154,8 @@ function runCopycat(p,leader,priceMap){
p.cash=+(p.cash-cost).toFixed(2);
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,
- original_shares:shares,original_cost:cost,unrealized_pnl:0,conviction:lp.conviction,opened_at:cycleIso(),gain_stops:{},stop_losses:{}});
+ original_shares:shares,original_cost:cost,unrealized_pnl:0,conviction:lp.conviction,opened_at:cycleIso(),url:lp.url||"",
+ gain_stops:{},stop_losses:{}});
p.history.push({date:logDay(),action:"OPEN",question:lp.question,side:lp.side,
detail:`Copied leader โ ${shares} ${lp.side} '${lp.question.slice(0,36)}' @ ${pct(entry)}`});
}
@@ -1297,7 +1362,7 @@ function board(){
const MEDALS=["๐ฅ","๐ฅ","๐ฅ"];
/* ---------- Rendering ---------- */
-function renderAll(){renderOverview();renderLeaderboard();renderSuggestions();renderPortfolioTab();renderInvestTab();renderAbout();}
+function renderAll(){renderOverview();renderLeaderboard();renderSuggestions();renderPortfolioTab();renderInvestTab();renderPaperTab();renderAbout();}
function renderOverview(){
const b=board(); const lead=b[0];
@@ -1643,11 +1708,13 @@ function renderPositions(positions){
return (b.unrealized_pnl||0)-(a.unrealized_pnl||0);
});
root.innerHTML=ordered.map(p=>{const pnl=p.unrealized_pnl||0,col=catColor(p.category);
+ const title=`${esc(p.question)}`;
return `
-
${esc(p.question)}
+
${title}
${p.category?`${esc(p.category)} `:""}${p.shares} ${p.side} @ ${Math.round(p.entry_price*100)}ยข โ ${Math.round(p.current_price*100)}ยข
${esc(stopLossLabel(p))}
${esc(gainStopLabel(p))}
+
${fmtUSD(p.value)}
${pnl>=0?"+":""}${fmtUSD(pnl)}
`;}).join("");
}
function renderHistory(history){
@@ -1655,6 +1722,83 @@ function renderHistory(history){
if(!history.length){root.innerHTML=`No activity yet.
`;return;}
root.innerHTML=history.map(h=>`${h.action}${h.date} โ ${esc(h.detail)}
`).join("");
}
+function defaultPaperAccount(name="Paper Trader"){
+ return {id:"acct_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6),name,cash:STARTING_BALANCE,starting_balance:STARTING_BALANCE,positions:[],history:[]};
+}
+function loadPaperStore(){
+ let store;try{store=JSON.parse(localStorage.getItem(PAPER_KEY));}catch(e){store=null;}
+ if(!store||!store.accounts){
+ const acct=defaultPaperAccount("My Paper Account");
+ store={activeId:acct.id,accounts:{[acct.id]:acct}};
+ localStorage.setItem(PAPER_KEY,JSON.stringify(store));
+ }
+ if(!store.accounts[store.activeId])store.activeId=Object.keys(store.accounts)[0];
+ return store;
+}
+function savePaperStore(store,sync=true){
+ localStorage.setItem(PAPER_KEY,JSON.stringify(store));
+ if(sync)pushCloudState();
+}
+function activePaperAccount(store=loadPaperStore()){
+ return store.accounts[store.activeId]||Object.values(store.accounts)[0];
+}
+function paperEquity(acct){
+ return acct.cash+(acct.positions||[]).reduce((s,p)=>s+(p.value||p.shares*p.current_price||0),0);
+}
+function markPaperToMarket(acct){
+ const sugs=(loadSuggestions().suggestions)||[];
+ const byId={};sugs.forEach(s=>byId[s.market_id]=s);
+ (acct.positions||[]).forEach(pos=>{
+ const s=byId[pos.market_id];
+ if(s){
+ const price=pos.side==="YES"?s.yes_price:s.no_price;
+ pos.current_price=+price.toFixed(4);
+ pos.url=s.url||pos.url||"";
+ }
+ pos.value=+(pos.shares*pos.current_price).toFixed(2);
+ pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
+ });
+}
+function cloneAgentBook(agentId){
+ const st=loadState(),src=st.agents[agentId],cfg=agentById(agentId);
+ if(!src)return null;
+ return {cash:+src.cash.toFixed(2),positions:JSON.parse(JSON.stringify(src.positions||[])),
+ history:[{date:logDay(),action:"COPY",detail:`Copied ${cfg.name}'s current paper book exactly.`}]};
+}
+function paperBuy(marketId,side,amount){
+ const store=loadPaperStore(),acct=activePaperAccount(store),s=((loadSuggestions().suggestions)||[]).find(x=>x.market_id===marketId);
+ if(!s)return toast("Market not found in current ideas.");
+ amount=Math.max(0,Math.min(Number(amount||0),acct.cash));
+ if(amount<1)return toast("Enter a paper amount first.");
+ const entry=side==="YES"?s.yes_price:s.no_price;
+ if(entry<=0||entry>=1)return toast("That market price is unavailable.");
+ const shares=+(amount/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
+ acct.cash=+(acct.cash-cost).toFixed(2);
+ acct.positions.push({market_id:s.market_id,question:s.question,side,shares,entry_price:+entry.toFixed(4),current_price:+entry.toFixed(4),
+ cost,value:cost,unrealized_pnl:0,category:s.category,opened_at:cycleIso(),url:s.url||"",paper:true});
+ acct.history.push({date:logDay(),action:"OPEN",detail:`Paper bought ${shares} ${side} '${s.question.slice(0,40)}' @ ${pct(entry)} for ${fmtUSD(cost)}`});
+ savePaperStore(store);renderPaperTab();toast("Paper bet placed.");
+}
+function paperClose(index){
+ const store=loadPaperStore(),acct=activePaperAccount(store),pos=acct.positions[index];
+ if(!pos)return;
+ const proceeds=+(pos.shares*pos.current_price).toFixed(2),realized=+(proceeds-pos.cost).toFixed(2);
+ acct.cash=+(acct.cash+proceeds).toFixed(2);
+ acct.history.push({date:logDay(),action:"CLOSE",detail:`Paper sold '${pos.question.slice(0,40)}' for ${fmtUSD(proceeds)} (${fmtUSD(realized)})`});
+ acct.positions.splice(index,1);
+ savePaperStore(store);renderPaperTab();
+}
+function renderExactTrades(){
+ const root=$("exactTrades");if(!root)return;
+ const rows=board().flatMap(row=>(row.p.positions||[]).map(pos=>({agent:row.c,pos})))
+ .sort((a,b)=>(b.pos.value||0)-(a.pos.value||0));
+ if(!rows.length){root.innerHTML=`No open agent trades yet.
`;return;}
+ root.innerHTML=rows.slice(0,80).map(({agent,pos})=>`
+
+
${esc(pos.side)}${Number(pos.shares||0).toFixed(2)} sharesentry ${pct(pos.entry_price)}now ${pct(pos.current_price)}${fmtUSD(pos.value||0)}
+
Open market
+
`).join("");
+}
function loadInvestAllocations(){
try{return JSON.parse(localStorage.getItem(INVEST_KEY))||{};}catch(e){return {};}
}
@@ -1668,7 +1812,7 @@ function agentExposureSummary(cfg,p,amount){
if(!amount||!open.length)return `Add an amount to preview this agent's largest mapped exposures.
`;
return open.map(pos=>{
const mapped=amount*((pos.value||0)/eq);
- const q=pos.url?`${esc(pos.question.slice(0,52))}${pos.question.length>52?"...":""}`:`${esc(pos.question.slice(0,52))}${pos.question.length>52?"...":""}`;
+ const q=`${esc(pos.question.slice(0,52))}${pos.question.length>52?"...":""}`;
return `${q}
${esc(pos.side)} ยท ${esc(pos.category||"Other")}${fmtUSD(mapped)}
`;
}).join("");
}
@@ -1711,6 +1855,69 @@ function renderInvestTab(){
input.oninput=saveDraft;
input.onchange=()=>{saveDraft();pushCloudState();renderInvestTab();};
});
+ renderExactTrades();
+}
+function renderPaperTab(){
+ const acctSelect=$("paperAccountSelect"),root=$("paperMarketList");
+ if(!acctSelect||!root)return;
+ const store=loadPaperStore(),acct=activePaperAccount(store);
+ markPaperToMarket(acct);savePaperStore(store,false);
+ acctSelect.innerHTML=Object.values(store.accounts).map(a=>``).join("");
+ acctSelect.onchange=()=>{store.activeId=acctSelect.value;savePaperStore(store);renderPaperTab();};
+ const agentSel=$("copyAgentSelect");
+ if(agentSel)agentSel.innerHTML=AGENTS.map(a=>``).join("");
+ const eq=paperEquity(acct),pnl=eq-acct.starting_balance;
+ $("paperStats").innerHTML=[
+ {ic:"๐ฐ",label:"Equity",value:fmtUSD(eq)},
+ {ic:"๐ต",label:"Cash",value:fmtUSD(acct.cash)},
+ {ic:"๐",label:"P&L",value:fmtUSD(pnl),cls:signClass(pnl)},
+ {ic:"๐ฏ",label:"Return",value:fmtPct((eq/acct.starting_balance-1)*100),cls:signClass(pnl)},
+ ].map(s=>`${s.ic}
${s.label}
${s.value}
`).join("");
+ renderPaperPositions(acct);
+ renderPaperMarkets();
+ renderPaperHistory(acct);
+}
+function renderPaperPositions(acct){
+ const root=$("paperPositions");if(!root)return;
+ if(!acct.positions.length){root.innerHTML=`No paper positions yet. Copy an agent or place a paper bet from the market browser.
`;return;}
+ root.innerHTML=acct.positions.map((p,i)=>{const pnl=p.unrealized_pnl||0,col=catColor(p.category);
+ return `
+
+
${p.category?`${esc(p.category)} `:""}${p.shares} ${p.side} @ ${Math.round(p.entry_price*100)}ยข โ ${Math.round(p.current_price*100)}ยข
+
+
${fmtUSD(p.value||0)}
${pnl>=0?"+":""}${fmtUSD(pnl)}
`;
+ }).join("");
+ document.querySelectorAll("[data-paper-close]").forEach(btn=>btn.onclick=()=>paperClose(Number(btn.dataset.paperClose)));
+}
+function renderPaperMarkets(){
+ const root=$("paperMarketList"),filter=$("paperFilterbar");if(!root)return;
+ const all=(loadSuggestions().suggestions)||[],focus=getFocus();
+ if(filter){
+ const counts={};CATEGORIES.forEach(c=>counts[c]=0);all.forEach(s=>{counts[counts[s.category]!==undefined?s.category:"Other"]++;});counts.All=all.length;
+ filter.innerHTML=CATEGORIES.map(c=>`${c}${counts[c]||0}`).join("");
+ document.querySelectorAll("[data-paper-cat]").forEach(el=>el.onclick=()=>{setFocus(el.dataset.paperCat);renderPaperTab();});
+ }
+ const shown=all.filter(s=>focus==="All"||!focus||s.category===focus).slice(0,24);
+ if(!shown.length){root.innerHTML=`Run a cycle to load paper-tradable Polymarket ideas.
`;return;}
+ root.innerHTML=shown.map(s=>`
+
+
${esc(s.category)}YES ${pct(s.yes_price)}NO ${pct(s.no_price)}${Math.round(s.conviction)} conviction
+
Open market
+
+
+
+
+
+
`).join("");
+ document.querySelectorAll("[data-paper-buy]").forEach(btn=>btn.onclick=()=>{
+ const input=[...document.querySelectorAll("[data-paper-amount]")].find(el=>el.dataset.paperAmount===btn.dataset.paperBuy);
+ paperBuy(btn.dataset.paperBuy,btn.dataset.side,input&&input.value);
+ });
+}
+function renderPaperHistory(acct){
+ const root=$("paperHistory");if(!root)return;
+ const hist=(acct.history||[]).slice(-40).reverse();
+ root.innerHTML=hist.length?hist.map(h=>`${h.action}${h.date} โ ${esc(h.detail)}
`).join(""):`No paper activity yet.
`;
}
function renderAbout(){
const root=$("aboutAgents");if(!root)return;
@@ -1721,7 +1928,7 @@ function renderAbout(){
function esc(s){return String(s).replace(/[&<>"]/g,c=>({"&":"&","<":"<",">":">",'"':"""}[c]));}
/* ---------- Tab routing ---------- */
-const TABS=["overview","leaderboard","suggestions","portfolio","invest","about"];
+const TABS=["overview","leaderboard","suggestions","portfolio","invest","paper","about"];
function showTab(name){
if(!TABS.includes(name))name="overview";
document.querySelectorAll(".tabpanel").forEach(p=>p.classList.toggle("active",p.dataset.tab===name));
@@ -1772,6 +1979,21 @@ $("resetBtn").addEventListener("click",()=>{
if(!confirm("Reset all ten agents to their $10,000 starting balance?"))return;
saveState(defaultState());localStorage.removeItem(SUG_KEY);pushCloudState(true);toast("All agents reset.");renderAll();
});
+$("createPaperAccountBtn").addEventListener("click",()=>{
+ const name=($("paperAccountName").value||"Paper Trader").trim().slice(0,40);
+ const store=loadPaperStore(),acct=defaultPaperAccount(name||"Paper Trader");
+ store.accounts[acct.id]=acct;store.activeId=acct.id;
+ $("paperAccountName").value="";
+ savePaperStore(store);renderPaperTab();toast("Paper account created.");
+});
+$("copyAgentBtn").addEventListener("click",()=>{
+ const agentId=$("copyAgentSelect").value,book=cloneAgentBook(agentId),cfg=agentById(agentId);
+ if(!book)return toast("Agent book unavailable.");
+ if(!confirm(`Replace this paper account with ${cfg.name}'s current paper positions?`))return;
+ const store=loadPaperStore(),acct=activePaperAccount(store);
+ acct.cash=book.cash;acct.positions=book.positions;acct.history=(acct.history||[]).concat(book.history);
+ savePaperStore(store);renderPaperTab();showTab("paper");toast(`Copied ${cfg.name} into paper account.`);
+});
window.addEventListener("focus",()=>refreshFromCloudAndRender(true));
document.addEventListener("visibilitychange",()=>{
if(!document.hidden)refreshFromCloudAndRender(true);