mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-23 20:48:08 +00:00
7548 lines
602 KiB
HTML
7548 lines
602 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta http-equiv="Cache-Control" content="no-store, max-age=0, must-revalidate" />
|
||
<meta http-equiv="Pragma" content="no-cache" />
|
||
<meta http-equiv="Expires" content="0" />
|
||
<title>Polymarket Analyst — 10 AI Agents Compete to Trade Prediction Markets</title>
|
||
<meta name="description" content="Frequent AI analysis of Polymarket with ten competing autonomous paper-trading strategies and a live leaderboard." />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏆</text></svg>">
|
||
<style>
|
||
:root{
|
||
--bg:#070b14;--bg2:#0b1120;--panel:rgba(22,29,46,.66);--panel-solid:#161d2e;
|
||
--panel-2:rgba(36,45,68,.6);--border:rgba(255,255,255,.09);--border-strong:rgba(255,255,255,.16);
|
||
--text:#eef2f9;--muted:#94a1bb;--green:#34d399;--red:#fb7185;--yellow:#fbbf24;
|
||
--accent:#7c8cff;--accent2:#a878ff;--cyan:#38d2e6;
|
||
--shadow:0 18px 50px -20px rgba(0,0,0,.7);
|
||
}
|
||
*{box-sizing:border-box}
|
||
html{scroll-behavior:smooth}
|
||
body{margin:0;font-family:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
||
background:var(--bg);color:var(--text);line-height:1.5;-webkit-font-smoothing:antialiased;
|
||
min-height:100vh;overflow-x:hidden}
|
||
h1,h2,h3,.brand-name{font-family:'Space Grotesk','Inter',sans-serif}
|
||
|
||
/* ---------- layered background imagery ---------- */
|
||
.bg{position:fixed;inset:0;z-index:-2;overflow:hidden;
|
||
background:
|
||
radial-gradient(1200px 700px at 80% -10%, rgba(124,140,255,.18), transparent 60%),
|
||
radial-gradient(900px 600px at 5% 10%, rgba(168,120,255,.16), transparent 55%),
|
||
radial-gradient(1000px 800px at 50% 110%, rgba(56,210,230,.12), transparent 60%),
|
||
linear-gradient(180deg,#070b14,#0a1020 40%,#070b14)}
|
||
.bg::before{content:"";position:absolute;inset:0;opacity:.5;
|
||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='44'%3E%3Cpath d='M44 0H0V44' fill='none' stroke='%23ffffff' stroke-opacity='0.035' stroke-width='1'/%3E%3C/svg%3E")}
|
||
.bg .orb{position:absolute;border-radius:50%;filter:blur(70px);opacity:.55;animation:float 18s ease-in-out infinite}
|
||
.orb.a{width:480px;height:480px;left:-120px;top:-80px;background:radial-gradient(circle,#5b6cff,transparent 70%)}
|
||
.orb.b{width:520px;height:520px;right:-140px;top:140px;background:radial-gradient(circle,#a878ff,transparent 70%);animation-delay:-6s}
|
||
.orb.c{width:440px;height:440px;left:35%;bottom:-180px;background:radial-gradient(circle,#21b6c9,transparent 70%);animation-delay:-12s}
|
||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(40px,-30px)}}
|
||
.bg .chartline{position:absolute;left:0;right:0;bottom:0;width:100%;height:240px;opacity:.5}
|
||
|
||
/* ---------- app shell ---------- */
|
||
.app{max-width:1240px;margin:0 auto;padding:0 22px 60px}
|
||
.topnav{position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:18px;flex-wrap:wrap;
|
||
padding:14px 18px;margin:14px 0 8px;border:1px solid var(--border);border-radius:18px;
|
||
background:rgba(10,15,28,.72);backdrop-filter:blur(16px);box-shadow:var(--shadow)}
|
||
.brand{display:flex;align-items:center;gap:11px;min-width:0}
|
||
.logo{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;font-size:20px;
|
||
background:linear-gradient(135deg,var(--accent),var(--accent2));box-shadow:0 6px 20px -6px var(--accent)}
|
||
.brand-name{font-size:18px;font-weight:700;letter-spacing:-.2px;line-height:1}
|
||
.brand-sub{font-size:11px;color:var(--muted);margin-top:2px}
|
||
.build-badge{display:inline-flex;align-items:center;margin-top:5px;padding:3px 8px;border-radius:999px;border:1px solid rgba(124,140,255,.35);background:rgba(124,140,255,.12);color:#cbd5ff;font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase}
|
||
.tabs{display:flex;gap:4px;background:rgba(255,255,255,.04);padding:4px;border-radius:12px;border:1px solid var(--border);max-width:100%;overflow-x:auto}
|
||
.tab{appearance:none;border:0;background:transparent;color:var(--muted);font-family:inherit;
|
||
font-size:13.5px;font-weight:600;padding:8px 14px;border-radius:9px;cursor:pointer;transition:.18s;white-space:nowrap}
|
||
.tab:hover{color:var(--text)}
|
||
.tab.active{color:#fff;background:linear-gradient(135deg,var(--accent),var(--accent2));box-shadow:0 6px 18px -8px var(--accent)}
|
||
.nav-right{margin-left:auto;display:flex;align-items:center;gap:10px;min-width:0}
|
||
.status{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);
|
||
padding:6px 11px;border-radius:9px;background:rgba(255,255,255,.04);border:1px solid var(--border);min-width:0}
|
||
#statusText{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:220px}
|
||
.dot{width:8px;height:8px;border-radius:50%;background:var(--muted)}
|
||
.dot.live{background:var(--green);animation:pulse 1.6s infinite}
|
||
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(52,211,153,.5)}70%{box-shadow:0 0 0 7px rgba(52,211,153,0)}100%{box-shadow:0 0 0 0 rgba(52,211,153,0)}}
|
||
.btn{border:1px solid var(--border-strong);border-radius:10px;padding:9px 15px;font-size:13.5px;
|
||
font-weight:600;font-family:inherit;cursor:pointer;background:rgba(255,255,255,.05);color:var(--text);transition:.16s}
|
||
.btn:hover{border-color:var(--accent);transform:translateY(-1px)}
|
||
.btn.primary{background:linear-gradient(135deg,var(--accent),var(--accent2));border-color:transparent;color:#fff;box-shadow:0 8px 22px -10px var(--accent)}
|
||
.btn.primary:hover{filter:brightness(1.08)}
|
||
.btn:disabled{opacity:.55;cursor:wait;transform:none}
|
||
.btn.ghost{background:transparent}
|
||
body:not(.personal-mode) [data-tab="live"]{display:none!important}
|
||
.personal-mode [data-tab="invest"]{display:none!important}
|
||
.personal-banner{display:none;margin:0 0 14px;border:1px solid rgba(251,191,36,.22);border-radius:14px;
|
||
padding:12px 14px;background:rgba(251,191,36,.08);color:#f8e7b0;font-size:13px}
|
||
.personal-mode .personal-banner{display:block}
|
||
.personal-banner b{color:#fff}
|
||
.live-build-banner{display:block;margin:0 0 14px;padding:11px 14px;border-radius:14px;border:1px solid rgba(124,140,255,.30);background:linear-gradient(135deg,rgba(124,140,255,.14),rgba(56,210,230,.08));color:#dbeafe;font-size:12.5px}
|
||
.live-build-banner b{color:#fff}
|
||
|
||
/* ---------- hero ---------- */
|
||
.hero{position:relative;border:1px solid var(--border);border-radius:22px;padding:34px 32px;margin:14px 0 22px;
|
||
overflow:hidden;background:linear-gradient(120deg,rgba(124,140,255,.16),rgba(168,120,255,.08) 50%,rgba(56,210,230,.10));box-shadow:var(--shadow)}
|
||
.hero-art{position:absolute;right:-10px;top:-10px;bottom:-10px;width:46%;opacity:.85;pointer-events:none}
|
||
.hero h1{margin:0 0 10px;font-size:34px;letter-spacing:-.8px;line-height:1.08;max-width:660px}
|
||
.hero h1 .grad{background:linear-gradient(100deg,#a9b6ff,#d9b4ff 50%,#8fe8f3);-webkit-background-clip:text;background-clip:text;color:transparent}
|
||
.hero p{margin:0;color:#c3cce0;font-size:15.5px;max-width:580px}
|
||
.hero .badges{display:flex;gap:9px;flex-wrap:wrap;margin-top:18px}
|
||
.hbadge{font-size:12px;font-weight:600;color:#dfe6f5;padding:6px 12px;border-radius:999px;background:rgba(255,255,255,.07);border:1px solid var(--border)}
|
||
|
||
/* ---------- panels & cards ---------- */
|
||
.tabpanel{display:none;animation:fade .35s ease}
|
||
.tabpanel.active{display:block}
|
||
@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
.grid2{display:grid;grid-template-columns:1.05fr .95fr;gap:20px}
|
||
@media (max-width:920px){.grid2{grid-template-columns:1fr}}
|
||
.card{background:var(--panel);border:1px solid var(--border);border-radius:18px;padding:20px 22px;
|
||
margin-bottom:20px;backdrop-filter:blur(14px);box-shadow:var(--shadow)}
|
||
.card.hoverable{transition:.2s}
|
||
.card.hoverable:hover{border-color:var(--border-strong);transform:translateY(-2px)}
|
||
.card-h{display:flex;align-items:center;justify-content:space-between;margin:0 0 16px}
|
||
.card-h h3{margin:0;font-size:13px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;font-weight:600}
|
||
.section-title{display:flex;align-items:center;gap:11px;font-size:20px;margin:6px 0 16px;font-weight:700}
|
||
.section-title::before{content:"";width:5px;height:22px;border-radius:3px;background:linear-gradient(var(--accent),var(--accent2))}
|
||
.muted{color:var(--muted)}.small{font-size:12.5px}
|
||
|
||
/* ---------- stat cards ---------- */
|
||
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:14px;margin-bottom:20px}
|
||
.stat{position:relative;background:var(--panel);border:1px solid var(--border);border-radius:16px;padding:18px;overflow:hidden;backdrop-filter:blur(14px);transition:.2s}
|
||
.stat:hover{transform:translateY(-2px);border-color:var(--border-strong)}
|
||
.stat::after{content:"";position:absolute;left:0;top:0;height:3px;width:100%;background:linear-gradient(90deg,var(--accent),var(--accent2))}
|
||
.stat .ic{font-size:18px;opacity:.9}
|
||
.stat .label{color:var(--muted);font-size:11.5px;text-transform:uppercase;letter-spacing:.6px;margin-top:8px}
|
||
.stat .value{font-size:25px;font-weight:700;margin-top:4px;font-family:'Space Grotesk',sans-serif;letter-spacing:-.5px}
|
||
.pos-val{color:var(--green)}.neg-val{color:var(--red)}
|
||
|
||
/* ---------- charts ---------- */
|
||
.chart-wrap{width:min(100%,1120px);margin:10px auto 0}
|
||
#chart,#chart2{width:100%;height:auto;display:block;overflow:visible}
|
||
.legend{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:8px 12px;margin-top:14px}
|
||
.leg{display:flex;align-items:center;gap:8px;font-size:12px;color:#cdd6e8;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);border-radius:8px;padding:7px 9px;min-width:0}
|
||
.leg b{margin-left:auto;font-family:'Space Grotesk',sans-serif}
|
||
.legdot{width:9px;height:9px;border-radius:50%;display:inline-block;box-shadow:0 0 0 3px rgba(255,255,255,.06)}
|
||
.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}
|
||
.lb-card{position:relative;background:var(--panel);border:1px solid var(--border);border-radius:18px;padding:18px;
|
||
backdrop-filter:blur(14px);cursor:pointer;transition:.2s;overflow:hidden}
|
||
.lb-card:hover{transform:translateY(-3px);border-color:var(--border-strong);box-shadow:var(--shadow)}
|
||
.lb-top{display:flex;align-items:center;gap:10px;margin-bottom:14px}
|
||
.medal{font-size:20px;min-width:26px}
|
||
.lb-emoji{font-size:24px}
|
||
.lb-name{font-weight:700;font-size:15px;font-family:'Space Grotesk'}
|
||
.lb-blurb{font-size:11.5px;color:var(--muted);line-height:1.4;margin-top:2px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
|
||
.lb-mid{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px}
|
||
.lb-eq{font-size:26px;font-weight:700;font-family:'Space Grotesk';letter-spacing:-.5px}
|
||
.lb-ret{font-size:15px;font-weight:700;font-family:'Space Grotesk'}
|
||
.lb-foot{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
||
.rankpill{position:absolute;top:14px;right:14px;font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;background:rgba(255,255,255,.07);border:1px solid var(--border);color:var(--muted)}
|
||
|
||
/* ---------- segmented agent selector ---------- */
|
||
.seg{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:18px}
|
||
.segbtn{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:600;padding:8px 13px;border-radius:11px;cursor:pointer;
|
||
background:rgba(255,255,255,.05);border:1px solid var(--border);color:var(--muted);transition:.16s;user-select:none}
|
||
.segbtn:hover{color:var(--text);border-color:var(--border-strong)}
|
||
.segbtn.active{color:#fff;border-color:transparent}
|
||
|
||
.agent-brief{display:grid;grid-template-columns:1.2fr repeat(5,minmax(105px,.5fr));gap:14px;margin-bottom:18px}
|
||
.brief-main{background:var(--panel);border:1px solid var(--border);border-radius:16px;padding:18px;backdrop-filter:blur(14px)}
|
||
.brief-title{display:flex;align-items:center;gap:10px;font-family:'Space Grotesk';font-weight:700;font-size:18px;margin-bottom:7px}
|
||
.brief-main p{margin:0;color:#cdd6e8;font-size:13.5px}
|
||
.brief-mini{background:rgba(255,255,255,.04);border:1px solid var(--border);border-radius:14px;padding:14px}
|
||
.brief-mini .k{font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted)}
|
||
.brief-mini .v{margin-top:5px;font-size:14px;font-weight:700}
|
||
.card-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
||
.select{appearance:none;border:1px solid var(--border);border-radius:9px;background:rgba(255,255,255,.05);color:var(--text);
|
||
font:600 12.5px 'Inter',sans-serif;padding:7px 28px 7px 10px;cursor:pointer}
|
||
.select:focus{outline:1px solid var(--accent)}
|
||
.loading-empty{display:grid;gap:10px;place-items:center;padding:28px 12px;text-align:center}
|
||
.loading-ring{width:34px;height:34px;border-radius:50%;border:3px solid rgba(255,255,255,.12);border-top-color:var(--accent);animation:spin .9s linear infinite}
|
||
.empty-copy{max-width:420px;color:var(--muted);font-size:13.5px}
|
||
@keyframes spin{to{transform:rotate(360deg)}}
|
||
.report-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(255px,1fr));gap:14px}
|
||
.report{background:rgba(255,255,255,.04);border:1px solid var(--border);border-radius:15px;padding:16px;border-top:3px solid var(--accent)}
|
||
.report-title{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px}
|
||
.report-name{font-family:'Space Grotesk';font-weight:700}
|
||
.report-rank{font-size:11px;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:3px 8px;white-space:nowrap}
|
||
.report p{margin:0 0 9px;color:#cdd6e8;font-size:13px;line-height:1.5}
|
||
.report p:last-child{margin-bottom:0}
|
||
.report b{color:var(--text)}
|
||
.agent-chat{display:grid;grid-template-columns:.8fr 1.2fr;gap:16px}
|
||
.chat-panel{background:rgba(255,255,255,.035);border:1px solid var(--border);border-radius:15px;padding:14px}
|
||
.chat-persona{display:flex;align-items:center;gap:10px;margin-bottom:10px}
|
||
.chat-avatar{font-size:28px;width:42px;height:42px;display:grid;place-items:center;border-radius:13px;background:rgba(255,255,255,.06);border:1px solid var(--border)}
|
||
.chat-name{font-family:'Space Grotesk';font-weight:700}
|
||
.chat-meta{font-size:12px;color:var(--muted);margin-top:2px}
|
||
.chat-prompts{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
|
||
.chat-prompt{border:1px solid var(--border);background:rgba(255,255,255,.045);color:var(--text);border-radius:999px;padding:7px 10px;font:700 11.5px 'Inter';cursor:pointer}
|
||
.chat-prompt:hover{border-color:var(--accent)}
|
||
.chat-window{display:flex;flex-direction:column;gap:10px;max-height:420px;overflow:auto;padding-right:4px}
|
||
.chat-msg{border:1px solid var(--border);border-radius:14px;padding:11px 13px;font-size:13px;line-height:1.45;white-space:pre-wrap}
|
||
.chat-msg.user{align-self:flex-end;max-width:82%;background:rgba(124,140,255,.16);border-color:rgba(124,140,255,.35)}
|
||
.chat-msg.agent{align-self:flex-start;max-width:92%;background:rgba(255,255,255,.045)}
|
||
.chat-msg.pending{color:var(--muted);font-style:italic}
|
||
.chat-input-row{display:flex;gap:8px;margin-top:12px}
|
||
.chat-input-row .input{flex:1}
|
||
@media (max-width:760px){.agent-chat{grid-template-columns:1fr}.chat-msg.user,.chat-msg.agent{max-width:100%}}
|
||
|
||
/* ---------- suggestions ---------- */
|
||
.filterbar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px}
|
||
.chip{font-size:13px;font-weight:600;padding:7px 14px;border-radius:999px;cursor:pointer;background:rgba(255,255,255,.05);border:1px solid var(--border);color:var(--muted);transition:.16s;user-select:none}
|
||
.chip:hover{border-color:var(--accent);color:var(--text)}
|
||
.chip.active{background:linear-gradient(135deg,var(--accent),var(--accent2));border-color:transparent;color:#fff}
|
||
.chip .ct{opacity:.75;font-size:11px;margin-left:5px}
|
||
.sug-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
||
@media (max-width:760px){.sug-grid{grid-template-columns:1fr}}
|
||
.sug{position:relative;background:var(--panel);border:1px solid var(--border);border-radius:16px;padding:18px;border-left:4px solid var(--accent);backdrop-filter:blur(14px);transition:.2s}
|
||
.sug:hover{transform:translateY(-3px);border-color:var(--border-strong);box-shadow:var(--shadow)}
|
||
.sug .q{font-weight:600;font-size:15px;line-height:1.35;margin-top:6px}
|
||
.sug .event{color:var(--muted);font-size:12px;margin:3px 0 12px}
|
||
.sug-row{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:9px}
|
||
.cat-badge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:3px 9px;border-radius:7px}
|
||
.pill{font-size:12px;font-weight:700;padding:4px 11px;border-radius:999px}
|
||
.pill.YES{background:rgba(52,211,153,.16);color:var(--green)}
|
||
.pill.NO{background:rgba(251,113,133,.16);color:var(--red)}
|
||
.conv-bar{flex:1;min-width:90px;height:7px;background:rgba(255,255,255,.07);border-radius:999px;overflow:hidden}
|
||
.conv-bar>span{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--green))}
|
||
.rationale{font-size:13px;color:#cdd6e8;margin:10px 0}
|
||
.drivers{display:flex;gap:6px;flex-wrap:wrap}
|
||
.driver{font-size:11px;background:rgba(255,255,255,.05);border:1px solid var(--border);padding:3px 9px;border-radius:7px;color:var(--muted)}
|
||
.metrics{display:flex;gap:16px;font-size:12px;color:var(--muted);margin-top:12px;flex-wrap:wrap}
|
||
.metrics b{color:var(--text)}
|
||
a.market-link{color:var(--accent);text-decoration:none;font-size:12px;font-weight:600}
|
||
a.market-link:hover{text-decoration:underline}
|
||
a.market-title{color:var(--text);text-decoration:none}
|
||
a.market-title:hover{color:#fff;text-decoration:underline;text-decoration-color:var(--accent)}
|
||
|
||
/* ---------- positions / log ---------- */
|
||
.pos{display:flex;justify-content:space-between;align-items:center;padding:13px 0;border-bottom:1px solid var(--border)}
|
||
.pos:last-child{border-bottom:none}
|
||
.pos .pq{font-size:13.5px;max-width:62%;font-weight:500}
|
||
.pos .sub{color:var(--muted);font-size:11.5px;margin-top:3px}
|
||
.right{text-align:right}
|
||
.log-item{font-size:12.5px;padding:9px 0;border-bottom:1px solid var(--border);color:#cdd6e8}
|
||
.log-item:last-child{border-bottom:none}
|
||
.badge{font-weight:700;font-size:10px;padding:2px 7px;border-radius:5px;margin-right:7px}
|
||
.badge.OPEN{background:rgba(124,140,255,.2);color:var(--accent)}
|
||
.badge.CLOSE{background:rgba(251,191,36,.2);color:var(--yellow)}
|
||
.badge.STOP{background:rgba(251,113,133,.2);color:var(--red)}
|
||
.badge.GAIN{background:rgba(52,211,153,.18);color:var(--green)}
|
||
.badge.EXIT{background:rgba(251,191,36,.18);color:var(--yellow)}
|
||
.badge.RISK{background:rgba(56,210,230,.16);color:var(--cyan)}
|
||
.log-date{color:var(--muted)}
|
||
.empty{color:var(--muted);font-size:13.5px;padding:14px 0;text-align:center}
|
||
|
||
@media (max-width:700px){
|
||
.app{padding:0 10px 42px;max-width:100%;overflow:hidden}
|
||
.topnav{display:grid;grid-template-columns:1fr;gap:12px;padding:14px;margin:10px 0 8px;border-radius:14px}
|
||
.brand-sub{white-space:normal}
|
||
.tabs{width:100%;padding-bottom:5px}
|
||
.tab{padding:8px 12px}
|
||
.nav-right{width:100%;margin-left:0;display:flex;align-items:stretch;flex-wrap:wrap;gap:8px}
|
||
.nav-right .status{flex:1 1 100%}
|
||
#statusText{max-width:none;white-space:normal}
|
||
.status{align-items:flex-start}
|
||
.btn{padding:9px 11px}
|
||
.section-title{font-size:19px}
|
||
.stats{grid-template-columns:1fr;gap:12px}
|
||
.chart-wrap{width:100%;margin-top:8px}
|
||
.legend{grid-template-columns:1fr}
|
||
.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%}
|
||
.paper-grid{grid-template-columns:1fr}
|
||
.ta-grid{grid-template-columns:1fr}
|
||
.money-row{grid-template-columns:1fr}
|
||
.pos{align-items:flex-start;gap:12px}
|
||
.pos .pq{max-width:none}
|
||
.right{min-width:92px}
|
||
}
|
||
|
||
/* ---------- about ---------- */
|
||
.about-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:16px;margin-bottom:8px}
|
||
.feature{background:var(--panel);border:1px solid var(--border);border-radius:16px;padding:20px;backdrop-filter:blur(14px)}
|
||
.feature .fic{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;font-size:20px;margin-bottom:12px;background:linear-gradient(135deg,rgba(124,140,255,.25),rgba(168,120,255,.18));border:1px solid var(--border)}
|
||
.feature h4{margin:0 0 7px;font-size:15px}
|
||
.feature p{margin:0;font-size:13px;color:var(--muted);line-height:1.55}
|
||
.steps{counter-reset:s;list-style:none;padding:0;margin:0}
|
||
.steps li{position:relative;padding:0 0 16px 44px;font-size:14px;color:#cdd6e8}
|
||
.steps li::before{counter-increment:s;content:counter(s);position:absolute;left:0;top:-2px;width:30px;height:30px;border-radius:9px;display:grid;place-items:center;font-weight:700;font-size:13px;color:#fff;background:linear-gradient(135deg,var(--accent),var(--accent2))}
|
||
.disclaimer{border:1px solid rgba(251,191,36,.3);background:rgba(251,191,36,.07);border-radius:14px;padding:16px 18px;font-size:13px;color:#e6dcc2;margin-top:8px}
|
||
.invest-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:18px}
|
||
.invest-agent{display:grid;grid-template-columns:auto 1fr auto;gap:13px;align-items:center;padding:14px 0;border-bottom:1px solid var(--border)}
|
||
.invest-agent:last-child{border-bottom:none}
|
||
.invest-agent input{width:112px;border:1px solid var(--border);border-radius:9px;background:rgba(255,255,255,.05);color:var(--text);font:700 13px 'Inter',sans-serif;padding:8px 10px}
|
||
.invest-agent input:focus{outline:1px solid var(--accent)}
|
||
.invest-name{font-family:'Space Grotesk';font-weight:700}
|
||
.invest-copy{color:var(--muted);font-size:12.5px;margin-top:2px}
|
||
.invest-total{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:14px}
|
||
.invest-num{background:rgba(255,255,255,.04);border:1px solid var(--border);border-radius:14px;padding:14px}
|
||
.invest-num .k{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px}
|
||
.invest-num .v{font-family:'Space Grotesk';font-size:24px;font-weight:700;margin-top:3px}
|
||
.checklist{list-style:none;margin:0;padding:0;display:grid;gap:10px}
|
||
.checklist li{display:flex;gap:9px;color:#cdd6e8;font-size:13.5px}
|
||
.checklist li::before{content:"";width:9px;height:9px;border-radius:50%;margin-top:6px;background:var(--accent);box-shadow:0 0 0 4px rgba(124,140,255,.12);flex:0 0 auto}
|
||
.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}
|
||
.ta-grid{display:grid;grid-template-columns:1fr .6fr;gap:16px}
|
||
.ta-chart{width:100%;height:auto;display:block;overflow:visible}
|
||
.money-row{display:grid;grid-template-columns:1fr 1fr auto auto;gap:8px;margin-top:12px}
|
||
.leader-row{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--border)}
|
||
.leader-row:last-child{border-bottom:none}
|
||
.locked-panel{border:1px solid rgba(124,140,255,.28);background:rgba(124,140,255,.08);border-radius:14px;padding:14px;margin:12px 0}
|
||
|
||
/* ---------- 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)}
|
||
.toast.show{opacity:1;transform:translateY(0)}
|
||
footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color:var(--muted);font-size:12.5px;text-align:center}
|
||
::-webkit-scrollbar{width:11px;height:11px}
|
||
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12);border-radius:6px;border:3px solid transparent;background-clip:padding-box}
|
||
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.2);background-clip:padding-box}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="bg" aria-hidden="true">
|
||
<div class="orb a"></div><div class="orb b"></div><div class="orb c"></div>
|
||
<svg class="chartline" viewBox="0 0 1440 240" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||
<defs><linearGradient id="cl" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#7c8cff" stop-opacity="0.28"/><stop offset="100%" stop-color="#7c8cff" stop-opacity="0"/>
|
||
</linearGradient></defs>
|
||
<path d="M0,170 C120,150 180,110 300,120 C420,130 470,70 600,90 C720,108 800,40 940,70 C1080,100 1160,150 1280,120 C1360,100 1410,130 1440,120 L1440,240 L0,240 Z" fill="url(#cl)"/>
|
||
<path d="M0,170 C120,150 180,110 300,120 C420,130 470,70 600,90 C720,108 800,40 940,70 C1080,100 1160,150 1280,120 C1360,100 1410,130 1440,120" fill="none" stroke="#7c8cff" stroke-opacity="0.5" stroke-width="2"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="app">
|
||
<nav class="topnav">
|
||
<div class="brand">
|
||
<div class="logo">🏆</div>
|
||
<div><div class="brand-name">Polymarket Arena</div><div class="brand-sub">10 agents · 5 core + 5 aggressive</div><div class="build-badge">Adaptive strategy 62 · contest-level learning · build 106</div></div>
|
||
</div>
|
||
<div class="tabs" id="tabs">
|
||
<button class="tab" data-tab="overview">Overview</button>
|
||
<button class="tab" data-tab="leaderboard">Leaderboard</button>
|
||
<button class="tab" data-tab="suggestions">Suggestions</button>
|
||
<button class="tab" data-tab="portfolio">Portfolios</button>
|
||
<button class="tab" data-tab="invest">Invest</button>
|
||
<button class="tab" data-tab="paper">Paper Trade</button>
|
||
<button class="tab" data-tab="live">Live Money</button>
|
||
<button class="tab" data-tab="about">About</button>
|
||
</div>
|
||
<div class="nav-right">
|
||
<span class="status"><span class="dot" id="statusDot"></span><span id="statusText">idle</span></span>
|
||
<button id="runBtn" class="btn primary">Run cycle</button>
|
||
<button id="copyStateBtn" class="btn ghost">Copy Sync</button>
|
||
<button id="importStateBtn" class="btn ghost">Import Sync</button>
|
||
</div>
|
||
</nav>
|
||
<div class="personal-banner" id="personalBanner">
|
||
<b>Personal research mode.</b> This copy is for your own analysis, paper tracking, and manual trade research only. It does not pool money, onboard investors, custody funds, bypass eligibility rules, or place orders without your manual approval.
|
||
</div>
|
||
<div class="live-build-banner"><b>Build 106 active:</b> The public paper agents run autonomously every five minutes while every user device is closed. Each cycle scans the 500 most-active eligible markets and 1,000 most-active events, carries exact Gamma fee schedules through cloud and offline caches, and checks complete bundles against live depth. Strategy 62 adds a bounded sports-contest NO exploration lane: one highest-priced NO contract per real contest near the three-day checkpoint, exact entry fee plus slippage, 0.5% initial size, 3% total cap, and automatic forward promotion or suspension. This point-positive historical pattern is not a proven edge. Phones and computers display the same read-only autonomous state. Profits are not guaranteed.</div>
|
||
|
||
<!-- ============ OVERVIEW ============ -->
|
||
<section class="tabpanel" data-tab="overview">
|
||
<div class="hero">
|
||
<svg class="hero-art" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||
<defs><linearGradient id="ha" x1="0" y1="1" x2="1" y2="0"><stop offset="0" stop-color="#7c8cff" stop-opacity=".0"/><stop offset="1" stop-color="#a878ff" stop-opacity=".5"/></linearGradient></defs>
|
||
<g fill="none" stroke-width="2.5">
|
||
<path d="M20,250 L120,160 L200,200 L290,80 L380,40" stroke="#34d399"/>
|
||
<path d="M20,255 L120,210 L200,150 L290,170 L380,120" stroke="#fbbf24" opacity=".8"/>
|
||
<path d="M20,248 L120,230 L200,235 L290,210 L380,200" stroke="#fb7185" opacity=".7"/>
|
||
</g>
|
||
<g fill="#d9b4ff"><circle cx="380" cy="40" r="6"/></g>
|
||
</svg>
|
||
<h1>Ten AI agents race to <span class="grad">beat the market</span></h1>
|
||
<p>Each cycle loads the 500 most active eligible Yes/No Polymarket markets, then requires direction to agree across independent time windows before liquidity, evidence, timing, and friction checks can make an idea trade-ready.</p>
|
||
<div class="badges">
|
||
<span class="hbadge">🏆 10 competing agents</span>
|
||
<span class="hbadge">5 aggressive strategies</span>
|
||
<span class="hbadge">Top 500 eligible Yes/No markets</span>
|
||
<span class="hbadge">Ranked by activity + confirmation</span>
|
||
<span class="hbadge">Live Polymarket data</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stats" id="stats"></div>
|
||
|
||
<div class="grid2">
|
||
<div class="card hoverable">
|
||
<div class="card-h"><h3>Agent Returns</h3><div><span class="small muted" id="lastRun"></span><div class="rangebar" data-chart-ranges></div></div></div>
|
||
<div class="chart-wrap"><svg id="chart" viewBox="0 0 960 320" preserveAspectRatio="xMidYMid meet"></svg></div>
|
||
<div class="legend" id="comboLegend"></div>
|
||
<div class="small muted" style="margin-top:10px">Use the range buttons to zoom from one week to all history. The dashed line averages all ten agents, including the five aggressive agents from their later launch date; Core Avg is the comparable series for the original five.</div>
|
||
</div>
|
||
<div class="card hoverable">
|
||
<div class="card-h"><h3>Top Picks Today</h3><span class="small muted">highest conviction</span></div>
|
||
<div id="topPicks"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-h"><h3>Trade Email Alerts</h3><span class="small muted" id="emailAlertMeta">off</span></div>
|
||
<div class="account-row">
|
||
<input class="input" id="emailAlertInput" type="email" placeholder="you@example.com">
|
||
<button class="btn" id="saveEmailAlertsBtn">Save alerts</button>
|
||
<button class="btn ghost" id="testEmailAlertsBtn">Send test</button>
|
||
</div>
|
||
<div class="chiprow" id="emailAlertEvents" style="margin-top:10px"></div>
|
||
<div class="small muted" style="margin-top:12px">Agents to include</div>
|
||
<div class="chiprow" id="emailAlertAgents" style="margin-top:8px"></div>
|
||
<div class="small muted" id="emailAlertStatus" style="margin-top:8px">Send a digest whenever agents open, close, stop out, or take gains during a cycle.</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ LEADERBOARD ============ -->
|
||
<section class="tabpanel" data-tab="leaderboard">
|
||
<div class="section-title">🏆 Leaderboard <span class="small muted" id="lbSub" style="font-weight:400"></span></div>
|
||
<div class="lb-grid" id="leaderboard"></div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Returns — all agents</h3><div><span class="small muted">$10,000 start each</span><div class="rangebar" data-chart-ranges></div></div></div>
|
||
<div class="chart-wrap"><svg id="chart2" viewBox="0 0 960 320" preserveAspectRatio="xMidYMid meet"></svg></div>
|
||
<div class="legend" id="comboLegend2"></div>
|
||
<div class="small muted" style="margin-top:10px">Leaderboard rank uses current adaptive-strategy return. The chart and legacy/replay figure preserve older history for context; adaptive return continues across builds until the trading strategy itself changes.</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ SUGGESTIONS ============ -->
|
||
<section class="tabpanel" data-tab="suggestions">
|
||
<div class="section-title">💡 Live Suggestions <span class="small muted" id="sugDate" style="font-weight:400"></span></div>
|
||
<div class="card">
|
||
<div class="filterbar" id="filterbar"></div>
|
||
<div class="small muted" id="focusNote" style="margin-top:6px"></div>
|
||
</div>
|
||
<div class="sug-grid" id="suggestions"></div>
|
||
</section>
|
||
|
||
<!-- ============ PORTFOLIOS ============ -->
|
||
<section class="tabpanel" data-tab="portfolio">
|
||
<div class="section-title">🤖 Agent Portfolios</div>
|
||
<div class="seg" id="agentSel"></div>
|
||
<div class="agent-brief" id="agentBrief"></div>
|
||
<div class="stats" id="statsPf"></div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Chat With This Agent</h3><span class="small muted">ask about performance, risk, trades, or plans</span></div>
|
||
<div id="agentChatRoot"></div>
|
||
</div>
|
||
<div class="grid2">
|
||
<div class="card"><div class="card-h"><h3>Open Positions</h3><div class="card-tools"><select class="select" id="positionSort" aria-label="Sort open positions">
|
||
<option value="pnl_desc">Best P&L</option>
|
||
<option value="pnl_asc">Worst P&L</option>
|
||
<option value="value_desc">Largest Value</option>
|
||
<option value="newest">Newest</option>
|
||
<option value="category">Category</option>
|
||
</select></div></div><div id="positions" aria-live="polite"></div></div>
|
||
<div class="card"><div class="card-h"><h3>Activity Log</h3></div><div id="history"></div></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Technical Analysis</h3><span class="small muted">candles, moving averages, MACD, activity</span></div>
|
||
<div class="ta-grid">
|
||
<div><svg id="agentTAChart" class="ta-chart" viewBox="0 0 960 420" preserveAspectRatio="xMidYMid meet"></svg></div>
|
||
<div id="agentTAReadout"></div>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Daily Agent Performance Reports</h3><span class="small muted" id="reportDate"></span></div>
|
||
<div class="report-grid" id="agentReports"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ INVEST ============ -->
|
||
<section class="tabpanel" data-tab="invest">
|
||
<div class="section-title">💸 Invest in Agents</div>
|
||
<div class="invest-grid">
|
||
<div class="card">
|
||
<div class="card-h"><h3>Allocation draft</h3><span class="small muted">planning only</span></div>
|
||
<div class="invest-total" id="investTotals"></div>
|
||
<div id="investAgents"></div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>How real money should work</h3></div>
|
||
<ol class="steps">
|
||
<li><b>User owns funds</b> — connect a wallet or regulated brokerage account; the site should not custody deposits directly.</li>
|
||
<li><b>Consent per agent</b> — users choose which agents can trade and the maximum amount each may control.</li>
|
||
<li><b>Execution layer</b> — agent signals become signed user orders through Polymarket's trading flow or a compliant partner.</li>
|
||
<li><b>Risk controls</b> — stop-loss, gain-stop, exposure caps, market exclusions, and emergency pause stay visible before any order is sent.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Compliance checklist</h3></div>
|
||
<ul class="checklist">
|
||
<li>Eligibility, location, age, KYC, and sanctions screening before real trading.</li>
|
||
<li>Clear disclosure that prediction markets are risky and users can lose the full amount committed.</li>
|
||
<li>No pooled customer funds unless handled by a properly regulated structure.</li>
|
||
<li>Audit logs for every signal, user approval, order, fill, stop, and withdrawal.</li>
|
||
</ul>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Execution status</h3><span class="small muted">not enabled</span></div>
|
||
<p class="muted" style="margin-top:0">Real orders require user-signed trading credentials and an eligible account. The current site only creates an allocation draft.</p>
|
||
<div class="card-tools">
|
||
<a class="btn ghost" href="https://docs.polymarket.com/trading/overview" target="_blank" rel="noopener">Trading docs</a>
|
||
<a class="btn ghost" href="https://polymarket.com/terms-of-use" target="_blank" rel="noopener">Terms</a>
|
||
<button class="btn primary" disabled>Wallet trading locked</button>
|
||
</div>
|
||
</div>
|
||
<div class="risk-note">
|
||
<b>Important:</b> this site still does not move money or place orders. This tab is the bridge design: it lets people plan allocations and shows the safe path toward wallet-based or regulated real-money execution.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card" style="margin-top:18px">
|
||
<div class="card-h"><h3>Exact trades agents are holding</h3><span class="small muted">side, size, price, link</span></div>
|
||
<div class="exact-table" id="exactTrades"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ PAPER TRADING ============ -->
|
||
<section class="tabpanel" data-tab="paper">
|
||
<div class="section-title">🧾 Paper Trading Accounts</div>
|
||
<div class="paper-grid">
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Account</h3><span class="small muted">cloud paper money when password-backed</span></div>
|
||
<div class="account-row">
|
||
<select class="select" id="paperAccountSelect" aria-label="Paper account"></select>
|
||
<input class="input" id="paperAccountName" placeholder="New account name" />
|
||
<input class="input" id="paperAccountPassword" type="password" placeholder="Password optional" />
|
||
<button class="btn primary" id="createPaperAccountBtn">Create</button>
|
||
<button class="btn" id="paperLoginBtn">Log in</button>
|
||
<button class="btn ghost" id="paperSignOutBtn">Sign out</button>
|
||
</div>
|
||
<div id="paperLockPanel"></div>
|
||
<div class="stats" id="paperStats"></div>
|
||
<div class="money-row">
|
||
<input class="input" id="paperMoneyAmount" type="number" min="1" step="25" placeholder="Amount" />
|
||
<input class="input" id="paperPasswordInput" type="password" placeholder="Password" />
|
||
<button class="btn" id="paperDepositBtn">Deposit</button>
|
||
<button class="btn ghost" id="paperWithdrawBtn">Withdraw</button>
|
||
</div>
|
||
<div class="card-tools">
|
||
<select class="select" id="copyAgentSelect" aria-label="Agent to copy"></select>
|
||
<button class="btn" id="copyAgentBtn">Copy agent exactly</button>
|
||
</div>
|
||
<div class="card-tools" style="margin-top:10px">
|
||
<select class="select" id="buyAgentSelect" aria-label="Agent to buy"></select>
|
||
<input class="input" id="buyAgentAmount" type="number" min="1" step="25" placeholder="Amount" />
|
||
<button class="btn" id="buyAgentBtn">Buy agent shares</button>
|
||
</div>
|
||
<p class="small muted" style="margin:12px 0 0">Deposits and withdrawals are paper-cash controls. Accounts created with a password sync through the backend so the same portfolio can load on another device. Copying an agent replaces this paper account with that agent's current cash and open positions.</p>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Paper account leaderboard</h3><span class="small muted">best returns</span></div>
|
||
<div id="paperLeaderboard"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Portfolio value history</h3><span class="small muted">paper equity over time</span></div>
|
||
<svg id="paperPortfolioChart" class="ta-chart" viewBox="0 0 720 260" preserveAspectRatio="xMidYMid meet"></svg>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Paper positions</h3></div>
|
||
<div id="paperPositions"></div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Browse Polymarket</h3><span class="small muted">daily ideas or search all markets</span></div>
|
||
<div class="account-row">
|
||
<input class="input" id="paperSearchInput" placeholder="Search all Polymarket markets" />
|
||
<button class="btn" id="paperSearchBtn">Search</button>
|
||
<button class="btn" id="paperAllBtn">All active</button>
|
||
<button class="btn ghost" id="paperMoreBtn">Load more</button>
|
||
<button class="btn ghost" id="paperClearSearchBtn">Daily ideas</button>
|
||
</div>
|
||
<div class="filterbar" id="paperFilterbar"></div>
|
||
<div class="trade-list" id="paperMarketList"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Paper activity</h3></div>
|
||
<div id="paperHistory"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ PERSONAL TRADING COCKPIT ============ -->
|
||
<section class="tabpanel" data-tab="live">
|
||
<div class="section-title">🔐 Personal Trading Cockpit</div>
|
||
<div class="disclaimer" style="margin-bottom:16px"><b>Private use only.</b> This area is hidden from the public site and is for your own wallet, your own money, and manual approval only. It does not accept investor deposits, pool funds, sell agent access, or execute unattended trades.</div>
|
||
<div class="grid2">
|
||
<div class="card">
|
||
<div class="card-h"><h3>Personal safety checklist</h3><span class="small muted" id="liveReadyPct"></span></div>
|
||
<div id="liveChecklist"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Your wallet & limits</h3><span class="small muted">personal only</span></div>
|
||
<div class="account-row">
|
||
<input class="input" id="liveWalletAddress" placeholder="Your Polymarket deposit wallet address" />
|
||
<select class="select" id="liveJurisdiction">
|
||
<option value="">Jurisdiction</option><option>United States</option><option>Canada</option><option>European Union</option><option>Other</option>
|
||
</select>
|
||
</div>
|
||
<div class="money-row">
|
||
<input class="input" id="liveDepositLimit" type="number" min="0" step="100" placeholder="Max deposit" />
|
||
<input class="input" id="liveWithdrawReserve" type="number" min="0" step="100" placeholder="Withdrawal reserve" />
|
||
<button class="btn" id="saveLiveProfileBtn">Save profile</button>
|
||
<button class="btn primary" disabled>No public deposits</button>
|
||
</div>
|
||
<div class="risk-note" style="margin-top:12px">Fund only your own wallet directly through approved provider flows. This site will not accept, hold, or withdraw money for other people.</div>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Personal capital & agent shares</h3><span class="small muted">synced tracker, no custody</span></div>
|
||
<div id="personalCapitalSummary"></div>
|
||
<div class="account-row">
|
||
<input class="input" id="personalCapitalAmount" type="number" min="1" step="25" placeholder="Amount" />
|
||
<select class="select" id="personalCapitalAgent"></select>
|
||
<button class="btn" id="personalDepositBtn">Record external deposit</button>
|
||
<button class="btn ghost" id="personalWithdrawBtn">Record withdrawal</button>
|
||
<button class="btn primary" id="personalBuyAgentBtn">Allocate to agent</button>
|
||
<button class="btn ghost" id="personalSellAgentBtn">Reduce agent</button>
|
||
</div>
|
||
<div class="risk-note" style="margin-top:12px">This tracks money you personally control outside Poly Arena. Agent shares are bookkeeping allocations only, not securities, pooled deposits, or orders placed by the site.</div>
|
||
<div id="personalAgentAllocations" style="margin-top:12px"></div>
|
||
<div id="personalCapitalHistory" style="margin-top:12px"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Agent permissions & risk limits</h3><span class="small muted">manual approval only</span></div>
|
||
<div id="liveAgentPermissions"></div>
|
||
</div>
|
||
<div class="grid2">
|
||
<div class="card">
|
||
<div class="card-h"><h3>Manual order console</h3><span class="small muted">review before any trade</span></div>
|
||
<div id="liveBackendStatus" class="locked-panel">Checking backend readiness...</div>
|
||
<div class="account-row">
|
||
<select class="select" id="liveIntentAgent"></select>
|
||
<input class="input" id="liveIntentMarket" placeholder="Market ID" />
|
||
<input class="input" id="liveIntentQuestion" placeholder="Market question / note" />
|
||
<select class="select" id="liveIntentSide"><option>YES</option><option>NO</option></select>
|
||
<input class="input" id="liveIntentAmount" type="number" min="1" step="25" placeholder="Max amount" />
|
||
<input class="input" id="liveIntentPrice" type="number" min="0.01" max="0.99" step="0.01" placeholder="Limit price" />
|
||
<button class="btn" id="dryRunOrderBtn">Stage manual intent</button>
|
||
</div>
|
||
<ol class="steps">
|
||
<li><b>Research signal</b> — choose an agent idea and inspect the exact market yourself.</li>
|
||
<li><b>Manual review</b> — confirm side, amount, price, risk cap, and current market status.</li>
|
||
<li><b>Your wallet only</b> — sign or submit only from your own Polymarket/deposit wallet.</li>
|
||
<li><b>Emergency pause</b> — keep agent automation off and cancel staged intents any time.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Personal audit trail</h3><span class="small muted">local + Neon records</span></div>
|
||
<div id="liveOrderDryRun"></div>
|
||
<div id="liveAuditTrail"></div>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Browse Polymarket for manual tickets</h3><span class="small muted">search active markets</span></div>
|
||
<div class="account-row">
|
||
<input class="input" id="liveSearchInput" placeholder="Search Polymarket markets" />
|
||
<button class="btn" id="liveSearchBtn">Search</button>
|
||
<button class="btn" id="liveAllBtn">All active</button>
|
||
<button class="btn ghost" id="liveMoreBtn">Load more</button>
|
||
<button class="btn ghost" id="liveClearSearchBtn">Clear</button>
|
||
</div>
|
||
<div class="filterbar" id="liveMarketFilterbar"></div>
|
||
<div class="trade-list" id="liveMarketList"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Manual trade ticket queue</h3><span class="small muted">AI suggests, you sign elsewhere</span></div>
|
||
<div id="personalTicketQueue"></div>
|
||
</div>
|
||
<div class="grid2">
|
||
<div class="card">
|
||
<div class="card-h"><h3>Tracked real positions</h3><span class="small muted">manual fills only</span></div>
|
||
<div id="realPositionSummary"></div>
|
||
<div id="realPositions"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Manual fill history</h3><span class="small muted">no private key used</span></div>
|
||
<div id="realFills"></div>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Provider webhooks</h3><span class="small muted">events are stored in Neon</span></div>
|
||
<div id="liveWebhookStatus" class="small muted">Checking webhook setup...</div>
|
||
</div>
|
||
<div class="grid2">
|
||
<div class="card">
|
||
<div class="card-h"><h3>Policies & consent</h3><span class="small muted">required before deposits</span></div>
|
||
<div id="livePolicyStatus" class="locked-panel">Checking policy status...</div>
|
||
<div class="account-row">
|
||
<label class="small muted"><input type="checkbox" id="liveAcceptTerms"> terms</label>
|
||
<label class="small muted"><input type="checkbox" id="liveAcceptPrivacy"> privacy</label>
|
||
<label class="small muted"><input type="checkbox" id="liveAcceptRisk"> risk disclosure</label>
|
||
<button class="btn" id="recordConsentBtn">Record consent</button>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Server controls</h3><span class="small muted">live safety gates</span></div>
|
||
<div class="locked-panel">
|
||
<b>Public production switch remains off</b>
|
||
<div class="small muted">The public product remains paper trading only. Personal mode can store your wallet limits, consent status, staged manual intents, webhooks, and incident records, but does not onboard investors or pool money.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ ABOUT ============ -->
|
||
<section class="tabpanel" data-tab="about">
|
||
<div class="section-title">ℹ️ The competition</div>
|
||
<p class="muted" style="margin-top:-6px;max-width:820px">Every agent starts with <b>$10,000</b> in paper money. Five core strategies use disciplined entry rules, while five aggressive strategies pursue catalysts, durable trends, breakouts, asymmetric tails, and concentrated conviction. Aggressive agents may size confirmed setups more heavily, but cash is retained whenever their strategy has no qualifying signal.</p>
|
||
<div class="about-grid" id="aboutAgents"></div>
|
||
<div class="card" style="margin-top:16px">
|
||
<div class="card-h"><h3>The live cycle</h3></div>
|
||
<ol class="steps">
|
||
<li><b>Fetch</b> — load the 500 most active eligible Yes/No Polymarket markets and tag each by category.</li>
|
||
<li><b>Score</b> — rank those active markets 0–100 by conviction and estimate edge vs. a fair-value model.</li>
|
||
<li><b>Suggest</b> — surface the strongest picks per category with a plain-English rationale.</li>
|
||
<li><b>Compete</b> — the strategy agents trade the same suggestions their own way, stop-loss weak positions, scale out through gain-stop tiers, exit stale or fading trades, then snapshot equity.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Device sync</h3></div>
|
||
<p class="muted" style="margin:0">Portfolios, returns, reports, suggestions, and chart history now sync through the site automatically. <b>Copy Sync</b> and <b>Import Sync</b> remain as a fallback for moving the exact state between devices.</p>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-h"><h3>Real-money trading requirements</h3><span class="small muted">before enabling deposits</span></div>
|
||
<ol class="steps">
|
||
<li><b>Eligibility</b> — identity checks, location restrictions, age checks, terms acceptance, and sanctions screening.</li>
|
||
<li><b>Wallets</b> — non-custodial wallet connection or Polymarket deposit-wallet flow so users sign orders themselves.</li>
|
||
<li><b>Accounts</b> — production login, sign-out, password reset, consent records, portfolio history, and secure sessions.</li>
|
||
<li><b>Funds</b> — compliant deposit/withdraw rails, webhooks, reconciled balances, statements, tax/accounting exports, and customer support.</li>
|
||
<li><b>Execution</b> — order router using Polymarket CLOB authentication, signed orders, cancellations, fill tracking, idempotency, retries, and stale-order protection.</li>
|
||
<li><b>Risk controls</b> — server-side user permissions for each agent, exposure limits, stop rules, emergency pause, market blacklists, and withdrawal access at all times.</li>
|
||
<li><b>Operations</b> — monitoring, alerts, security review, audit retention, incident response, and rollback plan.</li>
|
||
</ol>
|
||
</div>
|
||
<div class="disclaimer">⚠️ <b>Paper trading only — not financial advice.</b> Nothing here places real orders or moves money. The analysis is a transparent heuristic. Portfolios sync through the site when cloud state is available, with this browser keeping a local backup.</div>
|
||
</section>
|
||
|
||
<footer>
|
||
Build 106 · Adaptive strategy 62 · Five-minute autonomous scans · Exact-fee directional learning · Contest-deduplicated sports exploration · Live depth, fee, and size-verified bundle audit · Corrected one-decision-per-event settlement audit · Autonomous runtime 1 · Offline runtime 2 · Maker research 3 · Agent learning 3 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · Not financial advice ·
|
||
<a class="market-link" href="https://github.com/theodore-song/polymarket-analyst" target="_blank" rel="noopener">Source on GitHub</a>
|
||
</footer>
|
||
</div>
|
||
|
||
<script>
|
||
"use strict";
|
||
/* ============================================================
|
||
Polymarket Arena — fully client-side.
|
||
Fetches live markets, scores them, and runs ten competing
|
||
paper-trading agents. State syncs through /api/state when configured and uses localStorage as the on-device cache.
|
||
============================================================ */
|
||
|
||
const GAMMA = "https://gamma-api.polymarket.com";
|
||
const STARTING_BALANCE = 10000;
|
||
const STOP_LOSS_TIERS = [
|
||
{id:"18",drawdown:0.18,sellFrac:1.00,label:"-18%"},
|
||
];
|
||
const GAIN_STOP_TIERS = [
|
||
{id:"lock1",gain:0.15,maxGainFrac:0.35,sellFrac:0.25,label:"first profit lock"},
|
||
{id:"lock2",gain:0.30,maxGainFrac:0.60,sellFrac:0.25,label:"second profit lock"},
|
||
{id:"lock3",gain:0.50,maxGainFrac:0.85,sellFrac:0.25,label:"final profit lock"},
|
||
];
|
||
const EXIT_STALE_DAYS = 21;
|
||
const MIN_POLICY_HOLD_HOURS = 24;
|
||
const POLITICS_TREND_MIN_HOLD_HOURS = 72;
|
||
const EXIT_CONFIRM_HOURS = 6;
|
||
const AGENTS_KEY = "pma_agents_v2";
|
||
const SUG_KEY = "pma_suggestions_v5";
|
||
const BUILD_VERSION = 106;
|
||
const AGENT_LEARNING_VERSION = 3;
|
||
const SUGGESTION_ENGINE_VERSION = 62;
|
||
const MAKER_STRATEGY_VERSION = 3;
|
||
const SHOCK_FADE_STRATEGY_VERSION = 4;
|
||
const RESOLUTION_WEEK_NO_STRATEGY_VERSION = 3;
|
||
const SPORTS_CONTEST_NO_STRATEGY_VERSION = 1;
|
||
const PREVIOUS_STRATEGY_VERSION = 61;
|
||
const DIRECTIONAL_SIGNAL_POLICY_VERSION = 2;
|
||
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN = 51;
|
||
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX = 62;
|
||
const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40});
|
||
function normalizedStrategyVersion(value){
|
||
const version=Number(value||0);
|
||
return Number(LEGACY_BUILD_STRATEGY_LINEAGE[version]||version);
|
||
}
|
||
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 INVEST_KEY = "pma_invest_allocations_v1";
|
||
const PAPER_KEY = "pma_paper_accounts_v1";
|
||
const LIVE_KEY = "pma_live_readiness_v1";
|
||
const CHAT_KEY = "pma_agent_chat_v1";
|
||
const PAID_AGENT_CHAT_KEY = "pma_paid_agent_chat_v1";
|
||
const EMAIL_ALERT_KEY = "pma_trade_email_alerts_v1";
|
||
const MARKET_CACHE_KEY = "pma_market_cache_v1";
|
||
const PAPER_SESSION_PREFIX = "pma_cloud_session_";
|
||
const PERSONAL_MODE = new URLSearchParams(location.search).get("personal") === "1";
|
||
const AUTOMATION_MODE = new URLSearchParams(location.search).get("automation") === "1";
|
||
const PERSONAL_USER_ID = "local-readiness-user";
|
||
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,PAPER_KEY,LIVE_KEY,EMAIL_ALERT_KEY];
|
||
const PUBLIC_RUNTIME_KEYS=Object.freeze([AGENTS_KEY,SUG_KEY]);
|
||
const SYNC_LIMITS={closed:80,history:160,snapshots:240,suggestions:900,paperHistory:120,paperSnapshots:120,audit:120};
|
||
const PUBLIC_RUNTIME_LIMITS=Object.freeze({...SYNC_LIMITS,suggestions:300});
|
||
const CAT_COLORS = {Politics:"#fb7185",Sports:"#34d399",Crypto:"#fbbf24",Economy:"#7c8cff",
|
||
"Pop Culture":"#c77dff",Other:"#94a1bb",All:"#7c8cff"};
|
||
const CLOB = "https://clob.polymarket.com";
|
||
let PAPER_SEARCH_RESULTS = null;
|
||
let PAPER_MARKET_OFFSET = 0;
|
||
let PAPER_MARKET_QUERY = "";
|
||
let LIVE_SEARCH_RESULTS = [];
|
||
let LIVE_MARKET_OFFSET = 0;
|
||
let LIVE_MARKET_QUERY = "";
|
||
let LIVE_BACKEND_STATUS = null;
|
||
let LIVE_POLICY_STATUS = null;
|
||
let TRADE_EMAIL_STATUS = null;
|
||
let PERSONAL_TICKETS = [];
|
||
let REAL_PORTFOLIO = {positions:[],fills:[]};
|
||
let PERSONAL_CAPITAL = {account:{cash:0},allocations:[],events:[]};
|
||
let PROVIDER_CONFIG = null;
|
||
const catColor = (c) => CAT_COLORS[c] || CAT_COLORS.Other;
|
||
const getFocus = () => localStorage.getItem(FOCUS_KEY) || "All";
|
||
const setFocus = (c) => localStorage.setItem(FOCUS_KEY, c);
|
||
|
||
function applyPersonalMode(){
|
||
if(!PERSONAL_MODE)return;
|
||
document.body.classList.add("personal-mode");
|
||
const brand=document.querySelector(".brand-name"),sub=document.querySelector(".brand-sub");
|
||
if(brand)brand.textContent="Personal Polymarket Arena";
|
||
if(sub)sub.textContent="private research · paper tracking · manual links";
|
||
const heroTitle=document.querySelector(".hero h1"),heroCopy=document.querySelector(".hero p");
|
||
if(heroTitle)heroTitle.innerHTML=`Your private agent lab for <span class="grad">manual market research</span>`;
|
||
if(heroCopy)heroCopy.textContent="Use the same agents, charts, paper portfolios, and Polymarket links for your own research. Nothing here pools outside money, manages investor funds, or places live orders without manual review.";
|
||
document.querySelectorAll(".hbadge").forEach((b,i)=>{
|
||
const labels=["Private research mode","Manual execution only","Cloud paper accounts","Live market links"];
|
||
if(labels[i])b.textContent=labels[i];
|
||
});
|
||
}
|
||
|
||
async function initProviderConfig(){
|
||
try{
|
||
const r=await fetch("/api/config",{cache:"no-store"});
|
||
PROVIDER_CONFIG=await r.json();
|
||
const dsn=PROVIDER_CONFIG&&PROVIDER_CONFIG.public&&PROVIDER_CONFIG.public.sentry_dsn;
|
||
if(dsn&&!window.Sentry){
|
||
const script=document.createElement("script");
|
||
script.src="https://browser.sentry-cdn.com/8.55.0/bundle.tracing.min.js";
|
||
script.crossOrigin="anonymous";
|
||
script.onload=()=>{if(window.Sentry)window.Sentry.init({dsn,tracesSampleRate:0.1,environment:"production"});};
|
||
document.head.appendChild(script);
|
||
}
|
||
}catch(e){}
|
||
}
|
||
async function refreshTradeEmailStatus(){
|
||
try{
|
||
const r=await fetch("/api/live",{cache:"no-store"});
|
||
const d=await r.json();
|
||
TRADE_EMAIL_STATUS=d&&d.trade_email?d.trade_email:null;
|
||
}catch(e){TRADE_EMAIL_STATUS=null;}
|
||
renderEmailAlerts();
|
||
}
|
||
|
||
/* ---------- ten competing agents ----------
|
||
Five core and five aggressive strategies share the same live suggestion pool. */
|
||
const AGENTS = [
|
||
{id:"value", name:"Value Hunter", emoji:"🎯", color:"#7c8cff", kind:"strategy",
|
||
blurb:"Runs the arena's non-directional book: complete negative-risk bundles plus paired resting YES/NO bids in calm, reward-scoring markets. A paired quote earns nothing until live prices cross each leg; two fills below $1 lock settlement value, while unmatched inventory is unwound after 24 hours.",
|
||
rank:(s)=>[...s].sort((a,b)=>(Math.abs(b.net_edge||b.edge||0)*120+b.conviction+Number(b.evidence_score||0)*18)-(Math.abs(a.net_edge||a.edge||0)*120+a.conviction+Number(a.evidence_score||0)*18)),
|
||
maxNew:4, maxFrac:0.055, minConv:58, kelly:0.24, targetExposure:0.62, drawdownExposure:0.42},
|
||
{id:"momentum", name:"Momentum Chaser", emoji:"🚀", color:"#34d399", kind:"strategy",
|
||
blurb:"Chases markets where attention and price are accelerating together. It prioritizes true positive-edge entries, then uses tightly sized trend probes when one-day and one-week price direction agree, spreads are narrow, and enough time remains before settlement.",
|
||
rank:(s)=>[...s].sort((a,b)=>(b.volume_24hr*(b.conviction+20)*Math.max(.4,Math.abs(b.net_edge||b.edge||0)*18))-(a.volume_24hr*(a.conviction+20)*Math.max(.4,Math.abs(a.net_edge||a.edge||0)*18))),
|
||
maxNew:5, maxFrac:0.06, minConv:57, kelly:0.30, targetExposure:0.72, drawdownExposure:0.50},
|
||
{id:"favorite", name:"Favorite Backer", emoji:"🛡️", color:"#38d2e6", kind:"strategy",
|
||
blurb:"Runs a bounded sports-contest NO exploration lane from the corrected 5,000-market settlement audit. It selects only the highest-priced NO contract per real contest about three days before the published start, pays the executable ask, exact Gamma taker fee, and slippage, starts at 0.5% per position, and automatically scales or shuts down from independent settled results.",
|
||
rank:(s)=>[...s].filter(x=>x.signal_type==="sports-contest-no-pilot"||x.entry_price>=0.6).sort((a,b)=>(Number(b.signal_type==="sports-contest-no-pilot")-Number(a.signal_type==="sports-contest-no-pilot"))||(b.entry_price+Math.abs(b.net_edge||b.edge||0)+Number(b.evidence_score||0)*.2)-(a.entry_price+Math.abs(a.net_edge||a.edge||0)+Number(a.evidence_score||0)*.2)||b.conviction-a.conviction),
|
||
maxNew:4, maxFrac:0.05, minConv:58, kelly:0.24, targetExposure:0.58, drawdownExposure:0.38},
|
||
{id:"longshot", name:"Longshot Hunter", emoji:"🎰", color:"#fbbf24", kind:"strategy",
|
||
blurb:"The swing-for-upside strategy. It considers cheaper contracts only when price direction is independently confirmed and the remaining signal margin, evidence, and liquidity justify the volatility.",
|
||
rank:(s)=>[...s].filter(x=>x.entry_price<=0.48).sort((a,b)=>(Math.abs(b.net_edge||b.edge||0)*100+b.conviction+Number(b.evidence_score||0)*22-b.entry_price*12)-(Math.abs(a.net_edge||a.edge||0)*100+a.conviction+Number(a.evidence_score||0)*22-a.entry_price*12)),
|
||
maxNew:6, maxFrac:0.07, minConv:57, kelly:0.25, allocationFrac:0.065, targetExposure:0.50, drawdownExposure:0.38},
|
||
{id:"diversifier", name:"The Diversifier", emoji:"🌐", color:"#c77dff", kind:"strategy",
|
||
blurb:"Builds a broad basket instead of making a few concentrated bets. It spreads smaller, flatter positions across high-conviction, evidence-backed ideas while avoiding crowded overlap and weak net-edge fillers.",
|
||
rank:(s)=>[...s].sort((a,b)=>(b.conviction+Number(b.evidence_score||0)*15+Math.abs(b.net_edge||b.edge||0)*55)-(a.conviction+Number(a.evidence_score||0)*15+Math.abs(a.net_edge||a.edge||0)*55)),
|
||
maxNew:6, maxFrac:0.03, minConv:56, kelly:0.15, flat:true, targetExposure:0.70, drawdownExposure:0.48},
|
||
{id:"catalyst", name:"Catalyst Strike", emoji:"⚡", color:"#ff8a4c", kind:"strategy", aggressive:true,
|
||
blurb:"An aggressive event-driven strategy that concentrates on markets with fresh outside context, multiple evidence signals, and enough activity for a fast repricing. It is willing to enter earlier than the core agents when a measurable catalyst and positive post-cost edge agree, while accepting larger swings to capture the move.",
|
||
rank:(s)=>[...s].sort((a,b)=>(Number(b.evidence_source_count||0)*28+Number(b.evidence_score||0)*32+effectiveEntryEdge(b)*170+b.conviction+Math.log10(Number(b.volume_24hr||0)+1)*5)-(Number(a.evidence_source_count||0)*28+Number(a.evidence_score||0)*32+effectiveEntryEdge(a)*170+a.conviction+Math.log10(Number(a.volume_24hr||0)+1)*5)),
|
||
maxNew:7, maxFrac:0.09, minConv:52, kelly:0.46, allocationFrac:0.055, maxPositions:18, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.72, drawdownExposure:0.60},
|
||
{id:"reversal", name:"Shock Reversion", emoji:"⧗", color:"#ff5f91", kind:"strategy", aggressive:true,
|
||
blurb:"The research specialist for a cost-stressed mean-reversion rule. It observes accelerating moves of at least 8 percentage points over three hours, requires a safe contract and executable opposite-side price from 8–92%, and grades the exit after 12 hours. Its independent Strategy 4 holdout was inconclusive, so all five aggressive agents share zero-capital observations until 40 new event-deduplicated outcomes establish a positive lower confidence bound.",
|
||
rank:(s)=>[...s].filter(x=>(x.signal_type==="shock-fade-pilot"&&x.trade_ready)
|
||
||(x.trade_ready&&x.signal_type==="trend"&&Math.abs(Number(x.price_change_1d||0))<=0.04&&Number(x.days_to_resolution||0)>=21))
|
||
.sort((a,b)=>(Number(b.signal_type==="shock-fade-pilot")-Number(a.signal_type==="shock-fade-pilot"))
|
||
||((Math.abs(Number(b.shock_move_3h||0))*120+Number(b.signal_confidence||0)*45+effectiveEntryEdge(b)*180+b.conviction)
|
||
-(Math.abs(Number(a.shock_move_3h||0))*120+Number(a.signal_confidence||0)*45+effectiveEntryEdge(a)*180+a.conviction))),
|
||
maxNew:6, maxFrac:0.08, minConv:54, kelly:0.42, maxPositions:18, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.80, drawdownExposure:0.65},
|
||
{id:"breakout", name:"Breakout Velocity", emoji:"▲", color:"#00d5b5", kind:"strategy", aggressive:true,
|
||
blurb:"A high-tempo breakout strategy that follows confirmed price direction when momentum, fresh volume, tight spreads, and positive modeled edge line up. It builds positions quickly and keeps less idle cash so strong moves can drive the portfolio, knowing failed breakouts will create sharper drawdowns.",
|
||
rank:(s)=>[...s].sort((a,b)=>(Number(b.momentum_strength||0)*65+Math.log10(Number(b.volume_24hr||0)+1)*9+effectiveEntryEdge(b)*130+b.conviction)-(Number(a.momentum_strength||0)*65+Math.log10(Number(a.volume_24hr||0)+1)*9+effectiveEntryEdge(a)*130+a.conviction)),
|
||
maxNew:8, maxFrac:0.075, minConv:50, kelly:0.44, maxPositions:22, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.88, drawdownExposure:0.72},
|
||
{id:"tailalpha", name:"Tail Risk Alpha", emoji:"✦", color:"#ffd166", kind:"strategy", aggressive:true,
|
||
blurb:"An asymmetric-upside strategy focused on cheaper contracts with confirmed direction and room to reprice. It accepts a low hit rate and volatile results in exchange for larger potential payoffs, but still requires evidence, liquidity, and time to resolution.",
|
||
rank:(s)=>[...s].filter(x=>Number(x.entry_price||1)<=0.42).sort((a,b)=>(effectiveEntryEdge(b)*220+Number(b.evidence_score||0)*42+b.conviction-Number(b.entry_price||0)*35)-(effectiveEntryEdge(a)*220+Number(a.evidence_score||0)*42+a.conviction-Number(a.entry_price||0)*35)),
|
||
maxNew:6, maxFrac:0.07, minConv:54, kelly:0.38, allocationFrac:0.065, maxPositions:18, maxCategoryPct:0.40, maxCycleDrawdown:8, targetExposure:0.72, drawdownExposure:0.58},
|
||
{id:"conviction", name:"Conviction Max", emoji:"◆", color:"#b58cff", kind:"strategy", aggressive:true,
|
||
blurb:"A concentrated highest-conviction strategy that ignores mediocre opportunities and commits to the few markets with the strongest combined confirmation, evidence, and signal margin. It deliberately runs a smaller book so its best ideas matter without filling exposure for its own sake.",
|
||
rank:(s)=>[...s].sort((a,b)=>(effectiveEntryEdge(b)*250+Number(b.evidence_score||0)*55+b.conviction*1.4+Number(b.evidence_source_count||0)*12)-(effectiveEntryEdge(a)*250+Number(a.evidence_score||0)*55+a.conviction*1.4+Number(a.evidence_source_count||0)*12)),
|
||
maxNew:4, maxFrac:0.10, minConv:60, kelly:0.58, maxPositions:18, maxCategoryPct:0.45, maxCycleDrawdown:8, targetExposure:0.82, drawdownExposure:0.68},
|
||
];
|
||
const agentById = (id) => AGENTS.find(a=>a.id===id) || AGENTS[0];
|
||
|
||
/* ---------- small utils ---------- */
|
||
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 paperSessionKey=(id)=>`${PAPER_SESSION_PREFIX}${id}`;
|
||
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",minute:"2-digit",hourCycle:"h23"}).formatToParts(d);
|
||
return Object.fromEntries(parts.map(p=>[p.type,p.value]));
|
||
}
|
||
const todayStr = () => {const p=partsInLocalTime();return `${p.year}-${p.month}-${p.day}`;};
|
||
const RUN_INTERVAL_MS = 60000;
|
||
const OFFLINE_ENTRY_MAX_AGE_MS = 90*60*1000;
|
||
const OFFLINE_CACHE_MAX_AGE_MS = 24*60*60*1000;
|
||
const NETWORK_REQUEST_TIMEOUT_MS = 8000;
|
||
const PRICE_REQUEST_TIMEOUT_MS = 4000;
|
||
const SIGNAL_EARLY_RISK_HORIZONS = Object.freeze([6]);
|
||
const SIGNAL_PROMOTION_HORIZONS = Object.freeze([24,72]);
|
||
const SIGNAL_EVAL_HORIZONS = Object.freeze([...SIGNAL_EARLY_RISK_HORIZONS,...SIGNAL_PROMOTION_HORIZONS]);
|
||
const SIGNAL_EVAL_TOLERANCE_HOURS = 6;
|
||
const SIGNAL_EVAL_HOURS = SIGNAL_EVAL_HORIZONS[0];
|
||
const SIGNAL_LEDGER_PENDING_LIMIT = 600;
|
||
const SIGNAL_LEDGER_OUTCOME_LIMIT = 1000;
|
||
const SIGNAL_LEDGER_RETRY_HOURS = SIGNAL_EVAL_HORIZONS[SIGNAL_EVAL_HORIZONS.length-1]+SIGNAL_EVAL_TOLERANCE_HOURS;
|
||
const SIGNAL_LEDGER_DUE_FETCH_LIMIT = 80;
|
||
const currentCycleHour = () => {const p=partsInLocalTime();return `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}|s${SUGGESTION_ENGINE_VERSION}`;};
|
||
function cycleHourFromIso(iso){
|
||
const d=new Date(iso);
|
||
if(isNaN(d))return null;
|
||
const p=partsInLocalTime(d);return `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}|s${SUGGESTION_ENGINE_VERSION}`;
|
||
}
|
||
const nowIso = () => new Date().toISOString();
|
||
const cycleIso = () => SIM_DAY ? new Date(SIM_DAY+"T12:00:00Z").toISOString() : nowIso();
|
||
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
|
||
let CYCLE_RUNNING = false;
|
||
const logDay = () => SIM_DAY || todayStr();
|
||
function toast(msg){
|
||
let t=document.querySelector(".toast");
|
||
if(!t){t=document.createElement("div");t.className="toast";document.body.appendChild(t);}
|
||
t.textContent=msg;t.classList.add("show");
|
||
clearTimeout(t._h);t._h=setTimeout(()=>t.classList.remove("show"),3600);
|
||
}
|
||
function setStatus(text,live){
|
||
const health=window.PMA_CLOUD_STATE_HEALTH;
|
||
if(!live&&health&&health.degraded&&!String(text).includes("local only")&&!String(text).includes("cloud sync")){
|
||
const label=window.PMA_CLOUD_STATUS_LABEL;
|
||
text=typeof label==="function"?label(text,health.providers):`${text} · local only`;
|
||
}
|
||
$("statusText").textContent=text;$("statusDot").classList.toggle("live",!!live);
|
||
}
|
||
|
||
async function fetchWithTimeout(url,options={},timeoutMs=NETWORK_REQUEST_TIMEOUT_MS){
|
||
const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),timeoutMs);
|
||
try{return await fetch(url,Object.assign({},options,{signal:controller.signal}));}
|
||
finally{clearTimeout(timer);}
|
||
}
|
||
async function mapWithConcurrency(items,limit,mapper){
|
||
const input=[...(items||[])],results=new Array(input.length),workers=[];
|
||
let cursor=0;
|
||
const work=async()=>{
|
||
while(cursor<input.length){const index=cursor++;results[index]=await mapper(input[index],index);}
|
||
};
|
||
for(let i=0;i<Math.min(Math.max(1,limit),input.length);i++)workers.push(work());
|
||
await Promise.all(workers);
|
||
return results;
|
||
}
|
||
|
||
/* ---------- Polymarket client ---------- */
|
||
function parseJsonField(v){if(v==null)return [];if(Array.isArray(v))return v;try{return JSON.parse(v);}catch(e){return [];}}
|
||
function toNum(v,d=0){const n=Number(v);return Number.isFinite(n)?n:d;}
|
||
function normalizeApiIso(value){
|
||
if(!value)return null;
|
||
const dt=new Date(String(value).replace(" ","T").replace(/\+00$/,"Z"));
|
||
return Number.isFinite(dt.getTime())?dt.toISOString():null;
|
||
}
|
||
function daysUntil(iso){const normalized=normalizeApiIso(iso);return normalized?(new Date(normalized)-new Date())/86400000:null;}
|
||
function hoursUntil(iso){const normalized=normalizeApiIso(iso);return normalized?(new Date(normalized)-new Date())/3600000:null;}
|
||
function hasYesNoOutcomes(raw){
|
||
const labels=parseJsonField(raw&&raw.outcomes).map(outcome=>String(outcome||"").trim().toLowerCase());
|
||
return labels.length===2&&labels[0]==="yes"&&labels[1]==="no";
|
||
}
|
||
const CATEGORY_RULES=[
|
||
["Politics",["politics","election","elections","us-politics","geopolitics","trump","world-leaders","government","congress","policy","biden","2026-election","democrats","republicans"]],
|
||
["Crypto",["crypto","bitcoin","ethereum","btc","eth","solana","memecoins","defi","stablecoin","xrp"]],
|
||
["Sports",["sports","soccer","football","nba","nfl","mlb","nhl","tennis","basketball","baseball","ufc","boxing","cricket","golf","f1","fifa-world-cup","games","tournament-futures","epl"]],
|
||
["Economy",["economy","business","fed","inflation","interest-rates","gdp","jobs","recession","stocks","earnings","financials","tariffs"]],
|
||
["Pop Culture",["pop-culture","entertainment","movies","music","tv","awards","celebrity","gaming","ai"]],
|
||
];
|
||
function classifyCategory(tags){
|
||
const slugs=tags.map(t=>(t.slug||t.label||"").toLowerCase());
|
||
for(const [cat,keys] of CATEGORY_RULES){if(slugs.some(s=>keys.includes(s)))return cat;}
|
||
return "Other";
|
||
}
|
||
function normalizeMarket(raw,{allowClosed=false}={}){
|
||
const outcomes=parseJsonField(raw.outcomes);
|
||
const prices=parseJsonField(raw.outcomePrices).map(p=>toNum(p));
|
||
if(!hasYesNoOutcomes(raw)||prices.length!==2)return null;
|
||
if(!allowClosed&&(raw.acceptingOrders===false||raw.closed))return null;
|
||
const yes=prices[0];
|
||
if(!Number.isFinite(yes)||yes<0||yes>1||(!allowClosed&&(yes<=0||yes>=1)))return null;
|
||
const events=raw.events||[]; const ev=events.length?events[0]:{};
|
||
const rawTags=[...(Array.isArray(raw.tags)?raw.tags:[]),...(Array.isArray(ev.tags)?ev.tags:[])];
|
||
const tags=rawTags.filter((tag,index)=>rawTags.findIndex(candidate=>(candidate.slug||candidate.label)===(tag.slug||tag.label))===index);
|
||
const gameStart=normalizeApiIso(raw.gameStartTime||raw.eventStartTime||ev.startTime||ev.eventDate);
|
||
return {category:classifyCategory(tags),tags:tags.map(t=>t.label).filter(Boolean).slice(0,4),market_slug:String(raw.slug||""),
|
||
id:String(raw.id),question:(raw.question||"").trim(),event:(ev.title||"").trim(),event_id:String(ev.id||raw.eventId||""),
|
||
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:normalizeApiIso(raw.endDateIso||raw.endDate),
|
||
game_start:gameStart,hours_to_start:hoursUntil(gameStart),sports_market_type:String(raw.sportsMarketType||""),
|
||
spread:toNum(raw.spread),best_bid:toNum(raw.bestBid),best_ask:toNum(raw.bestAsk),
|
||
price_change_1h:toNum(raw.oneHourPriceChange),price_change_1d:toNum(raw.oneDayPriceChange),
|
||
price_change_1w:toNum(raw.oneWeekPriceChange),price_change_1m:toNum(raw.oneMonthPriceChange),
|
||
days_to_resolution:daysUntil(raw.endDate),image:raw.image||"",condition_id:String(raw.conditionId||""),
|
||
tick_size:toNum(raw.orderPriceMinTickSize,0.01),order_min_size:toNum(raw.orderMinSize,5),
|
||
competitive:toNum(raw.competitive||(ev&&ev.competitive)),rewards_daily_rate:(raw.clobRewards||[]).reduce((sum,reward)=>sum+toNum(reward.rewardsDailyRate),0),
|
||
rewards_min_size:toNum(raw.rewardsMinSize),rewards_max_spread:toNum(raw.rewardsMaxSpread),
|
||
fees_enabled:raw.feesEnabled===false?false:(raw.feesEnabled===true?true:null),fee_schedule:normalizeBundleFeeSchedule(raw),
|
||
closed:Boolean(raw.closed),accepting_orders:raw.acceptingOrders!==false,
|
||
url:ev.slug?`https://polymarket.com/event/${ev.slug}`:""};
|
||
}
|
||
async function fetchMarkets(pages=Infinity,perPage=100,onProgress=null,eligibleLimit=null){
|
||
const all=[],eligible=[];
|
||
const maxPages=Number.isFinite(pages)?pages:MAX_ACTIVE_MARKET_PAGES;
|
||
const seen=new Set();
|
||
const useKeyset=!Number.isFinite(pages)||pages>20;
|
||
let cursor=null;
|
||
for(let i=0;i<maxPages;i++){
|
||
const params=new URLSearchParams({closed:"false",active:"true",archived:"false",include_tag:"true",
|
||
limit:String(perPage),order:"volume24hr",ascending:"false"});
|
||
if(useKeyset){
|
||
if(cursor)params.set("after_cursor",cursor);
|
||
}else{
|
||
params.set("offset",String(i*perPage));
|
||
}
|
||
const r=await fetchWithTimeout(`${GAMMA}/markets${useKeyset?"/keyset":""}?${params}`);
|
||
if(!r.ok){if(i===0)throw new Error("Polymarket API "+r.status);break;}
|
||
const page=await r.json();
|
||
const raw=useKeyset?(page.markets||[]):page;
|
||
if(!raw.length)break;
|
||
let added=0;
|
||
for(const m of raw){
|
||
const id=String(m.id||"");
|
||
if(id&&seen.has(id))continue;
|
||
if(id)seen.add(id);
|
||
all.push(m);added++;
|
||
const normalized=normalizeMarket(m);
|
||
if(normalized&&normalized.question)eligible.push(normalized);
|
||
}
|
||
if(useKeyset)cursor=page.next_cursor||null;
|
||
if(onProgress)onProgress(eligible.length,all.length);
|
||
if((eligibleLimit&&eligible.length>=eligibleLimit)||raw.length<perPage||added===0||(useKeyset&&!cursor))break;
|
||
}
|
||
return eligibleLimit?eligible.slice(0,eligibleLimit):eligible;
|
||
}
|
||
async function fetchMarketPrice(id){
|
||
try{const r=await fetchWithTimeout(`${GAMMA}/markets/${id}`,{},PRICE_REQUEST_TIMEOUT_MS);if(!r.ok)return null;return normalizeMarket(await r.json(),{allowClosed:true});}
|
||
catch(e){return null;}
|
||
}
|
||
const DOMINANCE_THRESHOLD_PATTERNS=Object.freeze([
|
||
{direction:"above",regex:/(\b(?:above|over|at least|higher than|greater than)\s*(?:[$€£]\s*)?)([0-9]+(?:,[0-9]{3})*(?:\.[0-9]+)?)\s*(k|m|b|%|bps)?\b/i},
|
||
{direction:"below",regex:/(\b(?:below|(?<!\/)under|at most|lower than|less than)\s*(?:[$€£]\s*)?)([0-9]+(?:,[0-9]{3})*(?:\.[0-9]+)?)\s*(k|m|b|%|bps)?\b/i},
|
||
]);
|
||
const DOMINANCE_MONTHS=Object.freeze({january:1,february:2,march:3,april:4,may:5,june:6,july:7,august:8,september:9,october:10,november:11,december:12});
|
||
const DOMINANCE_DEADLINE_PATTERN=/\b((?:on\s+or\s+)?(?:by|before)\s+(?:the\s+)?)(january|february|march|april|may|june|july|august|september|october|november|december)\s+(\d{1,2})(?:st|nd|rd|th)?(?:,?\s+(\d{4}))?\b/i;
|
||
function parseDominanceThreshold(question){
|
||
const text=String(question||"").trim();
|
||
for(const pattern of DOMINANCE_THRESHOLD_PATTERNS){
|
||
const match=pattern.regex.exec(text);if(!match)continue;
|
||
const raw=Number(match[2].replaceAll(",","")),suffix=String(match[3]||"").toLowerCase();
|
||
const multiplier=suffix==="k"?1e3:(suffix==="m"?1e6:(suffix==="b"?1e9:1)),value=raw*multiplier;
|
||
if(!Number.isFinite(value))continue;
|
||
const valueStart=match.index+match[1].length,valueEnd=valueStart+match[2].length;
|
||
const stem=`${text.slice(0,valueStart)}{threshold}${text.slice(valueEnd)}`.toLowerCase().replace(/\s+/g," ").trim();
|
||
return {kind:"threshold",direction:pattern.direction,value,stem};
|
||
}
|
||
return null;
|
||
}
|
||
function parseDominanceDeadline(question){
|
||
const text=String(question||"").trim(),match=DOMINANCE_DEADLINE_PATTERN.exec(text);if(!match)return null;
|
||
const month=DOMINANCE_MONTHS[String(match[2]).toLowerCase()],day=Number(match[3]),year=match[4]?Number(match[4]):null;
|
||
if(!month||!Number.isInteger(day)||day<1||day>new Date(Date.UTC(year||2024,month,0)).getUTCDate())return null;
|
||
const dateStart=match.index+match[1].length,dateEnd=match.index+match[0].length;
|
||
const stem=`${text.slice(0,dateStart)}{deadline}${text.slice(dateEnd)}`.toLowerCase().replace(/\s+/g," ").trim();
|
||
return {kind:"deadline",direction:"deadline",value:year?year*10000+month*100+day:month*100+day,
|
||
yearMode:year?"explicit-year":"implicit-year",stem};
|
||
}
|
||
function normalizeBundleFeeSchedule(raw){
|
||
if(raw&&raw.feesEnabled===false)return {rate:0,exponent:1,takerOnly:true};
|
||
const schedule=raw&&raw.feeSchedule,rate=Number(schedule&&schedule.rate),exponent=Number(schedule&&schedule.exponent);
|
||
return raw&&raw.feesEnabled===true&&Number.isFinite(rate)&&rate>=0&&Number.isFinite(exponent)&&exponent>0
|
||
?{rate,exponent,takerOnly:schedule.takerOnly!==false}:null;
|
||
}
|
||
function bundleFeePerShare(schedule,price){
|
||
const p=Number(price);if(!schedule||!Number.isFinite(p)||p<=0||p>=1)return null;
|
||
return Math.max(0,Number(schedule.rate))*Math.pow(p*(1-p),Number(schedule.exponent||1));
|
||
}
|
||
function bundleFeeReserve(raw,price){
|
||
if(raw&&raw.feesEnabled===false)return 0;
|
||
const fee=bundleFeePerShare(normalizeBundleFeeSchedule(raw),price);return Number.isFinite(fee)?fee:SIGNAL_ROUND_TRIP_COST;
|
||
}
|
||
function signalExecutionCost(raw,entry,exit){
|
||
const feesEnabled=raw&&raw.fees_enabled,feeSchedule=raw&&raw.fee_schedule;
|
||
if(feesEnabled===false)return {cost:SIGNAL_ROUND_TRIP_COST,exact:true,fee:0,slippage:SIGNAL_ROUND_TRIP_COST};
|
||
const entryFee=bundleFeePerShare(feeSchedule,entry),exitFee=bundleFeePerShare(feeSchedule,exit);
|
||
if(Number.isFinite(entryFee)&&Number.isFinite(exitFee)){
|
||
const fee=entryFee+exitFee;return {cost:SIGNAL_ROUND_TRIP_COST+fee,exact:true,fee,slippage:SIGNAL_ROUND_TRIP_COST};
|
||
}
|
||
return {cost:SIGNAL_ROUND_TRIP_COST+SIGNAL_UNKNOWN_FEE_FALLBACK,exact:false,fee:SIGNAL_UNKNOWN_FEE_FALLBACK,slippage:SIGNAL_ROUND_TRIP_COST};
|
||
}
|
||
function dominanceBundleSuggestions(event,{includeUnprofitable=false}={}){
|
||
if(!event||event.closed||event.active===false)return [];
|
||
const rawLegs=Array.isArray(event.markets)?event.markets:[],eventTags=Array.isArray(event.tags)?event.tags:[];
|
||
const quotes=rawLegs.map(raw=>{
|
||
const contract=parseDominanceThreshold(raw.question)||parseDominanceDeadline(raw.question);
|
||
const outcomes=parseJsonField(raw.outcomes).map(outcome=>String(outcome||"").trim().toLowerCase());
|
||
const prices=parseJsonField(raw.outcomePrices).map(toNum),tokens=parseJsonField(raw.clobTokenIds).map(String);
|
||
const yesBid=toNum(raw.bestBid,NaN),yesAsk=toNum(raw.bestAsk,NaN),liquidity=toNum(raw.liquidityNum||raw.liquidity);
|
||
if(!contract||outcomes.length!==2||outcomes[0]!=="yes"||outcomes[1]!=="no"||prices.length!==2||tokens.length!==2
|
||
||!tokens[0]||!tokens[1]||!Number.isFinite(yesBid)||!Number.isFinite(yesAsk)||yesBid<0||yesAsk>1||yesAsk<yesBid
|
||
||liquidity<NEG_RISK_MIN_LIQUIDITY||raw.closed||raw.active===false||raw.acceptingOrders===false)return null;
|
||
return Object.assign({},contract,{market_id:String(raw.id||""),question:String(raw.question||"").trim(),yes_bid:yesBid,yes_ask:yesAsk,
|
||
yes_mid:prices[0],no_mid:prices[1],yes_token:tokens[0],no_token:tokens[1],liquidity,
|
||
volume:toNum(raw.volumeNum||raw.volume),volume_24hr:toNum(raw.volume24hr),spread:toNum(raw.spread),
|
||
fees_enabled:raw.feesEnabled===false?false:true,fee_schedule:normalizeBundleFeeSchedule(raw),
|
||
url:event.slug?`https://polymarket.com/event/${event.slug}`:""});
|
||
}).filter(Boolean);
|
||
const grouped=new Map();
|
||
quotes.forEach(quote=>{const key=`${quote.kind}|${quote.direction}|${quote.yearMode||""}|${quote.stem}`,rows=grouped.get(key)||[];rows.push(quote);grouped.set(key,rows);});
|
||
const candidates=[];
|
||
for(const rows of grouped.values()){
|
||
if(rows.length<2||new Set(rows.map(row=>row.value)).size!==rows.length)continue;
|
||
const ordered=[...rows].sort((a,b)=>a.value-b.value);
|
||
for(let left=0;left<ordered.length-1;left++)for(let right=left+1;right<ordered.length;right++){
|
||
const lower=ordered[left],higher=ordered[right],deadline=lower.kind==="deadline";
|
||
const superset=deadline?null:(lower.direction==="above"?lower:higher),subset=deadline?null:(lower.direction==="above"?higher:lower);
|
||
const pair=deadline?[{quote:lower,side:"NO"},{quote:higher,side:"YES"}]:[{quote:superset,side:"YES"},{quote:subset,side:"NO"}];
|
||
const legs=pair.map(({quote,side})=>{const topAsk=side==="YES"?quote.yes_ask:1-quote.yes_bid,feeReserve=bundleFeeReserve({feesEnabled:quote.fees_enabled,feeSchedule:quote.fee_schedule},topAsk);return {market_id:quote.market_id,question:quote.question,side,
|
||
token_id:side==="YES"?quote.yes_token:quote.no_token,
|
||
top_ask_price:topAsk,fee_reserve:feeReserve,fees_enabled:quote.fees_enabled,fee_schedule:quote.fee_schedule,
|
||
entry_price:topAsk+feeReserve,
|
||
current_price:side==="YES"?quote.yes_mid:quote.no_mid,liquidity:quote.liquidity,url:quote.url};})
|
||
.map(leg=>Object.assign({},leg,{entry_price:+leg.entry_price.toFixed(4),current_price:+Number(leg.current_price).toFixed(4)}));
|
||
if(legs.some(leg=>!leg.market_id||!Number.isFinite(leg.entry_price)||!Number.isFinite(leg.current_price)||leg.entry_price<=0||leg.entry_price>=1))continue;
|
||
const cost=legs.reduce((sum,leg)=>sum+leg.entry_price,0),payout=1,profit=payout-cost,netReturn=profit/cost;
|
||
const actionable=profit>=NEG_RISK_MIN_NET_PROFIT&&netReturn>=NEG_RISK_MIN_NET_RETURN;
|
||
if(!actionable&&!includeUnprofitable)continue;
|
||
const bundleLogic=deadline?"deadline-dominance":"threshold-dominance",bundleId=`dominance:${event.id}:${legs[0].market_id}:${legs[1].market_id}`;
|
||
const relation=deadline?`${lower.question} is the earlier deadline and ${higher.question} is the later deadline`
|
||
:`${superset.question} logically contains ${subset.question}`;
|
||
const strategy=deadline?"Buying NO on the earlier deadline and YES on the later deadline":"Buying YES on the superset and NO on the subset";
|
||
const quoteA=pair[0].quote,quoteB=pair[1].quote;
|
||
candidates.push({market_id:bundleId,bundle_id:bundleId,bundle_side:"MIXED",bundle_logic:bundleLogic,
|
||
question:`Dominance pair: ${event.title||(deadline?lower.stem.replace("{deadline}","___"):superset.stem.replace("{threshold}","___"))}`,event:event.title||"",url:legs[0].url,
|
||
category:classifyCategory(eventTags),tags:eventTags.map(tag=>tag.label).filter(Boolean).slice(0,4),side:"MIXED",
|
||
entry_price:+(cost/2).toFixed(4),yes_price:+(cost/2).toFixed(4),no_price:+(cost/2).toFixed(4),fair_value:+(payout/2).toFixed(4),
|
||
edge:+netReturn.toFixed(4),net_edge:+netReturn.toFixed(4),friction:+legs.reduce((sum,leg)=>sum+leg.fee_reserve,0).toFixed(4),chase_penalty:0,
|
||
evidence_score:1,evidence_source_count:0,quality:"bundle-arb",conviction:+clamp(80+netReturn*1200,80,96).toFixed(1),
|
||
volume:quoteA.volume+quoteB.volume,volume_24hr:quoteA.volume_24hr+quoteB.volume_24hr,liquidity:Math.min(quoteA.liquidity,quoteB.liquidity),
|
||
spread:Math.max(quoteA.spread,quoteB.spread),price_change_1h:0,price_change_1d:0,price_change_1w:0,momentum_strength:0,
|
||
signal_strength:1,signal_confidence:1,signal_type:"bundle-arb",trade_ready:actionable,entry_candidate:actionable,audited_observation_only:!actionable,
|
||
adaptive_promotion:false,watch_only:!actionable,jump_risk:false,requires_live:true,opportunity_actionable:actionable,days_to_resolution:daysUntil(event.endDate),
|
||
bundle_cost_per_unit:+cost.toFixed(4),bundle_payout_per_unit:payout,bundle_net_profit_per_unit:+profit.toFixed(4),bundle_legs:legs,
|
||
depth_verified:false,fees_verified:false,execution_model:"top-of-book-estimate",
|
||
drivers:[deadline?"same-event nested deadlines":"same-event nested thresholds","live executable ask pair","guaranteed minimum one-contract payout","positive margin after estimated costs"],
|
||
rationale:`${deadline?"Deadline":"Threshold"} dominance: ${relation}. ${strategy} costs ${cost.toFixed(3)} per pair against a guaranteed minimum payout of $1.00 when both use the same event terms. The preliminary margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); a live depth and market-specific fee check is required before entry. Both live legs must open together and remain paired through settlement.`});
|
||
}
|
||
}
|
||
return candidates.sort((a,b)=>b.net_edge-a.net_edge);
|
||
}
|
||
function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
|
||
if(!event||!event.negRisk||event.enableNegRisk===false)return null;
|
||
const rawLegs=Array.isArray(event.markets)?event.markets:[];
|
||
if(rawLegs.length<2||rawLegs.some(m=>m.closed||m.active===false||m.acceptingOrders===false))return null;
|
||
const eventTags=Array.isArray(event.tags)?event.tags:[],category=classifyCategory(eventTags);
|
||
const quotes=rawLegs.map(raw=>{
|
||
const outcomes=parseJsonField(raw.outcomes).map(outcome=>String(outcome||"").trim().toLowerCase());
|
||
const prices=parseJsonField(raw.outcomePrices).map(toNum),tokens=parseJsonField(raw.clobTokenIds).map(String);
|
||
return {market_id:String(raw.id||""),question:(raw.question||"").trim(),yes_bid:toNum(raw.bestBid,NaN),yes_ask:toNum(raw.bestAsk,NaN),
|
||
yes_mid:prices[0],no_mid:prices[1],yes_token:tokens[0]||null,no_token:tokens[1]||null,
|
||
fees_enabled:raw.feesEnabled===false?false:true,fee_schedule:normalizeBundleFeeSchedule(raw),raw_market:raw,
|
||
binary_labels:outcomes[0]==="yes"&&outcomes[1]==="no",liquidity:toNum(raw.liquidityNum||raw.liquidity),url:event.slug?`https://polymarket.com/event/${event.slug}`:""};
|
||
});
|
||
if(quotes.some(leg=>!leg.binary_labels||!leg.market_id||!leg.yes_token||!leg.no_token||!Number.isFinite(leg.yes_bid)||!Number.isFinite(leg.yes_ask)
|
||
||leg.yes_bid<0||leg.yes_ask>1||leg.yes_ask<leg.yes_bid||!Number.isFinite(leg.yes_mid)||!Number.isFinite(leg.no_mid)
|
||
||leg.liquidity<NEG_RISK_MIN_LIQUIDITY))return null;
|
||
const makeCandidate=(side)=>{
|
||
const yesSide=side==="YES",payout=yesSide?1:quotes.length-1;
|
||
const legs=quotes.map(leg=>{
|
||
const topAsk=yesSide?leg.yes_ask:1-leg.yes_bid,feeReserve=bundleFeeReserve(leg.raw_market,topAsk),entry=topAsk+feeReserve,current=yesSide?leg.yes_mid:leg.no_mid;
|
||
return {market_id:leg.market_id,question:leg.question,side,token_id:yesSide?leg.yes_token:leg.no_token,
|
||
top_ask_price:+topAsk.toFixed(4),fee_reserve:feeReserve,fees_enabled:leg.fees_enabled,fee_schedule:leg.fee_schedule,
|
||
entry_price:+entry.toFixed(4),current_price:+Number(current).toFixed(4),liquidity:leg.liquidity,url:leg.url};
|
||
});
|
||
if(legs.some(leg=>!Number.isFinite(leg.entry_price)||!Number.isFinite(leg.current_price)||leg.entry_price<=0||leg.entry_price>=1))return null;
|
||
const cost=legs.reduce((sum,leg)=>sum+leg.entry_price,0),profit=payout-cost,netReturn=cost>0?profit/cost:0;
|
||
return {side,legs,cost,payout,profit,netReturn};
|
||
};
|
||
const candidate=[makeCandidate("YES"),makeCandidate("NO")].filter(Boolean)
|
||
.filter(x=>includeUnprofitable||(x.profit>=NEG_RISK_MIN_NET_PROFIT&&x.netReturn>=NEG_RISK_MIN_NET_RETURN))
|
||
.sort((a,b)=>b.netReturn-a.netReturn)[0];
|
||
if(!candidate)return null;
|
||
const {side,legs,cost,payout,profit,netReturn}=candidate,minLiquidity=Math.min(...legs.map(leg=>leg.liquidity));
|
||
const actionable=profit>=NEG_RISK_MIN_NET_PROFIT&&netReturn>=NEG_RISK_MIN_NET_RETURN;
|
||
return {market_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_side:side,
|
||
question:`Complete ${side} bundle: ${event.title||"multi-outcome event"}`,event:event.title||"",
|
||
url:event.slug?`https://polymarket.com/event/${event.slug}`:"",category,tags:eventTags.map(t=>t.label).filter(Boolean).slice(0,4),
|
||
side,entry_price:+(cost/legs.length).toFixed(4),yes_price:side==="YES"?+(cost/legs.length).toFixed(4):+(1-cost/legs.length).toFixed(4),
|
||
no_price:side==="NO"?+(cost/legs.length).toFixed(4):+(1-cost/legs.length).toFixed(4),fair_value:+(payout/legs.length).toFixed(4),
|
||
edge:+netReturn.toFixed(4),net_edge:+netReturn.toFixed(4),friction:+legs.reduce((sum,leg)=>sum+leg.fee_reserve,0).toFixed(4),chase_penalty:0,
|
||
evidence_score:1,evidence_source_count:0,quality:"bundle-arb",conviction:+clamp(78+netReturn*1200,78,96).toFixed(1),
|
||
volume:rawLegs.reduce((sum,m)=>sum+toNum(m.volumeNum||m.volume),0),volume_24hr:rawLegs.reduce((sum,m)=>sum+toNum(m.volume24hr),0),liquidity:minLiquidity,
|
||
spread:Math.max(...rawLegs.map(m=>toNum(m.spread))),price_change_1h:0,price_change_1d:0,price_change_1w:0,momentum_strength:0,
|
||
signal_strength:1,signal_confidence:1,signal_type:"bundle-arb",trade_ready:actionable,entry_candidate:actionable,audited_observation_only:!actionable,
|
||
adaptive_promotion:false,watch_only:!actionable,jump_risk:false,requires_live:true,opportunity_actionable:actionable,days_to_resolution:daysUntil(event.endDate),
|
||
bundle_cost_per_unit:+cost.toFixed(4),bundle_payout_per_unit:payout,bundle_net_profit_per_unit:+profit.toFixed(4),bundle_legs:legs,
|
||
depth_verified:false,fees_verified:false,execution_model:"top-of-book-estimate",
|
||
drivers:["complete negative-risk event","executable bid/ask gap","positive margin after estimated costs"],
|
||
rationale:`Complete bundle: buy every ${side} leg together for ${cost.toFixed(3)} per bundle unit against a ${payout.toFixed(2)} worst-case payout. The preliminary margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); a live depth and market-specific fee check is required before entry. This must stay intact until settlement.`};
|
||
}
|
||
function bundleBookAskLevels(book){
|
||
return (book&&Array.isArray(book.asks)?book.asks:[]).map(row=>({price:Number(row.price),size:Number(row.size)}))
|
||
.filter(row=>Number.isFinite(row.price)&&row.price>0&&row.price<1&&Number.isFinite(row.size)&&row.size>0)
|
||
.sort((a,b)=>a.price-b.price);
|
||
}
|
||
function bundleExecutableAsk(book,units){
|
||
const result=bundleExecutableLeg(book,units,{rate:0,exponent:1,takerOnly:true});return result&&result.ask_price;
|
||
}
|
||
function bundleExecutableLeg(book,units,feeSchedule){
|
||
let remaining=Number(units),cost=0,fees=0,feeConfigValid=Boolean(feeSchedule);if(!(remaining>0))return null;
|
||
for(const level of bundleBookAskLevels(book)){
|
||
const filled=Math.min(remaining,level.size),fee=bundleFeePerShare(feeSchedule,level.price);
|
||
cost+=filled*level.price;if(Number.isFinite(fee))fees+=filled*fee;else feeConfigValid=false;
|
||
remaining-=filled;if(remaining<=1e-9)break;
|
||
}
|
||
if(remaining>1e-9)return null;
|
||
const askPrice=cost/Number(units),feePerShare=feeConfigValid?fees/Number(units):SIGNAL_ROUND_TRIP_COST;
|
||
return {ask_price:askPrice,fee_per_share:feePerShare,total_price:askPrice+feePerShare,fee_config_valid:feeConfigValid};
|
||
}
|
||
async function fetchBundleBooks(tokenIds){
|
||
const books=new Map(),unique=[...new Set((tokenIds||[]).map(String).filter(Boolean))];
|
||
for(let index=0;index<unique.length;index+=BUNDLE_BOOK_BATCH_SIZE){
|
||
const batch=unique.slice(index,index+BUNDLE_BOOK_BATCH_SIZE),response=await fetchWithTimeout(`${CLOB}/books`,{
|
||
method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(batch.map(token_id=>({token_id}))),
|
||
});
|
||
if(!response.ok)throw new Error(`Polymarket order-book API ${response.status}`);
|
||
const payload=await response.json();(Array.isArray(payload)?payload:[]).forEach(book=>books.set(String(book.asset_id||""),book));
|
||
}
|
||
return books;
|
||
}
|
||
async function fetchBundleFeeRates(tokenIds){
|
||
const unique=[...new Set((tokenIds||[]).map(String).filter(Boolean))],results=new Map();
|
||
let cursor=0;
|
||
const workers=Array.from({length:Math.min(BUNDLE_FEE_CONCURRENCY,unique.length)},async()=>{
|
||
while(cursor<unique.length){
|
||
const token=unique[cursor++];
|
||
try{
|
||
const response=await fetchWithTimeout(`${CLOB}/fee-rate?token_id=${encodeURIComponent(token)}`);
|
||
if(!response.ok){results.set(token,null);continue;}
|
||
const payload=await response.json(),rate=Number(payload&&payload.base_fee);results.set(token,Number.isFinite(rate)?rate:null);
|
||
}catch(e){results.set(token,null);}
|
||
}
|
||
});
|
||
await Promise.all(workers);return results;
|
||
}
|
||
async function verifyExecutableBundles(candidates){
|
||
const shortlist=(candidates||[]).slice().sort((a,b)=>Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity)).slice(0,BUNDLE_DEPTH_CANDIDATE_LIMIT);
|
||
const tokenIds=shortlist.flatMap(candidate=>(candidate.bundle_legs||[]).map(leg=>leg.token_id));
|
||
const books=await fetchBundleBooks(tokenIds),depthChecked=shortlist.map(candidate=>{
|
||
const estimatedCost=Number(candidate.bundle_cost_per_unit),requiredUnits=+(Math.max(BUNDLE_MIN_UNITS,BUNDLE_MIN_NOTIONAL/Math.max(estimatedCost,0.01))).toFixed(2);
|
||
const legs=(candidate.bundle_legs||[]).map(leg=>{
|
||
const book=books.get(String(leg.token_id||"")),minimum=Number(book&&book.min_order_size||0),units=Math.max(requiredUnits,minimum);
|
||
const executable=bundleExecutableLeg(book,units,leg.fee_schedule);
|
||
return Object.assign({},leg,{required_units:+units.toFixed(2),ask_vwap:executable?+executable.ask_price.toFixed(6):null,
|
||
fee_per_share:executable?+executable.fee_per_share.toFixed(6):null,entry_price:executable?+executable.total_price.toFixed(6):null,
|
||
fee_config_valid:Boolean(executable&&executable.fee_config_valid),depth_verified:Boolean(executable)});
|
||
});
|
||
const commonUnits=Math.max(requiredUnits,...legs.map(leg=>Number(leg.required_units||0)));
|
||
const repricedLegs=legs.map(leg=>{
|
||
if(Number(leg.required_units)===commonUnits)return leg;
|
||
const executable=bundleExecutableLeg(books.get(String(leg.token_id||"")),commonUnits,leg.fee_schedule);
|
||
return Object.assign({},leg,{required_units:+commonUnits.toFixed(2),ask_vwap:executable?+executable.ask_price.toFixed(6):null,
|
||
fee_per_share:executable?+executable.fee_per_share.toFixed(6):null,entry_price:executable?+executable.total_price.toFixed(6):null,
|
||
fee_config_valid:Boolean(executable&&executable.fee_config_valid),depth_verified:Boolean(executable)});
|
||
});
|
||
const depthVerified=repricedLegs.length>=2&&repricedLegs.every(leg=>leg.depth_verified),cost=depthVerified?repricedLegs.reduce((sum,leg)=>sum+Number(leg.entry_price),0):Infinity;
|
||
const payout=Number(candidate.bundle_payout_per_unit),profit=payout-cost,netReturn=Number.isFinite(cost)&&cost>0?profit/cost:-Infinity;
|
||
return Object.assign({},candidate,{bundle_legs:repricedLegs,execution_units:+commonUnits.toFixed(2),execution_notional:Number.isFinite(cost)?+(cost*commonUnits).toFixed(2):null,
|
||
bundle_cost_per_unit:Number.isFinite(cost)?+cost.toFixed(6):null,bundle_net_profit_per_unit:Number.isFinite(profit)?+profit.toFixed(6):null,
|
||
edge:Number.isFinite(netReturn)?+netReturn.toFixed(6):null,net_edge:Number.isFinite(netReturn)?+netReturn.toFixed(6):null,
|
||
depth_verified:depthVerified,fees_verified:false,trade_ready:false,entry_candidate:false,opportunity_actionable:false,watch_only:true,audited_observation_only:true,
|
||
execution_model:"live-order-book-vwap",verification_status:depthVerified?"depth-verified":"insufficient-depth"});
|
||
});
|
||
const potentiallyActionable=depthChecked.filter(candidate=>candidate.depth_verified
|
||
&&Number(candidate.bundle_net_profit_per_unit)>=NEG_RISK_MIN_NET_PROFIT&&Number(candidate.net_edge)>=NEG_RISK_MIN_NET_RETURN
|
||
&&(candidate.bundle_legs||[]).every(leg=>leg.fee_config_valid));
|
||
const reportedFees=await fetchBundleFeeRates(potentiallyActionable.flatMap(candidate=>candidate.bundle_legs.map(leg=>leg.token_id)));
|
||
return depthChecked.map(candidate=>{
|
||
const feeConfigValid=(candidate.bundle_legs||[]).every(leg=>leg.fee_config_valid);
|
||
const feesVerified=feeConfigValid&&(candidate.bundle_legs||[]).every(leg=>{
|
||
const reported=reportedFees.get(String(leg.token_id));return Number.isFinite(reported)&&(leg.fees_enabled===false?reported===0:reported>0);
|
||
});
|
||
const actionable=candidate.depth_verified&&feesVerified
|
||
&&Number(candidate.bundle_net_profit_per_unit)>=NEG_RISK_MIN_NET_PROFIT&&Number(candidate.net_edge)>=NEG_RISK_MIN_NET_RETURN;
|
||
const feeState=!feeConfigValid?"fee-schedule-unavailable":(feesVerified?"verified-market-specific":"fee-verification-unavailable");
|
||
const friction=(candidate.bundle_legs||[]).reduce((sum,leg)=>sum+Number(leg.fee_per_share||0),0);
|
||
return Object.assign({},candidate,{fees_verified:feesVerified,fee_model:feeState,trade_ready:actionable,entry_candidate:actionable,
|
||
opportunity_actionable:actionable,watch_only:!actionable,audited_observation_only:!actionable,
|
||
verification_status:actionable?"executable":(candidate.depth_verified?feeState:candidate.verification_status),
|
||
friction:+friction.toFixed(6),drivers:[...(candidate.drivers||[]).filter(driver=>driver!=="positive margin after estimated costs"),
|
||
"live order-book VWAP",feesVerified?"CLOB-verified fee schedule":"fee check blocks entry"],
|
||
rationale:`${candidate.rationale} Live verification modeled ${Number(candidate.execution_units||0).toFixed(2)} equal units (${candidate.execution_notional==null?"unavailable":fmtUSD(candidate.execution_notional)}) across every leg and applied the taker fee curve at each consumed ask. ${actionable?`All legs had sufficient depth and their CLOB fee state matched Gamma; executable worst-case profit is ${Number(candidate.bundle_net_profit_per_unit).toFixed(4)} per unit after ${friction.toFixed(4)} in fees per unit.`:`Entry is blocked: ${candidate.depth_verified?feeState:"one or more legs lacked sufficient ask depth"}.`}`});
|
||
}).sort((a,b)=>Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity));
|
||
}
|
||
async function fetchNegativeRiskBundles(limit=NEG_RISK_EVENT_SCAN_LIMIT){
|
||
const events=[],pageSize=100;
|
||
for(let offset=0;offset<limit;offset+=pageSize){
|
||
const size=Math.min(pageSize,limit-offset),params=new URLSearchParams({active:"true",closed:"false",archived:"false",limit:String(size),offset:String(offset),order:"volume24hr",ascending:"false"});
|
||
const response=await fetchWithTimeout(`${GAMMA}/events?${params}`);
|
||
if(!response.ok)throw new Error(`Polymarket event API ${response.status}`);
|
||
const page=await response.json();if(!Array.isArray(page)||!page.length)break;
|
||
events.push(...page);if(page.length<size)break;
|
||
}
|
||
const audited=events.flatMap(event=>{
|
||
const complete=negativeRiskBundleSuggestion(event,{includeUnprofitable:true});
|
||
return [...(complete?[complete]:[]),...dominanceBundleSuggestions(event,{includeUnprofitable:true})];
|
||
}).sort((a,b)=>b.net_edge-a.net_edge);
|
||
const executableAudit=await verifyExecutableBundles(audited);
|
||
const candidates=executableAudit.filter(candidate=>candidate.opportunity_actionable);
|
||
const seen=new Set();
|
||
const unique=candidates.filter(candidate=>{const id=String(candidate.bundle_id||candidate.market_id);if(!id||seen.has(id))return false;seen.add(id);return true;});
|
||
const closest=executableAudit.find(candidate=>candidate.depth_verified)||executableAudit[0]||null;
|
||
return {candidates:unique.slice(0,20),audit:{status:"ok",scanned_at:nowIso(),event_scan_limit:limit,events_scanned:events.length,
|
||
evaluated_structures:audited.length,depth_checked_structures:executableAudit.length,depth_verified_structures:executableAudit.filter(candidate=>candidate.depth_verified).length,
|
||
fee_verified_structures:executableAudit.filter(candidate=>candidate.fees_verified).length,actionable_bundles:unique.length,
|
||
closest_margin_cents:closest?+(Number(closest.bundle_net_profit_per_unit)*100).toFixed(2):null,
|
||
closest_return_pct:closest?+(Number(closest.net_edge)*100).toFixed(3):null,
|
||
closest_title:closest?closest.question:null,closest_url:closest?closest.url:null}};
|
||
}
|
||
async function fetchRewardMakerCandidates(limit=60){
|
||
const params=new URLSearchParams({limit:String(limit),min_hours:"48"});
|
||
const response=await fetchWithTimeout(`/api/liquidity?${params}`,{},NETWORK_REQUEST_TIMEOUT_MS*2);
|
||
const payload=await response.json().catch(()=>({}));
|
||
if(!response.ok||!payload.ok)throw new Error(payload.error||`Liquidity audit ${response.status}`);
|
||
return Array.isArray(payload.candidates)?payload.candidates:[];
|
||
}
|
||
|
||
/* ---------- Analysis engine ---------- */
|
||
const W={LIQ:0.24,MOM:0.18,SIGNAL:0.38,TIME:0.20};
|
||
const MIN_VOLUME=18000;
|
||
const MIN_LIQUIDITY=1400;
|
||
const MIN_SCOUT_VOLUME=1500;
|
||
const MIN_SCOUT_LIQUIDITY=150;
|
||
const MIN_SCOUT_EDGE=0.006;
|
||
const MIN_ENTRY_EDGE=0.028;
|
||
const MIN_SELECTIVE_ENTRY_EDGE=0.022;
|
||
const MIN_TREND_EDGE=0.018;
|
||
const MIN_LIQUIDITY_EDGE=0.020;
|
||
const MIN_CORE_PROBE_EDGE=0.024;
|
||
const MIN_AGGRESSIVE_EDGE=0.016;
|
||
const MIN_ALLOCATION_EDGE=MIN_AGGRESSIVE_EDGE;
|
||
const MIN_ENTRY_DAYS=3.0;
|
||
const EDGE_SCALE=0.08;
|
||
const MAX_STRATEGY_POSITIONS=30;
|
||
const MAX_NEW_POSITION_PCT=0.06;
|
||
const MAX_PROBE_POSITION_PCT=0.015;
|
||
const MAX_CORE_ALLOCATION_PCT=0.04;
|
||
const MAX_AGGRESSIVE_ALLOCATION_PCT=0.045;
|
||
const MAX_CATEGORY_EXPOSURE_PCT=0.25;
|
||
const MAX_CYCLE_DRAWDOWN_PCT=4;
|
||
const MAX_CORE_TRADE_LOSS_PCT=0.04;
|
||
const MAX_AGGRESSIVE_TRADE_LOSS_PCT=0.05;
|
||
const MAX_CORE_GAP_TRADE_LOSS_PCT=0.025;
|
||
const MAX_AGGRESSIVE_GAP_TRADE_LOSS_PCT=0.03;
|
||
const STOP_COOLDOWN_HOURS=72;
|
||
const MAX_ACTIVE_MARKET_PAGES=1000;
|
||
const MARKET_ANALYSIS_LIMIT=500;
|
||
const ACTIVE_MARKET_FETCH_LIMIT=500;
|
||
const SUGGESTION_TOTAL=900;
|
||
const SUGGESTION_PER_CATEGORY=180;
|
||
const VISIBLE_SUGGESTIONS=900;
|
||
const REAL_WORLD_SIGNAL_LIMIT=120;
|
||
const SIGNAL_ROUND_TRIP_COST=0.005;
|
||
const SIGNAL_UNKNOWN_FEE_FALLBACK=0.04;
|
||
const NEG_RISK_EVENT_SCAN_LIMIT=1000;
|
||
const NEG_RISK_MIN_LIQUIDITY=1000;
|
||
const NEG_RISK_MIN_NET_PROFIT=0.003;
|
||
const NEG_RISK_MIN_NET_RETURN=0.0015;
|
||
const BUNDLE_BOOK_BATCH_SIZE=200;
|
||
const BUNDLE_DEPTH_CANDIDATE_LIMIT=60;
|
||
const BUNDLE_FEE_CONCURRENCY=8;
|
||
const BUNDLE_MIN_NOTIONAL=50;
|
||
const BUNDLE_MIN_UNITS=5;
|
||
const SPORTS_FAVORITE_MIN_ENTRY=0.03;
|
||
const SPORTS_FAVORITE_MAX_ENTRY=0.97;
|
||
const SPORTS_FAVORITE_TARGET_LEAD_HOURS=72;
|
||
const SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS=12;
|
||
const SPORTS_FAVORITE_PILOT_POSITION_PCT=0.005;
|
||
const SPORTS_FAVORITE_PROMOTED_POSITION_PCT=0.0075;
|
||
const SPORTS_FAVORITE_MAX_POSITION_PCT=0.01;
|
||
const SPORTS_FAVORITE_TOTAL_CAPITAL_PCT=0.03;
|
||
const SPORTS_FAVORITE_VERDICT_EVENTS=15;
|
||
const SPORTS_FAVORITE_PROMOTION_EVENTS=20;
|
||
const SPORTS_FAVORITE_MAX_PROMOTION_EVENTS=40;
|
||
const SPORTS_FAVORITE_MAX_NEW_PER_CYCLE=1;
|
||
const SPORTS_FAVORITE_SHADOW_LIMIT=20;
|
||
const SPORTS_FAVORITE_OUTCOME_LIMIT=240;
|
||
const SPORTS_FAVORITE_SLIPPAGE_BUFFER=0.0025;
|
||
const SPORTS_FAVORITE_MAX_EXECUTION_COST=0.04;
|
||
const SHOCK_FADE_MIN_MOVE=0.08;
|
||
const SHOCK_FADE_SIGNAL_WINDOW_HOURS=3;
|
||
const SHOCK_FADE_EXIT_HOURS=12;
|
||
const SHOCK_FADE_GRADE_TOLERANCE_HOURS=2;
|
||
const SHOCK_FADE_INITIAL_POSITION_PCT=0.01;
|
||
const SHOCK_FADE_PROMOTED_POSITION_PCT=0.015;
|
||
const SHOCK_FADE_TOTAL_CAPITAL_PCT=0.05;
|
||
const SHOCK_FADE_MAX_NEW_PER_AGENT_CYCLE=2;
|
||
const SHOCK_FADE_VERDICT_EVENTS=20;
|
||
const SHOCK_FADE_QUALIFICATION_EVENTS=40;
|
||
const SHOCK_FADE_PROMOTION_EVENTS=80;
|
||
const SHOCK_FADE_SHADOW_LIMIT=30;
|
||
const SHOCK_FADE_OUTCOME_LIMIT=300;
|
||
const SHOCK_FADE_SLIPPAGE_BUFFER=0.0025;
|
||
const SHOCK_FADE_MAX_EXECUTION_COST=0.01;
|
||
const SHOCK_FADE_MIN_ENTRY=0.08;
|
||
const SHOCK_FADE_MAX_ENTRY=0.92;
|
||
const SHOCK_FADE_HISTORY_CACHE_MS=10*60*1000;
|
||
const SHOCK_FADE_BACKTEST_APPROVED=false;
|
||
const SHOCK_FADE_ADOPTER_IDS=new Set(["catalyst","reversal","breakout","tailalpha","conviction"]);
|
||
const RESOLUTION_WEEK_NO_ADOPTER_IDS=new Set(["catalyst","reversal","breakout","tailalpha","conviction"]);
|
||
const RESOLUTION_WEEK_NO_WINDOWS=Object.freeze([
|
||
{days:4,min:3.5,max:4.5},
|
||
]);
|
||
const RESOLUTION_WEEK_NO_AUDIT=Object.freeze({
|
||
4:{events:145,eventMean:0.3842,lower:0.2612,trainEvents:102,trainLower:0.2909,holdoutEvents:46,holdoutLower:0.0383},
|
||
});
|
||
const RESOLUTION_WEEK_NO_MIN_ENTRY=0.50;
|
||
const RESOLUTION_WEEK_NO_MAX_ENTRY=0.55;
|
||
const RESOLUTION_WEEK_NO_MAX_EXECUTABLE_ENTRY=0.56;
|
||
const RESOLUTION_WEEK_NO_POSITION_PCT=0.005;
|
||
const RESOLUTION_WEEK_NO_PROMOTED_POSITION_PCT=0.005;
|
||
const RESOLUTION_WEEK_NO_TOTAL_CAPITAL_PCT=0.03;
|
||
const RESOLUTION_WEEK_NO_MAX_NEW_PER_AGENT_CYCLE=1;
|
||
const RESOLUTION_WEEK_NO_VERDICT_EVENTS=20;
|
||
const RESOLUTION_WEEK_NO_PROMOTION_EVENTS=40;
|
||
const RESOLUTION_WEEK_NO_SHADOW_LIMIT=30;
|
||
const RESOLUTION_WEEK_NO_OUTCOME_LIMIT=300;
|
||
const RESOLUTION_WEEK_NO_SLIPPAGE_BUFFER=0.0025;
|
||
const RESOLUTION_WEEK_NO_MAX_EXECUTION_COST=0.01;
|
||
const RESOLUTION_WEEK_NO_BACKTEST_APPROVED=false;
|
||
const MAKER_MAX_QUOTES=6;
|
||
const MAKER_MAX_QUOTE_CAPITAL_PCT=0.02;
|
||
const MAKER_TOTAL_CAPITAL_PCT=0.10;
|
||
const MAKER_QUOTE_EXPIRY_HOURS=24;
|
||
const MAKER_SHADOW_HORIZON_HOURS=1;
|
||
const MAKER_MIN_LOCK_MARGIN=0.005;
|
||
const MAKER_OUTCOME_LIMIT=240;
|
||
const MAKER_MIN_COHORT_ATTEMPTS=20;
|
||
const MAKER_MIN_PROMOTION_LOCKS=3;
|
||
const MAKER_TOUCH_FIDELITY_MINUTES=1;
|
||
const MAKER_CAPITAL_BACKTEST_APPROVED=false;
|
||
const clamp=(x,a,b)=>Math.max(a,Math.min(b,x));
|
||
function liquiditySignal(m){const vol=Math.min(1,Math.log10(m.volume+1)/6.0);const liq=Math.min(1,Math.log10(m.liquidity+1)/5.3);return 0.6*vol+0.4*liq;}
|
||
function momentumSignal(m){const da=m.volume_1wk?m.volume_1wk/7:0;if(da<=0)return m.volume_24hr>0?0.3:0;return clamp((m.volume_24hr/da-0.5)/2.0,0,1);}
|
||
function categoryPolicy(cat){
|
||
return ({
|
||
Politics:{minEdge:0.022,minVol:22000,minLiq:1800,minEvidence:0.48,uncertainty:0.005,label:"politics needs confirmed price action and outside context"},
|
||
Sports:{minEdge:0.024,minVol:26000,minLiq:2200,minEvidence:0.55,uncertainty:0.007,label:"sports needs strong confirmation and low settlement-gap risk"},
|
||
Crypto:{minEdge:0.024,minVol:28000,minLiq:2400,minEvidence:0.54,uncertainty:0.007,label:"crypto needs confirmation after jump-risk penalties"},
|
||
Economy:{minEdge:0.023,minVol:22000,minLiq:1800,minEvidence:0.50,uncertainty:0.006,label:"macro markets need strong evidence"},
|
||
"Pop Culture":{minEdge:0.020,minVol:16000,minLiq:1300,minEvidence:0.46,uncertainty:0.004,label:"culture markets need attention confirmation"},
|
||
Other:{minEdge:0.025,minVol:24000,minLiq:1900,minEvidence:0.52,uncertainty:0.007,label:"uncategorized markets need the highest proof"},
|
||
})[cat||"Other"]||({minEdge:0.025,minVol:24000,minLiq:1900,minEvidence:0.52,uncertainty:0.007,label:"uncategorized markets need the highest proof"});
|
||
}
|
||
function frictionPenalty(m){
|
||
const liq=liquiditySignal(m);
|
||
const price=Math.min(m.yes_price,m.no_price);
|
||
const thinPenalty=0.016*(1-liq);
|
||
const tailPenalty=price<0.12?0.007:0;
|
||
const lowLiqPenalty=m.liquidity<MIN_LIQUIDITY?0.008:0;
|
||
return clamp(thinPenalty+tailPenalty+lowLiqPenalty,0.002,0.034);
|
||
}
|
||
function chasePenalty(m,rawEdge){
|
||
const day=Math.abs(Number(m.price_change_1d||0)),hour=Math.abs(Number(m.price_change_1h||0));
|
||
if(day<0.045&&hour<0.012)return 0;
|
||
return clamp(Math.max(0,day-0.045)*0.20+Math.max(0,hour-0.012)*0.35,0,0.024);
|
||
}
|
||
function textEvidenceSignal(m,external={}){
|
||
const text=`${m.question||""} ${(m.tags||[]).join(" ")}`.toLowerCase();
|
||
let evidence=0.30;
|
||
const drivers=[];
|
||
if(/\b(poll|approval|election|primary|senate|house|minister|president)\b/.test(text)){evidence+=0.08;drivers.push("political-event context");}
|
||
if(/\b(injury|starter|lineup|playoff|final|championship|tournament|wins?|score)\b/.test(text)){evidence+=0.09;drivers.push("sports-event context");}
|
||
if(/\b(bitcoin|btc|ethereum|eth|solana|crypto|etf|fed|cpi|inflation|rates?)\b/.test(text)){evidence+=0.08;drivers.push("macro/crypto context");}
|
||
if(/\b(release|box office|views|album|movie|stream|award)\b/.test(text)){evidence+=0.06;drivers.push("culture-attention context");}
|
||
if(m.days_to_resolution!=null&&m.days_to_resolution<=14){evidence+=0.07;drivers.push("near-term resolution");}
|
||
if(m.volume_24hr>1500){evidence+=0.05;drivers.push("fresh market activity");}
|
||
if(external.source_count){evidence+=Math.min(0.20,0.05*external.source_count);drivers.push(`${external.source_count} recent context hit${external.source_count===1?"":"s"} (coverage only; direction comes from market confirmation)`);}
|
||
if(external.latest_title)drivers.push(`latest context: ${external.latest_title.slice(0,72)}${external.latest_title.length>72?"...":""}`);
|
||
return {score:clamp(evidence,0,1),drivers,source_count:external.source_count||0,latest_title:external.latest_title||""};
|
||
}
|
||
async function fetchRealWorldSignals(markets){
|
||
const selected=[...markets].sort((a,b)=>(b.volume_24hr-a.volume_24hr)||(b.volume-a.volume)).slice(0,REAL_WORLD_SIGNAL_LIMIT);
|
||
try{
|
||
const r=await fetchWithTimeout("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({
|
||
action:"market_context",
|
||
markets:selected.map(m=>({id:m.id,question:m.question,category:m.category,tags:m.tags})),
|
||
})},NETWORK_REQUEST_TIMEOUT_MS);
|
||
const d=await r.json().catch(()=>({}));
|
||
if(!r.ok||!d.ok)return {};
|
||
return d.signals||{};
|
||
}catch(e){return {};}
|
||
}
|
||
function confirmedMarketSignal(m,evidence={score:0,source_count:0}){
|
||
const hour=Number(m.price_change_1h||0),day=Number(m.price_change_1d||0),week=Number(m.price_change_1w||0);
|
||
const daySign=Math.sign(day),weekSign=Math.sign(week),hourSign=Math.sign(hour);
|
||
const trendAligned=daySign&&daySign===weekSign&&Math.abs(day)>=0.006&&Math.abs(week)>=0.012
|
||
&&Math.abs(day)<=0.08&&Math.abs(week)<=0.18&&(!hourSign||hourSign===daySign||Math.abs(hour)<0.008);
|
||
const reversalConfirmed=daySign&&Math.abs(day)>=0.04&&Math.abs(day)<=0.18&&hourSign===-daySign&&Math.abs(hour)>=0.004
|
||
&&(!weekSign||weekSign!==daySign||Math.abs(week)<Math.abs(day)*1.6);
|
||
if(!trendAligned&&!reversalConfirmed)return {side:null,type:"none",strength:0,gross_move:0,confidence:0,confirmed:false};
|
||
const type=reversalConfirmed?"reversal":"trend";
|
||
const sign=reversalConfirmed?-daySign:daySign;
|
||
const liq=liquiditySignal(m),activity=momentumSignal(m);
|
||
const moveStrength=type==="trend"
|
||
?clamp(0.30+Math.abs(day)*4+Math.abs(week)*1.5+(daySign===weekSign?0.16:0),0,1)
|
||
:clamp(0.30+Math.abs(day)*3.2+Math.abs(hour)*8,0,1);
|
||
const confidence=clamp(moveStrength*0.48+liq*0.22+activity*0.12+Number(evidence.score||0)*0.18,0,1);
|
||
const side=sign>0?"YES":"NO",availableMove=side==="YES"?1-Number(m.yes_price||0):Number(m.yes_price||0);
|
||
const gross=Math.min(type==="trend"?0.018+moveStrength*0.052:0.016+moveStrength*0.045,Math.max(0,availableMove)*0.80);
|
||
return {side,type,strength:moveStrength,gross_move:gross,confidence,confirmed:confidence>=0.56};
|
||
}
|
||
function fairValue(m,external={}){
|
||
const evidence=textEvidenceSignal(m,external),signal=confirmedMarketSignal(m,evidence);
|
||
if(!signal.side)return m.yes_price;
|
||
const direction=signal.side==="YES"?1:-1;
|
||
return clamp(m.yes_price+direction*signal.gross_move,0.03,0.97);
|
||
}
|
||
function quickMarketGap(m){
|
||
if(!m||!Number.isFinite(m.yes_price))return 0;
|
||
return Math.abs(fairValue(m)-m.yes_price);
|
||
}
|
||
function marketAnalysisScore(m){
|
||
const activity=Math.log10((m.volume_24hr||0)*8+(m.volume||0)+1);
|
||
const liquidity=Math.log10((m.liquidity||0)+1);
|
||
const gap=quickMarketGap(m);
|
||
const timing=timingSignal(m.days_to_resolution);
|
||
return activity*18+liquidity*9+gap*260+timing*8;
|
||
}
|
||
function selectMarketsForAnalysis(markets,limit=MARKET_ANALYSIS_LIMIT){
|
||
return [...markets]
|
||
.filter(m=>m&&m.question&&m.volume>=MIN_SCOUT_VOLUME&&m.liquidity>=MIN_SCOUT_LIQUIDITY)
|
||
.sort((a,b)=>marketAnalysisScore(b)-marketAnalysisScore(a))
|
||
.slice(0,limit);
|
||
}
|
||
function timingSignal(d){if(d==null)return 0.4;if(d<1)return 0.1;if(d<=3)return 0.5;if(d<=90)return 1.0-(d-3)/87.0*0.4;return 0.35;}
|
||
function hardSettlementJumpRisk(m){
|
||
const text=`${m&&m.question||""} ${m&&m.event||""}`.toLowerCase();
|
||
const numericRange=/\b\d+(?:\.\d+)?\s*(?:%|percent)?\s*(?:-|–|—|to)\s*\d+(?:\.\d+)?\s*(?:%|percent|votes?|points?|seats?|bps|basis points?|tweets?|posts?|goals?)(?![a-z])/;
|
||
const currencyRange=/[$€£]\d+(?:\.\d+)?\s*(?:-|–|—|to)\s*[$€£]?\d+(?:\.\d+)?/;
|
||
const betweenRange=/\bbetween\s+[$€£]?\d+(?:\.\d+)?\s*(?:%|percent)?\s+(?:and|to)\s+[$€£]?\d+(?:\.\d+)?/;
|
||
const pathDependentBarrier=/\b(?:reach|hit|touch|dip(?:\s+(?:to|below))?|rise\s+(?:to|above)|fall\s+(?:to|below))\b[^?]{0,45}(?:[$€£]\s*)?\d[\d,]*(?:\.\d+)?\s*(?:%|percent|k|m|b|points?|bps|basis points?)?/;
|
||
return numericRange.test(text)||currencyRange.test(text)||betweenRange.test(text)||pathDependentBarrier.test(text)
|
||
||/\bexact score\b|\bscore:\s*\d|\bposts? \d+-\d+|\bnumber of (tweets|posts)\b/.test(text);
|
||
}
|
||
function fastSettlementRisk(m){
|
||
const text=`${m&&m.question||""} ${m&&m.event||""}`.toLowerCase();
|
||
if(hardSettlementJumpRisk(m))return true;
|
||
if(/\bvs\.?\b/.test(text))return true;
|
||
if((m&&m.category)==="Sports"){
|
||
if(/\bwin on \d{4}-\d{2}-\d{2}\b|\bmatch winner\b/.test(text))return true;
|
||
if(m.days_to_resolution==null||m.days_to_resolution<14)return true;
|
||
}
|
||
if((m&&m.category)==="Crypto"&&(m.days_to_resolution==null||m.days_to_resolution<10)
|
||
&&/\b(reach|dip|above|below|between|hit|price of)\b/.test(text))return true;
|
||
return false;
|
||
}
|
||
function directionalPriceSignal(m){
|
||
const signal=confirmedMarketSignal(m,textEvidenceSignal(m));
|
||
return {side:signal.side,strength:signal.strength,aligned:signal.type==="trend"&&signal.confirmed,type:signal.type};
|
||
}
|
||
function effectiveEntryEdge(s){
|
||
const net=Math.abs(Number(s&&s.net_edge));
|
||
return Number.isFinite(net)?net:0;
|
||
}
|
||
function isGapProneTrade(s={}){
|
||
const days=Number(s.days_to_resolution),entry=Number(s.entry_price),dayMove=Math.abs(Number(s.price_change_1d||0));
|
||
const hasDays=s.days_to_resolution!=null&&s.days_to_resolution!==""&&Number.isFinite(days);
|
||
return (hasDays&&days<=14)||(Number.isFinite(entry)&&(entry<=0.22||entry>=0.80))
|
||
||dayMove>=0.045||s.signal_type==="reversal"||Boolean(s.jump_risk);
|
||
}
|
||
function maxAgentOverlapForTrade(s={}){return (isGapProneTrade(s)||(s.category||"Other")==="Crypto")?1:2;}
|
||
function tradeLossBudgetPct(cfg,s={}){
|
||
const aggressive=Boolean(cfg&&cfg.aggressive);
|
||
let cap=aggressive?MAX_AGGRESSIVE_TRADE_LOSS_PCT:MAX_CORE_TRADE_LOSS_PCT;
|
||
const gapProne=isGapProneTrade(s);
|
||
if(gapProne)cap=Math.min(cap,aggressive?MAX_AGGRESSIVE_GAP_TRADE_LOSS_PCT:MAX_CORE_GAP_TRADE_LOSS_PCT);
|
||
return +cap.toFixed(4);
|
||
}
|
||
function boundedStakeForRisk(eq,stake,cfg,s){
|
||
return Math.max(0,Math.min(Number(stake||0),Number(eq||0)*tradeLossBudgetPct(cfg,s)));
|
||
}
|
||
function resolutionWeekNoSuggestion(m){
|
||
if(!m||m.category==="Sports"||m.accepting_orders===false||m.closed||hardSettlementJumpRisk(m))return null;
|
||
const days=Number(m.days_to_resolution),marketPrice=Number(m.no_price),spread=Number(m.spread);
|
||
const window=RESOLUTION_WEEK_NO_WINDOWS.find(item=>Number.isFinite(days)&&days>=item.min&&days<=item.max);if(!window)return null;
|
||
if(marketPrice<RESOLUTION_WEEK_NO_MIN_ENTRY||marketPrice>=RESOLUTION_WEEK_NO_MAX_ENTRY)return null;
|
||
if(!(spread>0&&spread<=0.03)||Number(m.volume)<15000||Number(m.liquidity)<1400)return null;
|
||
const quotedAsk=Number(m.best_bid)>0?1-Number(m.best_bid):NaN;
|
||
const fallbackAsk=marketPrice+Math.max(0.0025,spread/2);
|
||
const executableAsk=Number.isFinite(quotedAsk)&"edAsk>=marketPrice&"edAsk<1?quotedAsk:fallbackAsk;
|
||
const modeledEntry=clamp(executableAsk+RESOLUTION_WEEK_NO_SLIPPAGE_BUFFER,0.01,0.99);
|
||
const executionCost=Math.max(0,modeledEntry-marketPrice);
|
||
if(modeledEntry>RESOLUTION_WEEK_NO_MAX_EXECUTABLE_ENTRY||executionCost>RESOLUTION_WEEK_NO_MAX_EXECUTION_COST+0.00001)return null;
|
||
const eventKey=String(m.event_id||m.event||m.id),conviction=+clamp(72+Math.log10(Math.max(1,m.volume_24hr||m.volume))*1.2+Math.log10(Math.max(1,m.liquidity)),72,82).toFixed(1);
|
||
const audit=RESOLUTION_WEEK_NO_AUDIT[window.days];
|
||
return {market_id:m.id,question:m.question,event:m.event,event_key:eventKey,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:m.yes_price,no_price:m.no_price,fair_value:+modeledEntry.toFixed(4),edge:0,net_edge:0,
|
||
friction:+executionCost.toFixed(4),chase_penalty:0,evidence_score:0.72,evidence_source_count:0,quality:"resolution-week-no-pilot",side:"NO",
|
||
market_price:+marketPrice.toFixed(4),entry_price:+modeledEntry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,
|
||
spread:m.spread,price_change_1h:m.price_change_1h,price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,
|
||
momentum_strength:0,signal_strength:0.72,signal_confidence:0.72,signal_type:"resolution-week-no-pilot",
|
||
trade_ready:false,entry_candidate:false,audited_observation_only:true,adaptive_promotion:false,watch_only:true,jump_risk:false,requires_live:false,
|
||
days_to_resolution:+days.toFixed(1),target_horizon_days:window.days,end_date:m.end_date,resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,
|
||
pilot_prior:{recent_markets:2916,recent_events:audit.events,recent_event_mean:audit.eventMean,recent_event_lower_90:audit.lower,
|
||
train_events:audit.trainEvents,train_lower_90:audit.trainLower,holdout_events:audit.holdoutEvents,holdout_lower_90:audit.holdoutLower,modeled_cost_cents:1,regime_specific:true},
|
||
drivers:[`corrected ${window.days}-day discovery window`,"NO midpoint is 50–55%","executable NO ask plus 0.25c slippage","one observation per event","zero capital until forward promotion"],
|
||
rationale:`Zero-capital forward learner: NO is priced at ${pct(marketPrice)} for a safe non-Sports contract resolving in ${days.toFixed(1)} days, inside the corrected ${window.days}-day discovery window. The modeled entry is ${pct(modeledEntry)} using the executable NO ask plus 0.25-cent slippage, ${(executionCost*100).toFixed(2)} cents above midpoint. A corrected recent replay selected only one highest-volume eligible contract per event and found ${audit.events} observations with a ${(audit.eventMean*100).toFixed(2)}% mean and ${(audit.lower*100).toFixed(2)}% lower 90% bound, but a disjoint older holdout failed the all-segment confidence gate. No capital is allowed until 40 independent forward settlements produce a positive lower confidence bound above 1%.`};
|
||
}
|
||
function sportsFavoriteLeadHours(item){return hoursUntil(item&&item.game_start);}
|
||
function sportsContestKey(m){
|
||
const slug=String(m&&m.market_slug||"").toLowerCase(),dated=slug.match(/^(.+?-\d{4}-\d{2}-\d{2})(?:-|$)/);
|
||
if(dated)return `sports:${dated[1]}`;
|
||
const start=normalizeApiIso(m&&m.game_start)||"unknown-start";
|
||
const title=String(m&&m.event||m&&m.question||"").toLowerCase().split(":")[0]
|
||
.replace(/\b(will|win|exact score|leading at halftime|to score first)\b/g," ").replace(/[^a-z0-9]+/g," ").trim();
|
||
return `sports:${start}:${title||String(m&&m.id||"unknown")}`;
|
||
}
|
||
function sportsFavoritePilotSuggestion(m){
|
||
if(!m||m.category!=="Sports"||!m.game_start)return null;
|
||
const lead=sportsFavoriteLeadHours(m),minLead=SPORTS_FAVORITE_TARGET_LEAD_HOURS-SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS;
|
||
const maxLead=SPORTS_FAVORITE_TARGET_LEAD_HOURS+SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS;
|
||
if(!Number.isFinite(lead)||lead<minLead||lead>maxLead||m.spread<=0||m.spread>0.03||m.volume<5000||m.liquidity<2000)return null;
|
||
const side="NO",marketPrice=Number(m.no_price);
|
||
if(marketPrice<SPORTS_FAVORITE_MIN_ENTRY||marketPrice>=SPORTS_FAVORITE_MAX_ENTRY)return null;
|
||
const quotedAsk=Number(m.best_bid)>0?1-Number(m.best_bid):NaN;
|
||
const fallbackAsk=marketPrice+Math.max(0.0025,Number(m.spread||0)/2);
|
||
const executableAsk=Number.isFinite(quotedAsk)&"edAsk>=marketPrice&"edAsk<1?quotedAsk:fallbackAsk;
|
||
const fee=m.fees_enabled===false?0:bundleFeePerShare(m.fee_schedule,executableAsk);
|
||
if(!Number.isFinite(fee))return null;
|
||
const modeledEntry=clamp(executableAsk+fee+SPORTS_FAVORITE_SLIPPAGE_BUFFER,0.01,0.99);
|
||
const executionCost=Math.max(0,modeledEntry-marketPrice);
|
||
if(modeledEntry>=SPORTS_FAVORITE_MAX_ENTRY||executionCost>SPORTS_FAVORITE_MAX_EXECUTION_COST+0.00001)return null;
|
||
const eventKey=sportsContestKey(m);
|
||
const conviction=+clamp(70+Math.log10(Math.max(1,m.liquidity))*1.4+Math.log10(Math.max(1,m.volume_24hr||m.volume))*0.8,70,80).toFixed(1);
|
||
return {market_id:m.id,question:m.question,event:m.event,event_key:eventKey,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:m.yes_price,no_price:m.no_price,fair_value:+modeledEntry.toFixed(4),edge:0,net_edge:0,
|
||
fees_enabled:m.fees_enabled,fee_schedule:m.fee_schedule,entry_fee:+fee.toFixed(5),friction:+executionCost.toFixed(4),chase_penalty:0,evidence_score:0.60,evidence_source_count:0,quality:"sports-contest-no-pilot",side,
|
||
market_price:+marketPrice.toFixed(4),entry_price:+modeledEntry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,
|
||
spread:m.spread,price_change_1h:m.price_change_1h,price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,
|
||
momentum_strength:0,signal_strength:0.60,signal_confidence:0.60,signal_type:"sports-contest-no-pilot",
|
||
trade_ready:true,entry_candidate:true,audited_observation_only:false,adaptive_promotion:true,watch_only:false,jump_risk:true,requires_live:false,
|
||
game_start:m.game_start,hours_to_start:+lead.toFixed(2),days_to_resolution:m.days_to_resolution!=null?+m.days_to_resolution.toFixed(1):null,
|
||
sports_contest_strategy_version:SPORTS_CONTEST_NO_STRATEGY_VERSION,
|
||
pilot_prior:{markets:5000,independent_contests:76,modeled_cost_cents:+(executionCost*100).toFixed(2),train_mean:0.23457,train_lower_95:0.057,
|
||
validation_mean:0.01776,validation_lower_95:-0.19809,holdout_mean:0.36683,holdout_lower_95:0.12251,strictly_promoted:false},
|
||
drivers:["bounded paper exploration","one highest-priced NO per real contest","three days before published start","executable NO ask","exact Gamma entry fee","0.25c slippage"],
|
||
rationale:`Bounded paper exploration: this is the highest-priced eligible NO contract found for ${eventKey.replace("sports:","")} ${lead.toFixed(1)}h before the published start. The modeled ${pct(modeledEntry)} entry includes the executable NO ask, ${(fee*100).toFixed(2)}c exact taker fee, and 0.25c slippage, ${(executionCost*100).toFixed(2)}c above midpoint. In the corrected 5,000-market audit, 76 independent contests were positive in all four chronological quarters; train and holdout lower bounds were positive, while validation returned +1.78% with a wide negative lower bound. This is not a proven edge, so it starts at 0.5%, allows one contest per cycle, and shuts down automatically if forward settlements reject it.`};
|
||
}
|
||
function sportsContestNoSuggestions(markets){
|
||
const grouped=new Map();
|
||
(markets||[]).forEach(m=>{
|
||
const candidate=sportsFavoritePilotSuggestion(m);if(!candidate)return;
|
||
const current=grouped.get(candidate.event_key);
|
||
if(!current||candidate.market_price>current.market_price||(candidate.market_price===current.market_price&&String(candidate.market_id)<String(current.market_id)))grouped.set(candidate.event_key,candidate);
|
||
});
|
||
return [...grouped.values()];
|
||
}
|
||
function analyzeMarket(m,realWorldSignals={}){
|
||
if(m.volume<MIN_SCOUT_VOLUME||m.liquidity<MIN_SCOUT_LIQUIDITY)return null;
|
||
const external=realWorldSignals[m.id]||{},p=m.yes_price,policy=categoryPolicy(m.category);
|
||
const liq=liquiditySignal(m),mom=momentumSignal(m),timing=timingSignal(m.days_to_resolution),evidence=textEvidenceSignal(m,external);
|
||
const signal=confirmedMarketSignal(m,evidence),direction=signal.side==="YES"?1:(signal.side==="NO"?-1:0);
|
||
const grossMove=signal.gross_move||0,friction=frictionPenalty(m),chase=chasePenalty(m,grossMove);
|
||
const rawNet=Math.max(0,grossMove-friction-chase-policy.uncertainty),edgeYes=direction*grossMove;
|
||
const netEdge=+(direction*rawNet).toFixed(4),absNet=Math.abs(netEdge),fair=clamp(p+edgeYes,0.03,0.97);
|
||
const signalScore=Math.min(1,absNet/EDGE_SCALE);
|
||
const conviction=+((W.LIQ*liq+W.MOM*mom+W.SIGNAL*signal.confidence+W.TIME*timing)*100).toFixed(1);
|
||
const jumpRisk=fastSettlementRisk(m);
|
||
const commonReady=signal.confirmed&&!jumpRisk&&direction!==0&&m.spread>0&&m.spread<=0.03
|
||
&&m.volume>=MIN_VOLUME&&m.liquidity>=MIN_LIQUIDITY&&m.volume_24hr>=750
|
||
&&m.days_to_resolution!=null&&m.days_to_resolution>=MIN_ENTRY_DAYS&&p>=0.08&&p<=0.92;
|
||
const strictTradeReady=commonReady&&absNet>=Math.max(MIN_ENTRY_EDGE,policy.minEdge)&&conviction>=70
|
||
&&m.volume>=Math.max(MIN_VOLUME,policy.minVol)&&m.liquidity>=Math.max(MIN_LIQUIDITY,policy.minLiq)&&evidence.score>=policy.minEvidence;
|
||
const catalystTradeReady=!strictTradeReady&&commonReady&&evidence.source_count>=1&&evidence.score>=0.55
|
||
&&absNet>=MIN_SELECTIVE_ENTRY_EDGE&&signal.confidence>=0.62;
|
||
const reversalTradeReady=!strictTradeReady&&!catalystTradeReady&&commonReady&&signal.type==="reversal"
|
||
&&absNet>=MIN_LIQUIDITY_EDGE&&signal.confidence>=0.64&&m.spread<=0.025;
|
||
const liquidityTradeReady=!strictTradeReady&&!catalystTradeReady&&!reversalTradeReady&&commonReady&&signal.type==="trend"
|
||
&&absNet>=MIN_LIQUIDITY_EDGE&&signal.confidence>=0.68&&evidence.score>=0.50&&m.spread<=0.018&&m.liquidity>=5000;
|
||
const trendTradeReady=!strictTradeReady&&!catalystTradeReady&&!reversalTradeReady&&!liquidityTradeReady&&commonReady&&signal.type==="trend"
|
||
&&absNet>=Math.max(MIN_TREND_EDGE,policy.minEdge)&&signal.confidence>=0.60&&evidence.score>=policy.minEvidence;
|
||
const entryCandidate=strictTradeReady||catalystTradeReady||reversalTradeReady||liquidityTradeReady||trendTradeReady;
|
||
const auditedObservationOnly=["trend","reversal"].includes(signal.type);
|
||
const tradeReady=!auditedObservationOnly&&entryCandidate;
|
||
const quality=signal.type==="reversal"?"reversal":(strictTradeReady?"confirmed":(catalystTradeReady?"catalyst":(liquidityTradeReady?"liquid-trend":(trendTradeReady?"trend":"watch"))));
|
||
const side=signal.side||((Number(m.price_change_1d||0)>=0)?"YES":"NO");
|
||
const entry=side==="YES"?p:m.no_price;
|
||
let rationale;
|
||
if(jumpRisk){rationale="Watch only: this market can jump directly to settlement before an hourly stop can protect the position, so agents will not buy it.";}
|
||
else if(signal.type==="reversal"){rationale=`Observation only: the hourly move opposes the one-day direction, but independent audits found this reversal rule lost after costs. The signal will be graded without risking portfolio cash.`;}
|
||
else if(signal.type==="trend"&&m.category==="Crypto"){rationale="Observation only: the exact-fee 1,000-market walk-forward audit found Crypto trends unreliable after costs. This signal keeps training the ledger without risking portfolio cash.";}
|
||
else if(signal.type==="trend"&&m.category==="Sports"){rationale="Observation only: the exact-fee 1,000-market walk-forward audit found Sports trends lost after costs across every chronological segment. This signal keeps training the ledger without risking portfolio cash.";}
|
||
else if(signal.type==="trend"){rationale=`Observation only: the exact-fee 1,000-market audit found the broad trend family lost after costs. This ${side} ${m.category} cohort must independently promote before agents can risk cash.`;}
|
||
else if(strictTradeReady){rationale=`Confirmed setup: ${side} has ${Math.round(signal.strength*100)} signal strength across independent time windows and a ${(absNet*100).toFixed(1)}c signal margin after friction and category uncertainty.`;}
|
||
else if(catalystTradeReady){rationale=`Catalyst-confirmed setup: ${side} price action agrees across the required windows, recent outside coverage exists, and ${(absNet*100).toFixed(1)}c of estimated movement remains after friction. News coverage confirms activity, not direction.`;}
|
||
else if(reversalTradeReady){rationale=`Confirmed reversal: the one-day move is reversing in the hourly window with adequate liquidity and a ${(absNet*100).toFixed(1)}c post-friction signal margin.`;}
|
||
else if(liquidityTradeReady){rationale=`Liquid trend: ${side} is aligned across one-day and one-week windows in a tight, deep market with a ${(absNet*100).toFixed(1)}c post-friction signal margin.`;}
|
||
else if(trendTradeReady){rationale=`Confirmed trend: ${side} is aligned across one-day and one-week windows, avoids chase conditions, and retains a ${(absNet*100).toFixed(1)}c post-friction signal margin.`;}
|
||
else if(!signal.side){rationale="Watch only: no direction is confirmed across independent time windows. Recent price movement alone is not treated as fair value.";}
|
||
else{rationale=`Watch only: the ${signal.type} setup leaves ${(absNet*100).toFixed(1)}c after friction and uncertainty, but it has not cleared every confirmation, liquidity, evidence, and timing gate.`;}
|
||
const drivers=[policy.label];
|
||
if(liq>0.6)drivers.push("deep/liquid market");
|
||
if(mom>0.6)drivers.push("strong fresh volume (24h surge)");
|
||
if(timing>0.8)drivers.push("resolves in a good window");
|
||
if(signal.type==="trend")drivers.push("one-day and one-week direction agree");
|
||
if(signal.type==="reversal")drivers.push("hourly reversal confirms the snapback");
|
||
if(signalScore>0.4)drivers.push("positive signal margin after modeled friction");
|
||
drivers.push(...evidence.drivers.slice(0,3));
|
||
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),
|
||
net_edge:netEdge,friction:+friction.toFixed(4),chase_penalty:+chase.toFixed(4),evidence_score:+evidence.score.toFixed(2),evidence_source_count:evidence.source_count||0,
|
||
quality,side,entry_price:+entry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,
|
||
spread:m.spread,price_change_1h:m.price_change_1h,price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,
|
||
momentum_strength:+signal.strength.toFixed(2),signal_strength:+signal.strength.toFixed(2),signal_confidence:+signal.confidence.toFixed(2),signal_type:signal.type,
|
||
trade_ready:tradeReady,entry_candidate:entryCandidate,audited_observation_only:auditedObservationOnly,jump_risk:jumpRisk,
|
||
fees_enabled:m.fees_enabled,fee_schedule:m.fee_schedule,
|
||
days_to_resolution:m.days_to_resolution!=null?+m.days_to_resolution.toFixed(1):null,drivers,rationale};
|
||
}
|
||
const pct=(x)=>Math.round(x*100)+"%";
|
||
function suggestionBand(s){
|
||
if(s.entry_price<0.35)return "longshot";
|
||
if(s.entry_price>0.65)return "favorite";
|
||
return "balanced";
|
||
}
|
||
function marketWatchSuggestion(m,realWorldSignals={}){
|
||
if(!m||!m.question)return null;
|
||
const p=m.yes_price,fair=fairValue(m,realWorldSignals[m.id]||{}),edgeYes=fair-p;
|
||
const liq=liquiditySignal(m),mom=momentumSignal(m),timing=timingSignal(m.days_to_resolution),policy=categoryPolicy(m.category),evidence=textEvidenceSignal(m,realWorldSignals[m.id]||{});
|
||
const friction=frictionPenalty(m),chase=chasePenalty(m,edgeYes),rawNet=Math.max(0,Math.abs(edgeYes)-friction-chase-policy.uncertainty),netEdge=+(Math.sign(edgeYes)*rawNet).toFixed(4);
|
||
const side=edgeYes>=0?"YES":"NO";
|
||
const entry=side==="YES"?p:m.no_price;
|
||
const conviction=+((0.36*liq+0.28*mom+0.18*timing+0.10*Math.min(1,Math.abs(netEdge)/EDGE_SCALE)+0.08*evidence.score)*100).toFixed(1);
|
||
const drivers=["broad market scan"];
|
||
if(m.volume>MIN_VOLUME)drivers.push("meaningful total volume");
|
||
if(m.volume_24hr>500)drivers.push("fresh 24h activity");
|
||
if(m.liquidity>MIN_LIQUIDITY)drivers.push("usable liquidity");
|
||
drivers.push(...evidence.drivers.slice(0,2));
|
||
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),
|
||
net_edge:netEdge,friction:+friction.toFixed(4),chase_penalty:+chase.toFixed(4),evidence_score:+evidence.score.toFixed(2),quality:"watch",
|
||
side,entry_price:+entry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,
|
||
trade_ready:false,entry_candidate:false,audited_observation_only:false,watch_only:true,
|
||
days_to_resolution:m.days_to_resolution!=null?+m.days_to_resolution.toFixed(1):null,drivers,
|
||
rationale:`Market watchlist: included for coverage from the active-market scan. Signal margin is ${(Math.abs(netEdge)*100).toFixed(1)}c and evidence score is ${Math.round(evidence.score*100)}, so agents will not buy it unless confirmation improves.`};
|
||
}
|
||
function generateSuggestions(markets,total=SUGGESTION_TOTAL,perCategory=SUGGESTION_PER_CATEGORY,realWorldSignals={}){
|
||
const contestNoByMarket=new Map(sportsContestNoSuggestions(markets).map(s=>[String(s.market_id),s]));
|
||
const actionable=markets.map(m=>resolutionWeekNoSuggestion(m)||contestNoByMarket.get(String(m.id))||analyzeMarket(m,realWorldSignals)).filter(Boolean).filter(a=>a.side!=="HOLD").sort((a,b)=>b.conviction-a.conviction);
|
||
const byCat={},byBucket={},seen=new Set(),out=[];
|
||
const add=(a,bucketCap=3,ignoreCategoryCap=false)=>{
|
||
if(out.length>=total||seen.has(a.market_id))return false;
|
||
const c=a.category||"Other",bucket=`${c}:${a.side}:${suggestionBand(a)}`;
|
||
if((!ignoreCategoryCap&&(byCat[c]||0)>=perCategory)||(byBucket[bucket]||0)>=bucketCap)return false;
|
||
byCat[c]=(byCat[c]||0)+1;byBucket[bucket]=(byBucket[bucket]||0)+1;seen.add(a.market_id);out.push(a);
|
||
return true;
|
||
};
|
||
for(const a of actionable)add(a,3); // first pass: category, side, and price-band diversity
|
||
for(const a of actionable)add(a,999); // second pass: fill remaining slots with the best leftovers
|
||
const watchlist=markets.map(m=>marketWatchSuggestion(m,realWorldSignals)).filter(Boolean)
|
||
.filter(a=>!seen.has(a.market_id))
|
||
.sort((a,b)=>(b.volume_24hr-a.volume_24hr)||(b.volume-a.volume)||(b.conviction-a.conviction));
|
||
for(const a of watchlist)add(a,999,true); // final pass: show broad market coverage even when no trade edge is present
|
||
return out.sort((a,b)=>b.conviction-a.conviction);
|
||
}
|
||
|
||
/* ---------- Multi-agent store ---------- */
|
||
function defaultPortfolio(){return {cash:STARTING_BALANCE,starting_balance:STARTING_BALANCE,positions:[],maker_quotes:[],maker_outcomes:[],sports_favorite_shadows:[],sports_favorite_outcomes:[],shock_fade_shadows:[],shock_fade_outcomes:[],shock_fade_expired_ungraded:0,resolution_week_no_shadows:[],resolution_week_no_outcomes:[],closed:[],history:[],snapshots:[],stopped:{},lastDecision:null};}
|
||
function defaultState(){const agents={};AGENTS.forEach(a=>agents[a.id]=defaultPortfolio());return {date:null,last_run:null,last_cycle_hour:null,
|
||
engine_version:BUILD_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION,engine_started_at:nowIso(),build_started_at:nowIso(),agents,signal_ledger:defaultSignalLedger(),seeded:false};}
|
||
function reconcileStateVersions(st){
|
||
let migrated=false;
|
||
const storedBuild=Number(st.engine_version||0);
|
||
const storedStrategy=normalizedStrategyVersion(st.strategy_version!=null?st.strategy_version:storedBuild);
|
||
const strategyChanged=storedStrategy!==SUGGESTION_ENGINE_VERSION;
|
||
const started=strategyChanged?nowIso():(st.strategy_started_at||st.engine_started_at||nowIso());
|
||
AGENTS.forEach(a=>{
|
||
const p=st.agents[a.id],eq=Number(p.cash||0)+(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||Number(pos.shares||0)*Number(pos.current_price||0)),0);
|
||
const baselineStrategy=normalizedStrategyVersion(p.engine_baseline&&p.engine_baseline.version);
|
||
if(strategyChanged||!p.engine_baseline||baselineStrategy!==SUGGESTION_ENGINE_VERSION){
|
||
p.engine_baseline={version:SUGGESTION_ENGINE_VERSION,started_at:started,equity:+eq.toFixed(2)};
|
||
migrated=true;
|
||
}else if(p.engine_baseline.version!==SUGGESTION_ENGINE_VERSION){p.engine_baseline.version=SUGGESTION_ENGINE_VERSION;migrated=true;}
|
||
});
|
||
if(strategyChanged||st.strategy_version!==SUGGESTION_ENGINE_VERSION||storedBuild!==BUILD_VERSION){
|
||
st.previous_engine_version=storedBuild||null;
|
||
st.engine_version=BUILD_VERSION;
|
||
st.strategy_version=SUGGESTION_ENGINE_VERSION;
|
||
st.engine_started_at=started;
|
||
st.strategy_started_at=started;
|
||
st.build_started_at=nowIso();
|
||
migrated=true;
|
||
}
|
||
return migrated;
|
||
}
|
||
function loadState(){
|
||
let st; try{st=JSON.parse(localStorage.getItem(AGENTS_KEY));}catch(e){st=null;}
|
||
if(!st||!st.agents)st=defaultState();
|
||
let migrated=false;
|
||
const activeIds=new Set(AGENTS.map(a=>a.id));
|
||
Object.keys(st.agents).forEach(id=>{if(!activeIds.has(id))delete st.agents[id];});
|
||
delete st.whales;
|
||
delete st.copycatLeader;
|
||
if(!st.signal_ledger||typeof st.signal_ledger!=="object")st.signal_ledger=defaultSignalLedger();
|
||
AGENTS.forEach(a=>{
|
||
if(!st.agents[a.id])st.agents[a.id]=defaultPortfolio();
|
||
if(!st.agents[a.id].stopped)st.agents[a.id].stopped={};
|
||
if(!Array.isArray(st.agents[a.id].maker_quotes))st.agents[a.id].maker_quotes=[];
|
||
if(!Array.isArray(st.agents[a.id].maker_outcomes))st.agents[a.id].maker_outcomes=[];
|
||
if(!Array.isArray(st.agents[a.id].sports_favorite_shadows))st.agents[a.id].sports_favorite_shadows=[];
|
||
if(!Array.isArray(st.agents[a.id].sports_favorite_outcomes))st.agents[a.id].sports_favorite_outcomes=[];
|
||
if(!Array.isArray(st.agents[a.id].shock_fade_shadows))st.agents[a.id].shock_fade_shadows=[];
|
||
if(!Array.isArray(st.agents[a.id].shock_fade_outcomes))st.agents[a.id].shock_fade_outcomes=[];
|
||
if(!Array.isArray(st.agents[a.id].resolution_week_no_shadows))st.agents[a.id].resolution_week_no_shadows=[];
|
||
if(!Array.isArray(st.agents[a.id].resolution_week_no_outcomes))st.agents[a.id].resolution_week_no_outcomes=[];
|
||
if(!Number.isFinite(Number(st.agents[a.id].shock_fade_expired_ungraded)))st.agents[a.id].shock_fade_expired_ungraded=0;
|
||
if(!("lastDecision" in st.agents[a.id]))st.agents[a.id].lastDecision=null;
|
||
});
|
||
migrated=reconcileStateVersions(st)||migrated;
|
||
if(!st.last_cycle_hour&&st.last_run)st.last_cycle_hour=cycleHourFromIso(st.last_run);
|
||
if(migrated)localStorage.setItem(AGENTS_KEY,JSON.stringify(compactAgentStateForSync(st)));
|
||
return st;
|
||
}
|
||
function compactPortfolioForSync(p,limits=SYNC_LIMITS){
|
||
if(!p||typeof p!=="object")return p;
|
||
const out=Object.assign({},p);
|
||
out.positions=Array.isArray(p.positions)?p.positions:[];
|
||
out.maker_quotes=Array.isArray(p.maker_quotes)?p.maker_quotes.slice(-MAKER_MAX_QUOTES):[];
|
||
out.maker_outcomes=Array.isArray(p.maker_outcomes)?p.maker_outcomes.slice(-MAKER_OUTCOME_LIMIT):[];
|
||
out.sports_favorite_shadows=Array.isArray(p.sports_favorite_shadows)?p.sports_favorite_shadows.slice(-SPORTS_FAVORITE_SHADOW_LIMIT):[];
|
||
out.sports_favorite_outcomes=Array.isArray(p.sports_favorite_outcomes)?p.sports_favorite_outcomes.slice(-SPORTS_FAVORITE_OUTCOME_LIMIT):[];
|
||
out.shock_fade_shadows=Array.isArray(p.shock_fade_shadows)?p.shock_fade_shadows.slice(-SHOCK_FADE_SHADOW_LIMIT):[];
|
||
out.shock_fade_outcomes=Array.isArray(p.shock_fade_outcomes)?p.shock_fade_outcomes.slice(-SHOCK_FADE_OUTCOME_LIMIT):[];
|
||
out.resolution_week_no_shadows=Array.isArray(p.resolution_week_no_shadows)?p.resolution_week_no_shadows.slice(-RESOLUTION_WEEK_NO_SHADOW_LIMIT):[];
|
||
out.resolution_week_no_outcomes=Array.isArray(p.resolution_week_no_outcomes)?p.resolution_week_no_outcomes.slice(-RESOLUTION_WEEK_NO_OUTCOME_LIMIT):[];
|
||
out.closed=Array.isArray(p.closed)?p.closed.slice(-limits.closed):[];
|
||
out.history=Array.isArray(p.history)?p.history.slice(-limits.history):[];
|
||
out.snapshots=Array.isArray(p.snapshots)?p.snapshots.slice(-limits.snapshots):[];
|
||
if(p.stopped&&typeof p.stopped==="object"){
|
||
out.stopped=Object.fromEntries(Object.entries(p.stopped).slice(-80));
|
||
}
|
||
return out;
|
||
}
|
||
function compactAgentStateForSync(st,limits=SYNC_LIMITS){
|
||
if(!st||typeof st!=="object")return st;
|
||
const out=Object.assign({},st,{agents:{}});
|
||
AGENTS.forEach(a=>{out.agents[a.id]=compactPortfolioForSync(st.agents&&st.agents[a.id],limits)||defaultPortfolio();});
|
||
if(st.signal_ledger&&typeof st.signal_ledger==="object")out.signal_ledger={
|
||
pending:(st.signal_ledger.pending||[]).slice(-SIGNAL_LEDGER_PENDING_LIMIT),
|
||
outcomes:(st.signal_ledger.outcomes||[]).slice(-SIGNAL_LEDGER_OUTCOME_LIMIT),
|
||
expired_ungraded:Number(st.signal_ledger.expired_ungraded||0),
|
||
};
|
||
delete out.whales;
|
||
delete out.copycatLeader;
|
||
return out;
|
||
}
|
||
function compactSuggestionForSync(s){
|
||
if(!s||typeof s!=="object")return s;
|
||
return {
|
||
market_id:s.market_id,question:s.question,event:s.event,url:s.url,category:s.category,
|
||
clob_yes:s.clob_yes,clob_no:s.clob_no,yes_price:s.yes_price,no_price:s.no_price,
|
||
fair_value:s.fair_value,edge:s.edge,side:s.side,entry_price:s.entry_price,market_price:s.market_price,
|
||
net_edge:s.net_edge,friction:s.friction,chase_penalty:s.chase_penalty,evidence_score:s.evidence_score,evidence_source_count:s.evidence_source_count,quality:s.quality,
|
||
conviction:s.conviction,volume:s.volume,volume_24hr:s.volume_24hr,liquidity:s.liquidity,
|
||
fees_enabled:s.fees_enabled,fee_schedule:s.fee_schedule,
|
||
spread:s.spread,price_change_1h:s.price_change_1h,price_change_1d:s.price_change_1d,price_change_1w:s.price_change_1w,momentum_strength:s.momentum_strength,
|
||
signal_strength:s.signal_strength,signal_confidence:s.signal_confidence,signal_type:s.signal_type,
|
||
trade_ready:s.trade_ready,entry_candidate:s.entry_candidate,audited_observation_only:s.audited_observation_only,
|
||
adaptive_promotion:s.adaptive_promotion,promoted_for_agents:s.promoted_for_agents,watch_only:s.watch_only,jump_risk:s.jump_risk,
|
||
requires_live:s.requires_live,bundle_id:s.bundle_id,bundle_side:s.bundle_side,bundle_logic:s.bundle_logic,bundle_cost_per_unit:s.bundle_cost_per_unit,
|
||
bundle_payout_per_unit:s.bundle_payout_per_unit,bundle_net_profit_per_unit:s.bundle_net_profit_per_unit,bundle_legs:s.bundle_legs,
|
||
depth_verified:s.depth_verified,fees_verified:s.fees_verified,fee_model:s.fee_model,execution_model:s.execution_model,
|
||
execution_units:s.execution_units,execution_notional:s.execution_notional,verification_status:s.verification_status,
|
||
days_to_resolution:s.days_to_resolution,event_key:s.event_key,game_start:s.game_start,hours_to_start:s.hours_to_start,
|
||
end_date:s.end_date,target_horizon_days:s.target_horizon_days,resolution_week_strategy_version:s.resolution_week_strategy_version,
|
||
sports_contest_strategy_version:s.sports_contest_strategy_version,entry_fee:s.entry_fee,
|
||
pilot_prior:s.pilot_prior,shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,
|
||
shock_observed_at:s.shock_observed_at,shock_strategy_version:s.shock_strategy_version,
|
||
drivers:s.drivers,rationale:s.rationale,
|
||
};
|
||
}
|
||
function compactSuggestionsForSync(payload,limits=SYNC_LIMITS){
|
||
if(!payload||typeof payload!=="object")return payload;
|
||
return Object.assign({},payload,{suggestions:(payload.suggestions||[]).slice(0,limits.suggestions).map(compactSuggestionForSync)});
|
||
}
|
||
function compactPaperStoreForSync(store,limits=SYNC_LIMITS){
|
||
if(!store||typeof store!=="object"||!store.accounts)return store;
|
||
const accounts={};
|
||
Object.entries(store.accounts).forEach(([id,acct])=>{
|
||
accounts[id]=Object.assign({},acct,{
|
||
history:Array.isArray(acct.history)?acct.history.slice(-limits.paperHistory):[],
|
||
snapshots:Array.isArray(acct.snapshots)?acct.snapshots.slice(-limits.paperSnapshots):[],
|
||
});
|
||
});
|
||
return Object.assign({},store,{accounts});
|
||
}
|
||
function compactSyncItems(items,limits=SYNC_LIMITS){
|
||
const out=Object.assign({},items||{});
|
||
try{if(out[AGENTS_KEY])out[AGENTS_KEY]=JSON.stringify(compactAgentStateForSync(JSON.parse(out[AGENTS_KEY]),limits));}catch(e){}
|
||
try{if(out[SUG_KEY])out[SUG_KEY]=JSON.stringify(compactSuggestionsForSync(JSON.parse(out[SUG_KEY]),limits));}catch(e){}
|
||
try{if(out[PAPER_KEY])out[PAPER_KEY]=JSON.stringify(compactPaperStoreForSync(JSON.parse(out[PAPER_KEY]),limits));}catch(e){}
|
||
try{if(out[LIVE_KEY]){const live=JSON.parse(out[LIVE_KEY]);if(Array.isArray(live.audit))live.audit=live.audit.slice(-limits.audit);out[LIVE_KEY]=JSON.stringify(live);}}catch(e){}
|
||
return out;
|
||
}
|
||
function saveState(st){localStorage.setItem(AGENTS_KEY,JSON.stringify(compactAgentStateForSync(st)));}
|
||
function loadSuggestions(){try{const s=localStorage.getItem(SUG_KEY);return s?JSON.parse(s):{date:null,suggestions:[]};}catch(e){return {date:null,suggestions:[]};}}
|
||
function saveSuggestions(sugs,marketCount=0,analyzedCount=null,opportunityAudit=null){const p={date:todayStr(),generated_at:nowIso(),engine_version:BUILD_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION,market_count:marketCount,analyzed_count:analyzedCount==null?marketCount:analyzedCount,analysis_limit:MARKET_ANALYSIS_LIMIT,suggestion_cap:SUGGESTION_TOTAL,opportunity_audit:opportunityAudit,suggestions:sugs};const compact=compactSuggestionsForSync(p);localStorage.setItem(SUG_KEY,JSON.stringify(compact));return compact;}
|
||
function compactCachedMarket(m){
|
||
return {id:m.id,question:m.question,event:m.event,event_id:m.event_id,market_slug:m.market_slug,url:m.url,category:m.category,tags:m.tags,
|
||
clob_token_ids:m.clob_token_ids,yes_price:m.yes_price,no_price:m.no_price,volume:m.volume,
|
||
volume_24hr:m.volume_24hr,volume_1wk:m.volume_1wk,liquidity:m.liquidity,spread:m.spread,
|
||
best_bid:m.best_bid,best_ask:m.best_ask,price_change_1h:m.price_change_1h,
|
||
price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,price_change_1m:m.price_change_1m,
|
||
days_to_resolution:m.days_to_resolution,end_date:m.end_date,game_start:m.game_start,hours_to_start:m.hours_to_start,
|
||
sports_market_type:m.sports_market_type,closed:m.closed,accepting_orders:m.accepting_orders,
|
||
condition_id:m.condition_id,tick_size:m.tick_size,order_min_size:m.order_min_size,competitive:m.competitive,
|
||
rewards_daily_rate:m.rewards_daily_rate,rewards_min_size:m.rewards_min_size,rewards_max_spread:m.rewards_max_spread,
|
||
fees_enabled:m.fees_enabled,fee_schedule:m.fee_schedule};
|
||
}
|
||
function saveMarketCache(markets,suggestions,priceMap={}){
|
||
const payload={version:BUILD_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION,captured_at:nowIso(),markets:(markets||[]).slice(0,MARKET_ANALYSIS_LIMIT).map(compactCachedMarket),
|
||
suggestions:compactSuggestionsForSync({suggestions:suggestions||[]}).suggestions,price_map:{}};
|
||
Object.entries(priceMap||{}).forEach(([id,m])=>{if(m)payload.price_map[id]=compactCachedMarket(m);});
|
||
try{localStorage.setItem(MARKET_CACHE_KEY,JSON.stringify(payload));return payload;}catch(e){return null;}
|
||
}
|
||
function loadMarketCache(){
|
||
try{
|
||
const payload=JSON.parse(localStorage.getItem(MARKET_CACHE_KEY)||"null");
|
||
if(!payload||!payload.captured_at)return null;
|
||
payload.age_ms=Math.max(0,Date.now()-new Date(payload.captured_at).getTime());
|
||
return Number.isFinite(payload.age_ms)?payload:null;
|
||
}catch(e){return null;}
|
||
}
|
||
function offlineCachePolicy(ageMs){
|
||
const age=Number(ageMs);
|
||
const usable=Number.isFinite(age)&&age>=0&&age<=OFFLINE_CACHE_MAX_AGE_MS;
|
||
const executionAllowed=usable&&age<=OFFLINE_ENTRY_MAX_AGE_MS;
|
||
return {usable,entriesAllowed:executionAllowed,exitsAllowed:executionAllowed,
|
||
markOnly:usable&&!executionAllowed};
|
||
}
|
||
function prepareCycleSuggestions(suggestions,runMode,entriesAllowed){
|
||
return (suggestions||[]).map(s=>{
|
||
let next=s;
|
||
if((s.signal_type||"")==="sports-contest-no-pilot"){
|
||
const lead=sportsFavoriteLeadHours(s),eligible=Number.isFinite(lead)
|
||
&&lead>=SPORTS_FAVORITE_TARGET_LEAD_HOURS-SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS
|
||
&&lead<=SPORTS_FAVORITE_TARGET_LEAD_HOURS+SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS;
|
||
next=Object.assign({},s,{hours_to_start:Number.isFinite(lead)?+lead.toFixed(2):null,
|
||
trade_ready:Boolean(s.trade_ready&&eligible),entry_candidate:Boolean(s.entry_candidate&&eligible),watch_only:!eligible||Boolean(s.watch_only)});
|
||
}
|
||
return entriesAllowed&&(runMode==="live"||!next.requires_live)
|
||
?next:Object.assign({},next,{trade_ready:false,entry_candidate:false,watch_only:true});
|
||
});
|
||
}
|
||
function collectSyncItems(){const items={};SYNC_KEYS.forEach(k=>{const v=localStorage.getItem(k);if(v!=null)items[k]=v;});return compactSyncItems(items);}
|
||
function collectPublicRuntimeItems(){
|
||
const st=compactAgentStateForSync(loadState(),PUBLIC_RUNTIME_LIMITS);
|
||
st.pending_sync=false;
|
||
st.runtime_managed=true;
|
||
const suggestions=compactSuggestionsForSync(loadSuggestions(),PUBLIC_RUNTIME_LIMITS);
|
||
return {[AGENTS_KEY]:JSON.stringify(st),[SUG_KEY]:JSON.stringify(suggestions)};
|
||
}
|
||
function buildPublicRuntimeSnapshot(){
|
||
const st=loadState();
|
||
return {schema_version:1,build_version:BUILD_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION,
|
||
generated_at:nowIso(),updated_at:nowIso(),last_cycle_hour:st.last_cycle_hour||null,
|
||
source:"github-actions",read_only:true,items:collectPublicRuntimeItems(),
|
||
summary:{agents:AGENTS.map(cfg=>{const p=st.agents[cfg.id],eq=equity(p);return {id:cfg.id,equity:+eq.toFixed(2),
|
||
return_pct:+((eq/STARTING_BALANCE-1)*100).toFixed(3),cash:+Number(p.cash||0).toFixed(2),positions:(p.positions||[]).length};})}};
|
||
}
|
||
function applyPublicRuntimeSnapshot(snapshot){
|
||
if(!snapshot||typeof snapshot!=="object"||!snapshot.items||typeof snapshot.items!=="object")return false;
|
||
const items={};PUBLIC_RUNTIME_KEYS.forEach(key=>{if(typeof snapshot.items[key]==="string")items[key]=snapshot.items[key];});
|
||
if(!stateFromSyncItems(items))return false;
|
||
return applySyncItems(items);
|
||
}
|
||
function applySyncItems(items){
|
||
if(!items||typeof items!=="object")return false;
|
||
const compact=compactSyncItems(items);
|
||
try{SYNC_KEYS.forEach(k=>{if(compact[k]!=null)localStorage.setItem(k,compact[k]);});return true;}
|
||
catch(e){
|
||
const tighter=compactSyncItems(items,{closed:30,history:60,snapshots:90,suggestions:300,paperHistory:60,paperSnapshots:60,audit:60});
|
||
try{SYNC_KEYS.forEach(k=>{if(tighter[k]!=null)localStorage.setItem(k,tighter[k]);});toast("Loaded a compact mobile state.");return true;}
|
||
catch(err){setStatus("storage full",false);toast("This device's browser storage is full. Open in a normal tab or clear site data.");return false;}
|
||
}
|
||
}
|
||
function stateFromSyncItems(items){
|
||
try{return items&&items[AGENTS_KEY]?JSON.parse(items[AGENTS_KEY]):null;}
|
||
catch(e){return null;}
|
||
}
|
||
let CLOUD_STATE_HEALTH={ok:null,degraded:false,read_only:false,source:null,providers:null,error:null};
|
||
window.PMA_CLOUD_STATE_HEALTH=CLOUD_STATE_HEALTH;
|
||
function rememberCloudHealth(next){
|
||
CLOUD_STATE_HEALTH={...CLOUD_STATE_HEALTH,...next};
|
||
window.PMA_CLOUD_STATE_HEALTH=CLOUD_STATE_HEALTH;
|
||
return CLOUD_STATE_HEALTH;
|
||
}
|
||
function localOnlyStatus(base,providerOverride=null){
|
||
if(CLOUD_STATE_HEALTH.read_only&&CLOUD_STATE_HEALTH.source==="github-actions")return `${base} · autonomous shared state`;
|
||
const providers=providerOverride||CLOUD_STATE_HEALTH.providers||{};
|
||
const dbStatus=providers.database&&providers.database.status;
|
||
const blobStatus=providers.blob&&providers.blob.status;
|
||
if(dbStatus==="provider_payment_required")return `${base} · local only (Neon paused)`;
|
||
if(blobStatus==="authorization_failed")return `${base} · local only (cloud token invalid)`;
|
||
return `${base} · local only`;
|
||
}
|
||
function autonomousRuntimeControlsCycle(health=CLOUD_STATE_HEALTH,automation=AUTOMATION_MODE){
|
||
return Boolean(health&&health.read_only&&health.source==="github-actions"&&!automation);
|
||
}
|
||
window.PMA_CLOUD_STATUS_LABEL=localOnlyStatus;
|
||
async function pushCloudState(force=false){
|
||
if(CLOUD_STATE_HEALTH.read_only)return false;
|
||
try{
|
||
const r=await fetchWithTimeout("/api/state",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({items:collectSyncItems(),force})},NETWORK_REQUEST_TIMEOUT_MS);
|
||
if(r.status===409){
|
||
const d=await r.json().catch(()=>null);
|
||
if(d&&d.state&&d.state.items)applySyncItems(d.state.items);
|
||
else await loadAuthoritativeCloudState();
|
||
toast("Loaded the newer shared cloud result.");
|
||
renderAll();
|
||
return false;
|
||
}
|
||
const d=await r.json().catch(()=>null);
|
||
if(!r.ok){
|
||
rememberCloudHealth({ok:false,degraded:true,read_only:Boolean(d&&d.read_only),source:d&&d.source||null,providers:d&&d.providers||null,error:d&&d.error||"Cloud sync unavailable"});
|
||
return false;
|
||
}
|
||
rememberCloudHealth({ok:true,degraded:false,read_only:false,source:"managed-storage",providers:null,error:null});
|
||
return true;
|
||
}catch(e){rememberCloudHealth({ok:false,degraded:true,error:"Cloud sync unavailable"});return false;}
|
||
}
|
||
async function pullCloudState(){
|
||
try{
|
||
const r=await fetchWithTimeout("/api/state",{cache:"no-store"},NETWORK_REQUEST_TIMEOUT_MS);
|
||
if(!r.ok){
|
||
const d=await r.json().catch(()=>null);
|
||
rememberCloudHealth({ok:false,degraded:true,read_only:Boolean(d&&d.read_only),source:d&&d.source||null,providers:d&&d.providers||null,error:d&&d.error||"Cloud sync unavailable"});
|
||
return {ok:false,state:null,degraded:true,read_only:Boolean(d&&d.read_only),source:d&&d.source||null,providers:d&&d.providers||null,error:d&&d.error};
|
||
}
|
||
const d=await r.json();
|
||
const result={ok:true,state:(d&&d.state)||null,degraded:Boolean(d&&d.degraded),read_only:Boolean(d&&d.read_only),source:d&&d.source||null,error:d&&d.error,providers:d&&d.providers||null};
|
||
rememberCloudHealth(result);
|
||
return result;
|
||
}catch(e){}
|
||
rememberCloudHealth({ok:false,degraded:true,error:"Cloud sync unavailable"});
|
||
return {ok:false,state:null};
|
||
}
|
||
async function loadAuthoritativeCloudState(){
|
||
const cloud=await pullCloudState();
|
||
if(!cloud.ok)return {ok:false,loaded:false,degraded:true,read_only:Boolean(cloud.read_only),source:cloud.source||null};
|
||
const cloudState=cloud.state;
|
||
if(cloudState&&cloudState.items&&stateFromSyncItems(cloudState.items)){
|
||
applySyncItems(cloudState.items);
|
||
return {ok:true,loaded:true,degraded:Boolean(cloud.degraded),read_only:Boolean(cloud.read_only),source:cloud.source||null,providers:cloud.providers||null};
|
||
}
|
||
return {ok:true,loaded:false,degraded:Boolean(cloud.degraded),read_only:Boolean(cloud.read_only),source:cloud.source||null,providers:cloud.providers||null};
|
||
}
|
||
async function refreshFromCloudAndRender(silent=true){
|
||
const cloud=await loadAuthoritativeCloudState();
|
||
if(cloud.loaded){
|
||
renderAll();
|
||
setStatus(cloud.read_only?"autonomous shared state loaded":"cloud synced",false);
|
||
if(!silent)toast(cloud.read_only?"Autonomous agent state refreshed.":"Shared cloud state refreshed.");
|
||
}else if(cloud.degraded){
|
||
setStatus(localOnlyStatus("cloud storage paused"),false);
|
||
if(!silent)toast("Cloud storage is unavailable. This device is using its local backup.");
|
||
}else if(!cloud.ok){
|
||
setStatus("cloud sync unavailable",false);
|
||
if(!silent)toast("Cloud sync is unavailable. Results may be stale until it reconnects.");
|
||
}
|
||
return cloud;
|
||
}
|
||
|
||
const EMAIL_ALERT_TYPES=[
|
||
{id:"OPEN",label:"Opens"},
|
||
{id:"CLOSE",label:"Closes"},
|
||
{id:"EXIT",label:"Policy exits"},
|
||
{id:"STOP",label:"Stop losses"},
|
||
{id:"GAIN",label:"Gain stops"},
|
||
{id:"RISK",label:"Risk rebalances"},
|
||
];
|
||
function defaultEmailAlerts(){
|
||
return {
|
||
enabled:false,
|
||
email:"",
|
||
events:{OPEN:true,CLOSE:true,EXIT:true,STOP:true,GAIN:true,RISK:true},
|
||
agents:Object.fromEntries(AGENTS.map(a=>[a.id,true])),
|
||
last_sent_at:null,
|
||
last_error:null,
|
||
};
|
||
}
|
||
function loadEmailAlerts(){
|
||
try{
|
||
const base=defaultEmailAlerts();
|
||
const saved=JSON.parse(localStorage.getItem(EMAIL_ALERT_KEY)||"{}");
|
||
const merged=Object.assign(base,saved||{});
|
||
merged.events=Object.assign(base.events,(saved&&saved.events)||{});
|
||
merged.agents=Object.assign(base.agents,(saved&&saved.agents)||{});
|
||
return merged;
|
||
}
|
||
catch(e){return defaultEmailAlerts();}
|
||
}
|
||
function saveEmailAlerts(cfg){
|
||
const base=defaultEmailAlerts();
|
||
const merged=Object.assign(base,cfg||{});
|
||
merged.email=String(merged.email||"").trim();
|
||
merged.events=Object.assign(base.events,(cfg&&cfg.events)||{});
|
||
merged.agents=Object.assign(base.agents,(cfg&&cfg.agents)||{});
|
||
merged.enabled=Boolean(merged.enabled&&validEmail(merged.email));
|
||
localStorage.setItem(EMAIL_ALERT_KEY,JSON.stringify(merged));
|
||
return merged;
|
||
}
|
||
function validEmail(email){return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(email||"").trim());}
|
||
function agentHistoryOffsets(st){
|
||
const offsets={};
|
||
AGENTS.forEach(a=>{offsets[a.id]=((st.agents[a.id]&&st.agents[a.id].history)||[]).length;});
|
||
return offsets;
|
||
}
|
||
function collectNewAgentEvents(st,offsets){
|
||
const cfg=loadEmailAlerts();
|
||
if(!cfg.enabled||!validEmail(cfg.email))return [];
|
||
const enabled=cfg.events||{};
|
||
const selected=cfg.agents||{};
|
||
return AGENTS.flatMap(a=>{
|
||
if(!selected[a.id])return [];
|
||
const p=st.agents[a.id]||defaultPortfolio();
|
||
const start=Math.max(0,offsets[a.id]||0);
|
||
return (p.history||[]).slice(start).filter(h=>enabled[h.action]).map(h=>({
|
||
agent_id:a.id,
|
||
agent_name:a.name,
|
||
agent_emoji:a.emoji,
|
||
action:h.action,
|
||
date:h.date,
|
||
question:h.question||"",
|
||
side:h.side||"",
|
||
detail:h.detail||"",
|
||
equity:+equity(p).toFixed(2),
|
||
return_pct:+((equity(p)/STARTING_BALANCE-1)*100).toFixed(2),
|
||
}));
|
||
}).slice(0,100);
|
||
}
|
||
async function notifyTradeDigest(events,{test=false}={}){
|
||
const cfg=loadEmailAlerts();
|
||
if(!events.length||!cfg.enabled||!validEmail(cfg.email))return {ok:false,skipped:true};
|
||
try{
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({
|
||
action:"trade_email",
|
||
to:cfg.email,
|
||
events,
|
||
test,
|
||
app_url:location.origin+location.pathname,
|
||
})});
|
||
const d=await r.json().catch(()=>({}));
|
||
if(!r.ok)throw new Error(d.error||"Email alert failed");
|
||
saveEmailAlerts(Object.assign(cfg,{last_sent_at:nowIso(),last_error:null}));
|
||
if(test)toast("Test email sent.");
|
||
return d;
|
||
}catch(e){
|
||
saveEmailAlerts(Object.assign(cfg,{last_error:e.message||"Email alert failed"}));
|
||
if(test)toast(e.message||"Email alert failed.");
|
||
else toast("Trade email alert failed. Check email provider settings.");
|
||
return {ok:false,error:e.message};
|
||
}finally{renderEmailAlerts();}
|
||
}
|
||
|
||
/* ---------- Agent mechanics ---------- */
|
||
const equity=(p)=>p.cash+p.positions.reduce((s,x)=>s+x.shares*x.current_price,0);
|
||
const hasPosition=(p,id)=>p.positions.some(x=>x.market_id===id);
|
||
function recentReturnDelta(p){
|
||
const snaps=(p.snapshots||[]).slice(-3);
|
||
if(snaps.length<2)return 0;
|
||
return (snaps[snaps.length-1].return_pct||0)-(snaps[0].return_pct||0);
|
||
}
|
||
function entryBand(price){const p=Number(price||0);return p<0.25?"longshot":p<0.55?"mid":p<0.78?"favorite":"heavy-favorite";}
|
||
function resolutionBand(days){
|
||
const value=Number(days);
|
||
if(!Number.isFinite(value))return "unknown";
|
||
return value<=21?"short":(value<=90?"medium":"long");
|
||
}
|
||
function learningFeatures(trade){
|
||
return [`signal:${trade.signal_type||"unknown"}`,`quality:${trade.quality||"unknown"}`,
|
||
`category:${trade.category||"Other"}`,`side:${trade.side||"unknown"}`,`price:${entryBand(trade.entry_price)}`,
|
||
`duration:${resolutionBand(trade.days_to_resolution)}`];
|
||
}
|
||
function summarizeLearningBucket(bucket,shrinkage){
|
||
const weight=Math.max(0,Number(bucket&&bucket.weight||0));
|
||
const raw=weight?Number(bucket.sum||0)/weight:0;
|
||
const variance=weight?Math.max(0,Number(bucket.sumSq||0)/weight-raw*raw):0;
|
||
const priorWeight=4,priorVariance=0.04;
|
||
const pooledVariance=(priorWeight*priorVariance+weight*variance)/(priorWeight+weight||1);
|
||
const stderr=Math.sqrt(pooledVariance/Math.max(1,weight));
|
||
const score=Number(bucket&&bucket.sum||0)/(weight+shrinkage);
|
||
return {samples:Number(bucket&&bucket.count||0),observations:Number(bucket&&bucket.observations||bucket&&bucket.count||0),weight:+weight.toFixed(2),current_weight:+Number(bucket&&bucket.currentWeight||0).toFixed(2),raw:+raw.toFixed(4),
|
||
score:+score.toFixed(4),stderr:+stderr.toFixed(4),lower_bound:+(score-1.28*stderr).toFixed(4),
|
||
upper_bound:+(score+1.28*stderr).toFixed(4),win_rate:weight?Number(bucket.wins||0)/weight:0};
|
||
}
|
||
function defaultSignalLedger(){return {pending:[],outcomes:[]};}
|
||
function signalPrice(m,side){return side==="YES"?Number(m&&m.yes_price):Number(m&&m.no_price);}
|
||
function signalPolicyVersion(item){
|
||
const explicit=Number(item&&item.signal_policy_version);
|
||
if(Number.isFinite(explicit)&&explicit>0)return explicit;
|
||
const strategy=normalizedStrategyVersion(item&&item.strategy_version);
|
||
return ["trend","reversal"].includes(item&&item.signal_type||"")
|
||
&&strategy>=DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN&&strategy<=DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX
|
||
?DIRECTIONAL_SIGNAL_POLICY_VERSION:0;
|
||
}
|
||
function currentSignalPolicy(item){return signalPolicyVersion(item)===DIRECTIONAL_SIGNAL_POLICY_VERSION;}
|
||
function signalEligibleForAgent(item,agentId){
|
||
if(!agentId)return true;
|
||
if(!item||!Object.prototype.hasOwnProperty.call(item,"eligible_agent_ids"))return true;
|
||
return Array.isArray(item.eligible_agent_ids)&&item.eligible_agent_ids.includes(agentId);
|
||
}
|
||
function signalObservationEventKey(item){return String(item&&item.event_key||item&&item.url||item&&item.event||item&&item.market_id||"").trim().toLowerCase();}
|
||
function signalObservationCohortKey(item){return `${item&&item.signal_type||"unknown"}|${item&&item.side||"unknown"}|${item&&item.category||"Other"}`;}
|
||
function prioritizeSignalObservations(suggestions,ledger,pending=[]){
|
||
const history=[...((ledger&&ledger.outcomes)||[]),...(pending||[])],eventCounts={},cohortCounts={},pairCounts={};
|
||
history.forEach(item=>{
|
||
const eventKey=signalObservationEventKey(item),cohortKey=signalObservationCohortKey(item),pair=`${item&&item.market_id||""}:${item&&item.side||""}`;
|
||
if(eventKey)eventCounts[eventKey]=(eventCounts[eventKey]||0)+1;
|
||
cohortCounts[cohortKey]=(cohortCounts[cohortKey]||0)+1;
|
||
if(pair!==":")pairCounts[pair]=(pairCounts[pair]||0)+1;
|
||
});
|
||
return (suggestions||[]).map((s,index)=>({s,index,eventCount:eventCounts[signalObservationEventKey(s)]||0,
|
||
cohortCount:cohortCounts[signalObservationCohortKey(s)]||0,pairCount:pairCounts[`${s.market_id}:${s.side}`]||0}))
|
||
.sort((a,b)=>(a.cohortCount-b.cohortCount)||(a.eventCount-b.eventCount)||(a.pairCount-b.pairCount)
|
||
||(Number(b.s.conviction||0)-Number(a.s.conviction||0))||(a.index-b.index))
|
||
.map(row=>row.s);
|
||
}
|
||
function signalGradedHorizons(item){
|
||
return new Set((Array.isArray(item&&item.graded_horizons)?item.graded_horizons:[]).map(Number).filter(Number.isFinite));
|
||
}
|
||
function signalDueHorizon(item,ageHours){
|
||
const graded=signalGradedHorizons(item);
|
||
return SIGNAL_EVAL_HORIZONS.find(horizon=>!graded.has(horizon)&&ageHours>=horizon&&ageHours<=horizon+SIGNAL_EVAL_TOLERANCE_HOURS)||null;
|
||
}
|
||
function pendingSignalMarketIds(ledger,knownIds=new Set(),now=Date.now()){
|
||
const rows=((ledger&&ledger.pending)||[]).map(item=>({item,started:new Date(item.observed_at||0).getTime()}))
|
||
.filter(row=>Number.isFinite(row.started))
|
||
.filter(row=>!Object.prototype.hasOwnProperty.call(row.item||{},"signal_policy_version")||currentSignalPolicy(row.item))
|
||
.filter(row=>signalDueHorizon(row.item,(now-row.started)/3600000)!=null)
|
||
.sort((a,b)=>a.started-b.started);
|
||
const ids=[],seen=new Set();
|
||
for(const {item} of rows){
|
||
const id=String(item.market_id||"");
|
||
if(!id||knownIds.has(id)||seen.has(id))continue;
|
||
seen.add(id);ids.push(id);
|
||
if(ids.length>=SIGNAL_LEDGER_DUE_FETCH_LIMIT)break;
|
||
}
|
||
return ids;
|
||
}
|
||
function updateSignalLedger(st,markets,suggestions){
|
||
const ledger=st.signal_ledger&&typeof st.signal_ledger==="object"?st.signal_ledger:defaultSignalLedger();
|
||
ledger.pending=Array.isArray(ledger.pending)?ledger.pending:[];ledger.outcomes=Array.isArray(ledger.outcomes)?ledger.outcomes:[];
|
||
const now=Date.now(),marketMap=Object.fromEntries((markets||[]).map(m=>[String(m.id),m])),stillPending=[];
|
||
for(const item of ledger.pending){
|
||
if(Object.prototype.hasOwnProperty.call(item||{},"signal_policy_version")&&!currentSignalPolicy(item))continue;
|
||
const started=new Date(item.observed_at||0).getTime(),ageHours=Number.isFinite(started)?(now-started)/3600000:Infinity;
|
||
const graded=signalGradedHorizons(item),dueHorizon=signalDueHorizon(item,ageHours);
|
||
const fresh=marketMap[String(item.market_id)],future=signalPrice(fresh,item.side);
|
||
if(dueHorizon!=null&&Number.isFinite(future)&&future>=0&&future<=1){
|
||
const entry=Math.max(0.01,Number(item.entry_price||0.01));
|
||
const feeSource=fresh||item,executionCost=signalExecutionCost(feeSource,entry,future);
|
||
const gross=future/entry-1,estimatedCost=executionCost.cost/entry;
|
||
const outcome=clamp(gross-estimatedCost,-1,2);
|
||
graded.add(dueHorizon);
|
||
ledger.outcomes.push(Object.assign({},item,{graded_horizons:undefined,evaluated_at:nowIso(),target_horizon_hours:dueHorizon,horizon_hours:+ageHours.toFixed(1),future_price:+future.toFixed(4),
|
||
gross_return:+gross.toFixed(4),estimated_cost_per_share:+executionCost.cost.toFixed(5),estimated_fee_per_share:+executionCost.fee.toFixed(5),
|
||
estimated_slippage_per_share:+executionCost.slippage.toFixed(5),exact_fee_schedule:executionCost.exact,
|
||
estimated_cost_return:+estimatedCost.toFixed(4),return:+outcome.toFixed(4)}));
|
||
}
|
||
const remaining=SIGNAL_EVAL_HORIZONS.filter(horizon=>!graded.has(horizon)&&ageHours<=horizon+SIGNAL_EVAL_TOLERANCE_HOURS);
|
||
if(remaining.length)stillPending.push(Object.assign({},item,{graded_horizons:[...graded].sort((a,b)=>a-b)}));
|
||
else{
|
||
const missed=SIGNAL_EVAL_HORIZONS.filter(horizon=>!graded.has(horizon)).length;
|
||
if(missed)ledger.expired_ungraded=Number(ledger.expired_ungraded||0)+missed;
|
||
}
|
||
}
|
||
stillPending.sort((a,b)=>new Date(a.observed_at||0)-new Date(b.observed_at||0));
|
||
const existing=new Set(stillPending.map(x=>x.key)),pendingPairs=new Set(stillPending.filter(currentSignalPolicy)
|
||
.filter(x=>!Object.prototype.hasOwnProperty.call(x||{},"eligible_agent_ids")||(Array.isArray(x.eligible_agent_ids)&&x.eligible_agent_ids.length>0))
|
||
.map(x=>`${x.market_id}:${x.side}`));
|
||
const bucket=Math.floor(now/(6*3600000));
|
||
const observable=prioritizeSignalObservations((suggestions||[])
|
||
.filter(x=>!x.jump_risk&&["trend","reversal"].includes(x.signal_type)&&Number(x.signal_confidence||0)>=0.56),ledger,stillPending);
|
||
for(const s of observable){
|
||
const pair=`${s.market_id}:${s.side}`,key=`${pair}:${bucket}:p${DIRECTIONAL_SIGNAL_POLICY_VERSION}:l${AGENT_LEARNING_VERSION}`;if(existing.has(key)||pendingPairs.has(pair))continue;
|
||
if(stillPending.length>=SIGNAL_LEDGER_PENDING_LIMIT)break;
|
||
existing.add(key);pendingPairs.add(pair);stillPending.push({key,market_id:String(s.market_id),event_key:String(s.url||s.event||s.market_id),observed_at:nowIso(),graded_horizons:[],side:s.side,entry_price:Number(s.entry_price),
|
||
signal_type:s.signal_type||"unknown",quality:s.quality||"unknown",category:s.category||"Other",conviction:Number(s.conviction||0),
|
||
days_to_resolution:s.days_to_resolution,trade_ready_at_observation:Boolean(s.trade_ready),signal_confidence:Number(s.signal_confidence||0),
|
||
signal_strength:Number(s.signal_strength||0),evidence_score:Number(s.evidence_score||0),evidence_source_count:Number(s.evidence_source_count||0),
|
||
price_change_1d:Number(s.price_change_1d||0),agent_learning_version:AGENT_LEARNING_VERSION,
|
||
fees_enabled:s.fees_enabled,fee_schedule:s.fee_schedule,
|
||
eligible_agent_ids:AGENTS.filter(agent=>agentObservesSuggestion(agent,s)).map(agent=>agent.id),
|
||
signal_policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION,build_version:BUILD_VERSION});
|
||
}
|
||
ledger.pending=stillPending.slice(0,SIGNAL_LEDGER_PENDING_LIMIT);
|
||
ledger.outcomes=ledger.outcomes.slice(-SIGNAL_LEDGER_OUTCOME_LIMIT);
|
||
st.signal_ledger=ledger;return ledger;
|
||
}
|
||
function signalScopeCounts(items,agentId=null){
|
||
const rows=Array.isArray(items)?items:[],legacy=rows.filter(item=>!Object.prototype.hasOwnProperty.call(item||{},"eligible_agent_ids"));
|
||
const scoped=rows.filter(item=>Array.isArray(item&&item.eligible_agent_ids)&&item.eligible_agent_ids.length>0);
|
||
const unassigned=rows.filter(item=>Object.prototype.hasOwnProperty.call(item||{},"eligible_agent_ids")&&(!Array.isArray(item.eligible_agent_ids)||item.eligible_agent_ids.length===0));
|
||
return {global:rows.length,legacy:legacy.length,scoped:scoped.length,unassigned:unassigned.length,
|
||
agent_scoped:agentId?scoped.filter(item=>item.eligible_agent_ids.includes(agentId)).length:scoped.length};
|
||
}
|
||
function buildSignalCalibration(ledger,agentId=null){
|
||
const allOutcomes=(ledger&&ledger.outcomes)||[],outcomeScopes=signalScopeCounts(allOutcomes,agentId);
|
||
const clusters={},outcomes=allOutcomes.filter(outcome=>signalEligibleForAgent(outcome,agentId));
|
||
outcomes.forEach((outcome,index)=>{
|
||
const age=Math.max(0,Date.now()-new Date(outcome.evaluated_at||0).getTime()),version=normalizedStrategyVersion(outcome.strategy_version);
|
||
const policyCompatible=currentSignalPolicy(outcome);
|
||
const versionWeight=policyCompatible?1:(version===PREVIOUS_STRATEGY_VERSION?0.55:0.25);
|
||
const weight=Math.exp(-age/(30*86400000))*versionWeight;
|
||
const ret=clamp(Number(outcome.return||0),-1,2);if(!Number.isFinite(ret)||weight<=0)return;
|
||
const marketId=String(outcome.market_id||`unidentified-observation-${index}`);
|
||
const clusterId=String(outcome.event_key||outcome.event||marketId).trim().toLowerCase()||marketId;
|
||
const horizon=Number(outcome.target_horizon_hours),baseFeatures=learningFeatures(outcome);
|
||
const featureKeys=SIGNAL_EVAL_HORIZONS.includes(horizon)?[...baseFeatures,...baseFeatures.map(key=>`${key}|horizon:${horizon}`)]:baseFeatures;
|
||
featureKeys.forEach(key=>{
|
||
const byEvent=clusters[key]||(clusters[key]={}),cluster=byEvent[clusterId]||(byEvent[clusterId]={weight:0,currentWeight:0,sum:0,observations:0});
|
||
cluster.weight+=weight;if(policyCompatible)cluster.currentWeight+=weight;
|
||
cluster.sum+=ret*weight;cluster.observations++;
|
||
});
|
||
});
|
||
const buckets={};
|
||
Object.entries(clusters).forEach(([key,byEvent])=>{
|
||
const b=buckets[key]={weight:0,currentWeight:0,sum:0,sumSq:0,wins:0,count:0,observations:0};
|
||
Object.values(byEvent).forEach(cluster=>{
|
||
const clusterWeight=Math.min(1,Number(cluster.weight||0)),ret=Number(cluster.sum||0)/Math.max(0.0001,Number(cluster.weight||0));
|
||
b.weight+=clusterWeight;b.sum+=ret*clusterWeight;b.sumSq+=ret*ret*clusterWeight;b.wins+=(ret>0?clusterWeight:0);
|
||
b.currentWeight+=Math.min(clusterWeight,Number(cluster.currentWeight||0));
|
||
b.count++;b.observations+=Number(cluster.observations||0);
|
||
});
|
||
});
|
||
const learned=Object.fromEntries(Object.entries(buckets).map(([key,b])=>[key,summarizeLearningBucket(b,12)]));
|
||
const learnedRows=Object.values(learned);
|
||
const identifiedMarkets=new Set(outcomes.map((outcome,index)=>String(outcome.market_id||`unidentified-observation-${index}`)));
|
||
const identifiedEvents=new Set(outcomes.map((outcome,index)=>String(outcome.event_key||outcome.event||outcome.market_id||`unidentified-observation-${index}`).trim().toLowerCase()));
|
||
const currentOutcomes=outcomes.filter(currentSignalPolicy);
|
||
const currentMarkets=new Set(currentOutcomes.map((outcome,index)=>String(outcome.market_id||`unidentified-current-${index}`)));
|
||
const currentEvents=new Set(currentOutcomes.map((outcome,index)=>String(outcome.event_key||outcome.event||outcome.market_id||`unidentified-current-${index}`).trim().toLowerCase()));
|
||
const allPending=(ledger&&ledger.pending)||[],pendingScopes=signalScopeCounts(allPending,agentId);
|
||
const pending=allPending.filter(item=>signalEligibleForAgent(item,agentId));
|
||
return {version:SUGGESTION_ENGINE_VERSION,policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION,agent_id:agentId,samples:outcomes.length,markets:identifiedMarkets.size,events:identifiedEvents.size,
|
||
current_samples:currentOutcomes.length,current_markets:currentMarkets.size,current_events:currentEvents.size,buckets:learned,
|
||
promoted_buckets:learnedRows.filter(r=>r.weight>=8&&r.current_weight>=5&&r.lower_bound>0.003).length,
|
||
demoted_buckets:learnedRows.filter(r=>r.weight>=8&&r.current_weight>=5&&r.upper_bound<-0.003).length,
|
||
expired_ungraded:Number(ledger&&ledger.expired_ungraded||0),pending:pending.length,
|
||
pending_global:pendingScopes.global,pending_legacy:pendingScopes.legacy,pending_scoped:pendingScopes.scoped,
|
||
pending_unassigned:pendingScopes.unassigned,pending_agent_scoped:pendingScopes.agent_scoped,
|
||
outcomes_global:outcomeScopes.global,outcomes_legacy:outcomeScopes.legacy,outcomes_scoped:outcomeScopes.scoped,
|
||
outcomes_unassigned:outcomeScopes.unassigned,outcomes_agent_scoped:outcomeScopes.agent_scoped};
|
||
}
|
||
function calibrationDecisionRecord(calibration){
|
||
const source=calibration||{},count=(key)=>source[key]!=null&&Number.isFinite(Number(source[key]))?Number(source[key]):null;
|
||
return Object.assign({},source,{
|
||
samples:count("samples")??0,markets:count("markets"),events:count("events"),
|
||
current_samples:count("current_samples")??0,current_markets:count("current_markets"),current_events:count("current_events"),
|
||
pending:count("pending")??0,pending_global:count("pending_global"),pending_legacy:count("pending_legacy"),
|
||
pending_scoped:count("pending_scoped"),pending_unassigned:count("pending_unassigned"),pending_agent_scoped:count("pending_agent_scoped"),
|
||
outcomes_global:count("outcomes_global"),outcomes_legacy:count("outcomes_legacy"),outcomes_scoped:count("outcomes_scoped"),
|
||
outcomes_unassigned:count("outcomes_unassigned"),outcomes_agent_scoped:count("outcomes_agent_scoped"),
|
||
expired_ungraded:count("expired_ungraded")??0,
|
||
promoted_buckets:count("promoted_buckets")??0,demoted_buckets:count("demoted_buckets")??0,
|
||
buckets:source.buckets||{},count_schema:1,
|
||
});
|
||
}
|
||
function calibratedOpportunity(s,calibration){
|
||
const features=learningFeatures(s),buckets=calibration&&calibration.buckets||{};
|
||
const rows=features.map(k=>buckets[k]).filter(Boolean);
|
||
const independentWeight=rows.length?Math.max(...rows.map(r=>Number(r.weight||0))):0;
|
||
const score=rows.length?rows.reduce((sum,r)=>sum+r.score,0)/rows.length:0,confidence=independentWeight/(independentWeight+20);
|
||
const horizonRows=features.map(feature=>({feature,
|
||
promotionRows:SIGNAL_PROMOTION_HORIZONS.map(horizon=>buckets[`${feature}|horizon:${horizon}`]),
|
||
riskRows:SIGNAL_EVAL_HORIZONS.map(horizon=>buckets[`${feature}|horizon:${horizon}`])}));
|
||
const rowIsCurrent=(row)=>Number(row&&row.weight||0)>=8&&Number(row&&row.current_weight||0)>=5;
|
||
const positiveRows=horizonRows.filter(group=>group.promotionRows.every(row=>rowIsCurrent(row)&&Number(row.lower_bound||0)>0.003));
|
||
const negativeRows=horizonRows.filter(group=>group.riskRows.some(row=>rowIsCurrent(row)&&Number(row.upper_bound||0)<-0.003));
|
||
const promoted=positiveRows.length>=2&&negativeRows.length===0;
|
||
const demoted=negativeRows.length>=2&&positiveRows.length===0;
|
||
const trustedScore=promoted?positiveRows.reduce((sum,group)=>sum+Math.min(...group.promotionRows.map(row=>Number(row.lower_bound||0))),0)/positiveRows.length
|
||
:(demoted?negativeRows.reduce((sum,group)=>sum+Math.min(...group.riskRows.filter(rowIsCurrent).map(row=>Number(row.upper_bound||0))),0)/negativeRows.length:score*0.10);
|
||
const state=promoted?"promoted":(demoted?"demoted":"observing");
|
||
return {score:+score.toFixed(4),confidence:+confidence.toFixed(3),state,promoted,demoted,
|
||
supporting_features:promoted?positiveRows.length:negativeRows.length,
|
||
multiplier:+clamp(1+trustedScore*1.8,0.82,1.15).toFixed(3),allowed:!demoted};
|
||
}
|
||
function tradeReturnForLearning(trade,closed){
|
||
const basis=Math.max(1,Number(trade.original_cost||trade.cost||0));
|
||
const pnl=closed?Number(trade.realized_pnl||0):Number(trade.unrealized_pnl||0)+Number(trade.partial_realized_pnl||0);
|
||
return clamp(pnl/basis,-1,2);
|
||
}
|
||
function buildAdaptiveProfile(p){
|
||
const buckets={},currentBuckets={},observations=[],currentClosedObservations=[],currentClosedReturns=[];
|
||
const add=(trade,closed)=>{
|
||
const version=normalizedStrategyVersion(trade.strategy_version);if(version<34)return;
|
||
const age=Math.max(0,Date.now()-new Date(trade.closed_at||trade.opened_at||0).getTime());
|
||
const recency=Number.isFinite(age)?Math.exp(-age/(45*86400000)):0.4;
|
||
const versionWeight=version===SUGGESTION_ENGINE_VERSION?1:(version===PREVIOUS_STRATEGY_VERSION?0.45:0.20);
|
||
const weight=recency*versionWeight*(closed?1:Math.min(0.30,Math.max(0.08,daysHeld(trade)/20)));
|
||
const ret=tradeReturnForLearning(trade,closed);
|
||
if(!Number.isFinite(ret)||weight<=0)return;
|
||
observations.push({ret,weight,closed,version});
|
||
if(closed&&version===SUGGESTION_ENGINE_VERSION){currentClosedObservations.push({ret,weight});currentClosedReturns.push(ret);}
|
||
learningFeatures(trade).forEach(key=>{
|
||
const b=buckets[key]||(buckets[key]={weight:0,sum:0,sumSq:0,wins:0,count:0});
|
||
b.weight+=weight;b.sum+=ret*weight;b.sumSq+=ret*ret*weight;b.wins+=(ret>0?weight:0);b.count++;
|
||
if(version===SUGGESTION_ENGINE_VERSION){
|
||
const current=currentBuckets[key]||(currentBuckets[key]={weight:0,currentWeight:0,sum:0,sumSq:0,wins:0,count:0});
|
||
current.weight+=weight;current.currentWeight+=weight;current.sum+=ret*weight;current.sumSq+=ret*ret*weight;
|
||
current.wins+=(ret>0?weight:0);current.count++;
|
||
}
|
||
});
|
||
};
|
||
(p.closed||[]).forEach(t=>add(t,true));
|
||
(p.positions||[]).filter(t=>daysHeld(t)>=1).forEach(t=>add(t,false));
|
||
const learned=Object.fromEntries(Object.entries(buckets).map(([k,b])=>[k,summarizeLearningBucket(b,6)]));
|
||
const currentLearned=Object.fromEntries(Object.entries(currentBuckets).map(([k,b])=>[k,summarizeLearningBucket(b,6)]));
|
||
const totalWeight=observations.reduce((s,x)=>s+x.weight,0),totalSum=observations.reduce((s,x)=>s+x.ret*x.weight,0);
|
||
const globalScore=totalSum/(totalWeight+10);
|
||
const currentClosedWeight=currentClosedObservations.reduce((s,x)=>s+x.weight,0);
|
||
const currentClosedSum=currentClosedObservations.reduce((s,x)=>s+x.ret*x.weight,0);
|
||
const ranked=Object.entries(learned).filter(([k,v])=>!k.startsWith("side:")&&v.weight>=1)
|
||
.sort((a,b)=>b[1].score-a[1].score);
|
||
return {version:SUGGESTION_ENGINE_VERSION,samples:(p.closed||[]).filter(t=>normalizedStrategyVersion(t.strategy_version)>=34).length,
|
||
effective_samples:+totalWeight.toFixed(2),global_score:+globalScore.toFixed(4),buckets:learned,current_buckets:currentLearned,
|
||
current_samples:currentClosedReturns.length,current_effective_samples:+currentClosedWeight.toFixed(2),
|
||
current_global_score:+(currentClosedSum/(currentClosedWeight+4)).toFixed(4),
|
||
worst_return:currentClosedReturns.length?+Math.min(...currentClosedReturns).toFixed(4):0,
|
||
large_loss_count:currentClosedReturns.filter(x=>x<=-0.30).length,
|
||
best:ranked[0]?{feature:ranked[0][0],score:ranked[0][1].score}:null,
|
||
worst:ranked.length?{feature:ranked[ranked.length-1][0],score:ranked[ranked.length-1][1].score}:null};
|
||
}
|
||
function sportsFavoritePilotProfile(p){
|
||
const grouped=new Map();
|
||
(p&&p.sports_favorite_outcomes||[]).filter(outcome=>Number(outcome.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION)
|
||
.forEach((outcome,index)=>{
|
||
const key=String(outcome.event_key||outcome.market_id||`sports-shadow-${index}`);
|
||
const rows=grouped.get(key)||[];rows.push(clamp(Number(outcome.net_return||0),-1,2));grouped.set(key,rows);
|
||
});
|
||
(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="sports-contest-no-pilot"
|
||
&&Number(trade.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION).forEach((trade,index)=>{
|
||
const key=String(trade.event_key||trade.event||trade.market_id||`sports-pilot-${index}`);
|
||
const basis=Math.max(1,Number(trade.original_cost||trade.cost||0));
|
||
const rows=grouped.get(key)||[];rows.push(clamp(Number(trade.realized_pnl||0)/basis,-1,2));grouped.set(key,rows);
|
||
});
|
||
const returns=[...grouped.values()].map(rows=>rows.reduce((sum,value)=>sum+value,0)/rows.length),events=returns.length;
|
||
const mean=events?returns.reduce((sum,value)=>sum+value,0)/events:0;
|
||
const variance=events>1?returns.reduce((sum,value)=>sum+(value-mean)**2,0)/(events-1):0;
|
||
const margin=1.645*Math.sqrt(variance/Math.max(1,events)),lower=mean-margin,upper=mean+margin;
|
||
const pnl=(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="sports-contest-no-pilot"
|
||
&&Number(trade.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION).reduce((sum,trade)=>sum+Number(trade.realized_pnl||0),0);
|
||
const lossLimit=-Math.max(1,Number(p&&p.starting_balance||STARTING_BALANCE))*0.01;
|
||
const demoted=(events>=SPORTS_FAVORITE_VERDICT_EVENTS&&(mean<=-0.05||upper<=0))||pnl<=lossLimit;
|
||
const shadowPnl=(p&&p.sports_favorite_outcomes||[]).filter(outcome=>Number(outcome.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION)
|
||
.reduce((sum,outcome)=>sum+Number(outcome.pnl_per_100||0),0);
|
||
const forwardPositive=events>=SPORTS_FAVORITE_PROMOTION_EVENTS&&lower>0.005;
|
||
const maximumPositive=events>=SPORTS_FAVORITE_MAX_PROMOTION_EVENTS&&lower>0.01;
|
||
const positionPct=demoted?0:(maximumPositive?SPORTS_FAVORITE_MAX_POSITION_PCT:(forwardPositive?SPORTS_FAVORITE_PROMOTED_POSITION_PCT:SPORTS_FAVORITE_PILOT_POSITION_PCT));
|
||
return {state:demoted?"demoted":(maximumPositive?"max-promoted":(forwardPositive?"promoted":"exploration")),allowed:!demoted,promoted:forwardPositive,max_promoted:maximumPositive,demoted,events,
|
||
mean:+mean.toFixed(4),lower:+lower.toFixed(4),upper:+upper.toFixed(4),pnl:+pnl.toFixed(2),
|
||
shadow_pnl_per_100:+shadowPnl.toFixed(2),active_shadows:(p&&p.sports_favorite_shadows||[])
|
||
.filter(row=>Number(row.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION).length,
|
||
position_pct:positionPct};
|
||
}
|
||
function applySportsFavoriteForwardPromotion(s,p){
|
||
if(!s||(s.signal_type||"")!=="sports-contest-no-pilot")return s;
|
||
const profile=sportsFavoritePilotProfile(p);
|
||
if(profile.demoted)return Object.assign({},s,{trade_ready:false,entry_candidate:false,watch_only:true,
|
||
rationale:`Forward suspension: ${profile.events} independent contests produced a ${(profile.mean*100).toFixed(2)}% mean and ${(profile.upper*100).toFixed(2)}% 90% upper confidence bound, so this lane is back to zero capital. ${s.rationale||""}`});
|
||
return Object.assign({},s,{trade_ready:true,entry_candidate:true,audited_observation_only:false,adaptive_promotion:true,watch_only:false,
|
||
rationale:`Adaptive ${profile.state} size ${(profile.position_pct*100).toFixed(2)}% after ${profile.events} independent settled contest${profile.events===1?"":"s"}. ${s.rationale||""}`});
|
||
}
|
||
function stageSportsFavoriteShadows(p,suggestions,{execute=true}={}){
|
||
p.sports_favorite_shadows=Array.isArray(p.sports_favorite_shadows)?p.sports_favorite_shadows:[];
|
||
p.sports_favorite_outcomes=Array.isArray(p.sports_favorite_outcomes)?p.sports_favorite_outcomes:[];
|
||
if(!execute)return {active:p.sports_favorite_shadows.length,staged:0,candidates:0};
|
||
p.sports_favorite_shadows=p.sports_favorite_shadows.filter(row=>Number(row.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION);
|
||
const sportsRows=[...p.sports_favorite_shadows,...p.sports_favorite_outcomes,...(p.positions||[]),...(p.closed||[])];
|
||
const usedEvents=new Set(sportsRows
|
||
.filter(row=>Number(row.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION)
|
||
.map(row=>String(row.event_key||row.event||row.market_id)));
|
||
const usedMarkets=new Set(sportsRows
|
||
.filter(row=>Number(row.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION).map(row=>String(row.market_id)));
|
||
const candidates=(suggestions||[]).filter(s=>(s.signal_type||"")==="sports-contest-no-pilot");
|
||
let staged=0;
|
||
for(const s of candidates){
|
||
if(p.sports_favorite_shadows.length>=SPORTS_FAVORITE_SHADOW_LIMIT)break;
|
||
const eventKey=String(s.event_key||s.event||s.market_id),marketId=String(s.market_id);
|
||
if(usedEvents.has(eventKey)||usedMarkets.has(marketId))continue;
|
||
const openedAt=cycleIso();
|
||
p.sports_favorite_shadows.push({id:`sports-shadow:${marketId}:${Date.now()}:${staged}`,market_id:marketId,event_key:eventKey,
|
||
question:s.question,event:s.event||"",side:s.side,entry_price:Number(s.entry_price),market_price:Number(s.market_price),
|
||
current_price:Number(s.market_price),game_start:s.game_start,hours_to_start:s.hours_to_start,opened_at:openedAt,
|
||
strategy_version:SUGGESTION_ENGINE_VERSION,sports_contest_strategy_version:SPORTS_CONTEST_NO_STRATEGY_VERSION,
|
||
modeled_cost:+Math.max(0,Number(s.entry_price)-Number(s.market_price)).toFixed(4),url:s.url||""});
|
||
p.history.push({date:logDay(),action:"SPORTS_SHADOW",question:s.question,side:s.side,
|
||
detail:`Zero-capital forward observation recorded ${s.side} '${s.question.slice(0,40)}' at modeled ${pct(s.entry_price)} (${pct(s.market_price)} midpoint + ${((Number(s.entry_price)-Number(s.market_price))*100).toFixed(2)}c executable friction) · one observation for event ${eventKey} · cash unchanged`});
|
||
usedEvents.add(eventKey);usedMarkets.add(marketId);staged++;
|
||
}
|
||
return {active:p.sports_favorite_shadows.length,staged,candidates:candidates.length};
|
||
}
|
||
function manageSportsFavoriteShadows(p,priceMap,{execute=true}={}){
|
||
p.sports_favorite_shadows=(Array.isArray(p.sports_favorite_shadows)?p.sports_favorite_shadows:[])
|
||
.filter(row=>Number(row.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION);
|
||
p.sports_favorite_outcomes=Array.isArray(p.sports_favorite_outcomes)?p.sports_favorite_outcomes:[];
|
||
if(!execute)return {active:p.sports_favorite_shadows.length,completed:0};
|
||
const keep=[];let completed=0;
|
||
for(const row of p.sports_favorite_shadows){
|
||
const fresh=priceMap&&priceMap[String(row.market_id)];
|
||
if(!fresh){keep.push(row);continue;}
|
||
const price=Number(row.side==="YES"?fresh.yes_price:fresh.no_price);
|
||
if(Number.isFinite(price))row.current_price=+price.toFixed(4);
|
||
if(!marketIsSettled(fresh)){if(marketIsSuspended(fresh))row.price_status="orders-paused";keep.push(row);continue;}
|
||
const entry=Math.max(0.01,Number(row.entry_price||0)),netReturn=clamp(price/entry-1,-1,2),pnlPer100=netReturn*100;
|
||
if(!p.sports_favorite_outcomes.some(outcome=>outcome.id===row.id))p.sports_favorite_outcomes.push(Object.assign({},row,{closed_at:cycleIso(),
|
||
exit_price:+price.toFixed(4),net_return:+netReturn.toFixed(4),pnl_per_100:+pnlPer100.toFixed(2)}));
|
||
p.history.push({date:logDay(),action:"SPORTS_GRADE",question:row.question,side:row.side,
|
||
detail:`Forward sports observation settled at ${pct(price)} · modeled net return ${(netReturn*100).toFixed(2)}% after ${((row.modeled_cost||0)*100).toFixed(2)}c executable friction · portfolio cash unchanged`});
|
||
completed++;
|
||
}
|
||
p.sports_favorite_shadows=keep;
|
||
p.sports_favorite_outcomes=p.sports_favorite_outcomes.slice(-SPORTS_FAVORITE_OUTCOME_LIMIT);
|
||
return {active:keep.length,completed};
|
||
}
|
||
let SHOCK_FADE_HISTORY_CACHE={captured_at:0,history:{}};
|
||
function historyPointAtOrBefore(points,target,maxLagSeconds=4500){
|
||
let low=0,high=(points||[]).length-1,found=null;
|
||
while(low<=high){const mid=(low+high)>>1,point=points[mid];if(Number(point.t)<=target){found=point;low=mid+1;}else high=mid-1;}
|
||
return found&&target-Number(found.t)<=maxLagSeconds?found:null;
|
||
}
|
||
function shockFadeExecutableEntry(m,side){
|
||
const midpoint=Number(side==="YES"?m&&m.yes_price:m&&m.no_price);
|
||
const quoted=side==="YES"?Number(m&&m.best_ask):(Number(m&&m.best_bid)>0?1-Number(m.best_bid):NaN);
|
||
if(!(midpoint>0&&midpoint<1&"ed>=midpoint&"ed<1))return null;
|
||
const price=clamp(quoted+SHOCK_FADE_SLIPPAGE_BUFFER,0.01,0.99),cost=price-midpoint;
|
||
return price>=SHOCK_FADE_MIN_ENTRY&&price<=SHOCK_FADE_MAX_ENTRY&&cost<=SHOCK_FADE_MAX_EXECUTION_COST+0.00001?{midpoint,quoted,price,cost}:null;
|
||
}
|
||
function shockFadeExecutableExit(fresh,side){
|
||
if(!fresh)return null;
|
||
if(marketIsSettled(fresh))return Number(side==="YES"?fresh.yes_price:fresh.no_price);
|
||
const quoted=side==="YES"?Number(fresh.best_bid):(Number(fresh.best_ask)>0?1-Number(fresh.best_ask):NaN);
|
||
return quoted>0&"ed<1?clamp(quoted-SHOCK_FADE_SLIPPAGE_BUFFER,0.01,0.99):null;
|
||
}
|
||
function shockFadeContractEligible(m){
|
||
if(!m||hardSettlementJumpRisk(m))return false;
|
||
const days=Number(m.days_to_resolution),minimumDays=(SHOCK_FADE_EXIT_HOURS+SHOCK_FADE_GRADE_TOLERANCE_HOURS)/24;
|
||
return Number.isFinite(days)&&days>=minimumDays;
|
||
}
|
||
function shockFadeRiskKey(row){
|
||
if(row&&row.shock_risk_key)return String(row.shock_risk_key);
|
||
const text=`${row&&row.question||""} ${row&&row.event||""}`.toLowerCase();
|
||
const cryptoAssets=[
|
||
["bitcoin",/\b(?:bitcoin|btc)\b/],["ethereum",/\b(?:ethereum|ether|eth)\b/],["solana",/\b(?:solana|sol)\b/],
|
||
["xrp",/\b(?:xrp|ripple)\b/],["dogecoin",/\b(?:dogecoin|doge)\b/],["cardano",/\b(?:cardano|ada)\b/],
|
||
["bnb",/\b(?:bnb|binance coin)\b/],
|
||
];
|
||
const asset=cryptoAssets.find(([,pattern])=>pattern.test(text));
|
||
if(asset)return `crypto:${asset[0]}`;
|
||
return `event:${String(row&&row.event_key||row&&row.event||row&&row.market_id||"").trim().toLowerCase()}`;
|
||
}
|
||
function shockFadeClaimKeys(row,{includeRisk=true}={}){
|
||
const event=String(row&&row.event_key||row&&row.event||row&&row.market_id||"").trim().toLowerCase(),keys=[];
|
||
if(event)keys.push(`event:${event}`);
|
||
const risk=shockFadeRiskKey(row);if(includeRisk&&risk)keys.push(`risk:${risk}`);
|
||
return keys;
|
||
}
|
||
function shockFadeEvidenceKey(row,index=0,prefix="shock"){
|
||
const risk=shockFadeRiskKey(row),observed=Date.parse(row&&row.shock_observed_at||row&&row.opened_at||"");
|
||
if(risk&&Number.isFinite(observed))return `${risk}|window:${Math.floor(observed/(SHOCK_FADE_SIGNAL_WINDOW_HOURS*3600000))}`;
|
||
return String(row&&row.event_key||row&&row.market_id||`${prefix}-${index}`);
|
||
}
|
||
function shockFadeSuggestion(m,points,observedAt=Date.now()/1000){
|
||
if(!m||!(m.clob_token_ids||[])[0]||m.closed||m.accepting_orders===false||!shockFadeContractEligible(m))return null;
|
||
const now=Number(observedAt),p1=historyPointAtOrBefore(points,now-3600),p2=historyPointAtOrBefore(points,now-2*3600),p3=historyPointAtOrBefore(points,now-SHOCK_FADE_SIGNAL_WINDOW_HOURS*3600);
|
||
const current=Number(m.yes_price);if(!p1||!p2||!p3||!(current>0&¤t<1))return null;
|
||
const move1=current-Number(p1.p),priorMove1=Number(p1.p)-Number(p2.p),move3=current-Number(p3.p);
|
||
if(Math.abs(move3)<SHOCK_FADE_MIN_MOVE||move1*move3<=0||Math.abs(move1)<Math.abs(priorMove1))return null;
|
||
const side=move3>0?"NO":"YES",execution=shockFadeExecutableEntry(m,side);if(!execution)return null;
|
||
const eventKey=String(m.event_id||m.event||m.id),conviction=+clamp(72+Math.abs(move3)*75+Math.abs(move1)*35,72,88).toFixed(1);
|
||
return {market_id:String(m.id),question:m.question,event:m.event||"",event_key:eventKey,shock_risk_key:shockFadeRiskKey(m),url:m.url||"",category:m.category||"Other",tags:m.tags||[],
|
||
clob_yes:m.clob_token_ids[0],clob_no:m.clob_token_ids[1],yes_price:m.yes_price,no_price:m.no_price,
|
||
fair_value:+execution.price.toFixed(4),edge:0,net_edge:0,friction:+execution.cost.toFixed(4),chase_penalty:0,
|
||
evidence_score:0.66,evidence_source_count:1,quality:"shock-fade-shadow",side,market_price:+execution.midpoint.toFixed(4),entry_price:+execution.price.toFixed(4),
|
||
conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,spread:m.spread,
|
||
price_change_1h:+move1.toFixed(4),price_change_1d:m.price_change_1d,price_change_1w:m.price_change_1w,
|
||
shock_move_1h:+move1.toFixed(4),shock_prior_move_1h:+priorMove1.toFixed(4),shock_move_3h:+move3.toFixed(4),shock_observed_at:new Date(now*1000).toISOString(),
|
||
momentum_strength:+clamp(Math.abs(move3)/0.16,0,1).toFixed(3),signal_strength:0.74,signal_confidence:0.66,signal_type:"shock-fade-shadow",
|
||
trade_ready:false,entry_candidate:false,audited_observation_only:true,adaptive_promotion:false,watch_only:true,jump_risk:true,requires_live:true,
|
||
shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,days_to_resolution:m.days_to_resolution,
|
||
pilot_prior:{observations:534894,markets:2000,train_events:93,validation_events:85,chronological_holdout_events:159,event_holdout_events:80,
|
||
train_lower_90_at_two_cents:0.04009,validation_lower_90_at_two_cents:0.04546,chronological_holdout_lower_90_at_two_cents:0.03627,
|
||
event_holdout_lower_90_at_two_cents:0.01079,independent_markets:1000,independent_observations:246580,
|
||
independent_event_holdout_events:61,independent_event_holdout_lower_90:0.00367,independent_train_lower_90:-0.00168,
|
||
independent_chronological_holdout_lower_90:-0.0089,modeled_cost_cents:2,return_winsorization:[-1,2],strictly_promoted:true},
|
||
drivers:["8%+ three-hour shock","accelerating final hour","contract survives the full holding window","no path barrier or exact numeric range","8–92% opposite-side entry","12-hour executable exit","two-cent stress-tested"],
|
||
rationale:`Zero-capital Strategy ${SHOCK_FADE_STRATEGY_VERSION} research: YES moved ${(move3*100).toFixed(2)} points in three hours and ${(move1*100).toFixed(2)} in the accelerating final hour versus ${(priorMove1*100).toFixed(2)} previously. The strategy fades that move with ${side} at modeled ${pct(execution.price)}, ${(execution.cost*100).toFixed(2)} cents above midpoint, and grades an executable exit after ${SHOCK_FADE_EXIT_HOURS} hours. The contract-safe rule kept positive primary confidence bounds, but the untouched next-1,000 chronological holdout was negative and its event-holdout interval crossed zero. Capital therefore stays at zero until ${SHOCK_FADE_QUALIFICATION_EVENTS} new independent forward events produce a positive lower bound above 1%.`};
|
||
}
|
||
async function fetchShockFadeSuggestions(markets){
|
||
const eligible=(markets||[]).filter(m=>m&&m.clob_token_ids&&m.clob_token_ids[0]);if(!eligible.length)return [];
|
||
const now=Date.now();
|
||
if(now-Number(SHOCK_FADE_HISTORY_CACHE.captured_at||0)>=SHOCK_FADE_HISTORY_CACHE_MS){
|
||
const history={},tokens=eligible.map(m=>String(m.clob_token_ids[0]));
|
||
const batches=[];for(let index=0;index<tokens.length;index+=20)batches.push(tokens.slice(index,index+20));
|
||
const payloads=await mapWithConcurrency(batches,6,async batch=>{
|
||
try{
|
||
const response=await fetchWithTimeout(`${CLOB}/batch-prices-history`,{method:"POST",headers:{"content-type":"application/json"},
|
||
body:JSON.stringify({markets:batch,start_ts:Math.floor(now/1000)-5*3600,end_ts:Math.floor(now/1000),interval:"1m",fidelity:60})},PRICE_REQUEST_TIMEOUT_MS*2);
|
||
return response.ok?await response.json():null;
|
||
}catch(e){return null;}
|
||
});
|
||
payloads.forEach(payload=>Object.entries(payload&&payload.history||{}).forEach(([token,points])=>{history[String(token)]=(points||[])
|
||
.map(point=>({t:Number(point.t),p:Number(point.p)})).filter(point=>Number.isFinite(point.t)&&Number.isFinite(point.p)).sort((a,b)=>a.t-b.t);}));
|
||
SHOCK_FADE_HISTORY_CACHE={captured_at:Object.keys(history).length?now:0,history};
|
||
}
|
||
const observedAt=now/1000;
|
||
return eligible.map(m=>shockFadeSuggestion(m,SHOCK_FADE_HISTORY_CACHE.history[String(m.clob_token_ids[0])]||[],observedAt)).filter(Boolean)
|
||
.sort((a,b)=>Math.abs(b.shock_move_3h)-Math.abs(a.shock_move_3h));
|
||
}
|
||
function shockFadePilotProfile(p){
|
||
const grouped=new Map();
|
||
(p&&p.shock_fade_outcomes||[]).filter(outcome=>Number(outcome.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).forEach((outcome,index)=>{
|
||
const key=shockFadeEvidenceKey(outcome,index,"shock"),rows=grouped.get(key)||[];
|
||
rows.push(clamp(Number(outcome.net_return||0),-1,2));grouped.set(key,rows);
|
||
});
|
||
(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot"&&Number(trade.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).forEach((trade,index)=>{
|
||
const key=shockFadeEvidenceKey(trade,index,"shock-capital"),basis=Math.max(1,Number(trade.original_cost||trade.cost||0)),rows=grouped.get(key)||[];
|
||
rows.push(clamp(Number(trade.realized_pnl||0)/basis,-1,2));grouped.set(key,rows);
|
||
});
|
||
const returns=[...grouped.values()].map(rows=>rows.reduce((sum,value)=>sum+value,0)/rows.length),events=returns.length;
|
||
const mean=events?returns.reduce((sum,value)=>sum+value,0)/events:0,variance=events>1?returns.reduce((sum,value)=>sum+(value-mean)**2,0)/(events-1):0;
|
||
const margin=1.645*Math.sqrt(variance/Math.max(1,events)),lower=mean-margin,upper=mean+margin;
|
||
const pnl=(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="shock-fade-pilot"&&Number(trade.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).reduce((sum,trade)=>sum+Number(trade.realized_pnl||0),0);
|
||
const demoted=(events>=SHOCK_FADE_VERDICT_EVENTS&&(mean<=-0.02||upper<=0))||pnl<=-Math.max(1,Number(p&&p.starting_balance||STARTING_BALANCE))*0.03;
|
||
const qualified=!demoted&&events>=SHOCK_FADE_QUALIFICATION_EVENTS&&lower>0.01;
|
||
const promoted=qualified&&events>=SHOCK_FADE_PROMOTION_EVENTS&&lower>0.015;
|
||
const allowed=!demoted&&(SHOCK_FADE_BACKTEST_APPROVED||qualified);
|
||
return {state:demoted?"demoted":(promoted?"forward-promoted":(qualified?"forward-qualified":"shadow-only")),allowed,qualified,promoted,demoted,events,mean:+mean.toFixed(4),lower:+lower.toFixed(4),upper:+upper.toFixed(4),
|
||
pnl:+pnl.toFixed(2),shadow_pnl_per_100:+(mean*100).toFixed(2),active_shadows:(p&&p.shock_fade_shadows||[]).length,
|
||
expired_ungraded:Number(p&&p.shock_fade_expired_ungraded||0),position_pct:allowed?(promoted?SHOCK_FADE_PROMOTED_POSITION_PCT:SHOCK_FADE_INITIAL_POSITION_PCT):0};
|
||
}
|
||
function applyShockFadeForwardPromotion(s,p,profileOverride=null){
|
||
if(!s||(s.signal_type||"")!=="shock-fade-shadow")return s;const profile=profileOverride||shockFadePilotProfile(p);if(!profile.allowed)return s;
|
||
return Object.assign({},s,{quality:"shock-fade-pilot",signal_type:"shock-fade-pilot",trade_ready:true,entry_candidate:true,audited_observation_only:false,
|
||
adaptive_promotion:profile.promoted,watch_only:false,rationale:profile.promoted
|
||
?`Forward size promotion: ${profile.events} independent Strategy ${SHOCK_FADE_STRATEGY_VERSION} events produced a ${(profile.mean*100).toFixed(2)}% mean and ${(profile.lower*100).toFixed(2)}% 90% lower bound after executable costs. Paper size increases to 1.5% with a 5% total cap. ${s.rationale||""}`
|
||
:profile.qualified
|
||
?`Forward capital qualification: ${profile.events} independent Strategy ${SHOCK_FADE_STRATEGY_VERSION} events produced a ${(profile.mean*100).toFixed(2)}% mean and ${(profile.lower*100).toFixed(2)}% 90% lower bound after executable costs. Paper capital begins at 1% under a 5% total cap. ${s.rationale||""}`
|
||
:`The frozen backtest did not pass every independent Strategy ${SHOCK_FADE_STRATEGY_VERSION} holdout, so this remains a zero-capital observation until shared forward evidence qualifies it. ${s.rationale||""}`});
|
||
}
|
||
function stageShockFadeShadows(p,suggestions,{execute=true,usedEventKeys=null,usedClaimKeys=null}={}){
|
||
p.shock_fade_shadows=Array.isArray(p.shock_fade_shadows)?p.shock_fade_shadows:[];p.shock_fade_outcomes=Array.isArray(p.shock_fade_outcomes)?p.shock_fade_outcomes:[];
|
||
if(!execute)return {active:p.shock_fade_shadows.length,staged:0,candidates:0};
|
||
const activeRows=[...p.shock_fade_shadows,...(p.positions||[]).filter(row=>row.signal_type==="shock-fade-pilot")];
|
||
const historicRows=[...p.shock_fade_outcomes,...(p.closed||[]).filter(row=>row.signal_type==="shock-fade-pilot")];
|
||
const usedClaims=new Set([...(usedClaimKeys||usedEventKeys||[]),...activeRows.flatMap(row=>shockFadeClaimKeys(row)),...historicRows.flatMap(row=>shockFadeClaimKeys(row,{includeRisk:false}))]);
|
||
const usedMarkets=new Set([...activeRows,...historicRows].map(row=>String(row.market_id)));
|
||
const candidates=(suggestions||[]).filter(s=>(s.signal_type||"")==="shock-fade-shadow");let staged=0;
|
||
for(const s of candidates){
|
||
if(p.shock_fade_shadows.length>=SHOCK_FADE_SHADOW_LIMIT)break;const eventKey=String(s.event_key||s.market_id),marketId=String(s.market_id),claimKeys=shockFadeClaimKeys(s);
|
||
if(claimKeys.some(key=>usedClaims.has(key))||usedMarkets.has(marketId))continue;const openedAt=cycleIso();
|
||
p.shock_fade_shadows.push({id:`shock-shadow:${marketId}:${Date.now()}:${staged}`,market_id:marketId,event_key:eventKey,shock_risk_key:shockFadeRiskKey(s),question:s.question,event:s.event||"",side:s.side,
|
||
entry_price:Number(s.entry_price),market_price:Number(s.market_price),current_price:Number(s.market_price),opened_at:openedAt,target_at:new Date(new Date(openedAt).getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString(),
|
||
shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,
|
||
modeled_entry_cost:Number(s.friction||0),url:s.url||""});
|
||
p.history.push({date:logDay(),action:"SHOCK_SHADOW",question:s.question,side:s.side,detail:`Zero-capital ${SHOCK_FADE_EXIT_HOURS}h shock fade recorded ${s.side} at modeled ${pct(s.entry_price)} after a ${(Number(s.shock_move_3h)*100).toFixed(2)}-point YES move · cash unchanged`});
|
||
claimKeys.forEach(key=>usedClaims.add(key));usedMarkets.add(marketId);staged++;
|
||
}
|
||
return {active:p.shock_fade_shadows.length,staged,candidates:candidates.length};
|
||
}
|
||
function shockFadeTargetHours(row){
|
||
const opened=Date.parse(row&&row.opened_at||""),target=Date.parse(row&&(row.target_at||row.target_exit_at)||"");
|
||
if(Number.isFinite(opened)&&Number.isFinite(target)&&target>opened)return (target-opened)/3600000;
|
||
const version=Number(row&&row.shock_strategy_version||0);
|
||
return version>=3?12:(version===2?24:3);
|
||
}
|
||
function manageShockFadeShadows(p,priceMap,{execute=true}={}){
|
||
p.shock_fade_shadows=Array.isArray(p.shock_fade_shadows)?p.shock_fade_shadows:[];p.shock_fade_outcomes=Array.isArray(p.shock_fade_outcomes)?p.shock_fade_outcomes:[];
|
||
if(!execute)return {active:p.shock_fade_shadows.length,completed:0,expired:0};
|
||
const keep=[];let completed=0,expired=0;
|
||
for(const row of p.shock_fade_shadows){
|
||
const age=hoursSince(row.opened_at),targetHours=shockFadeTargetHours(row),fresh=priceMap&&priceMap[String(row.market_id)];
|
||
if(age<targetHours){keep.push(row);continue;}
|
||
const exit=shockFadeExecutableExit(fresh,row.side);
|
||
if(!(exit>0&&exit<1)){
|
||
if(age<=targetHours+SHOCK_FADE_GRADE_TOLERANCE_HOURS){keep.push(row);continue;}
|
||
p.shock_fade_expired_ungraded=Number(p.shock_fade_expired_ungraded||0)+1;expired++;
|
||
p.history.push({date:logDay(),action:"SHOCK_EXPIRED",question:row.question,side:row.side,detail:`${targetHours}-hour shock observation expired without an executable exit inside the allowed window; no return was invented`});continue;
|
||
}
|
||
const entry=Math.max(0.01,Number(row.entry_price||0)),netReturn=clamp(exit/entry-1,-1,2);
|
||
if(!p.shock_fade_outcomes.some(outcome=>outcome.id===row.id))p.shock_fade_outcomes.push(Object.assign({},row,{closed_at:cycleIso(),exit_price:+exit.toFixed(4),net_return:+netReturn.toFixed(4),pnl_per_100:+(netReturn*100).toFixed(2),horizon_hours:+age.toFixed(2)}));
|
||
p.history.push({date:logDay(),action:"SHOCK_GRADE",question:row.question,side:row.side,detail:`${targetHours}-hour shock fade exited at executable ${pct(exit)} · modeled net return ${(netReturn*100).toFixed(2)}% · cash unchanged`});completed++;
|
||
}
|
||
p.shock_fade_shadows=keep;p.shock_fade_outcomes=p.shock_fade_outcomes.slice(-SHOCK_FADE_OUTCOME_LIMIT);
|
||
return {active:keep.length,completed,expired};
|
||
}
|
||
function currentShockClaimKeys(st){
|
||
const claims=new Set();
|
||
AGENTS.forEach(agent=>{
|
||
const p=st&&st.agents&&st.agents[agent.id]||{};
|
||
const active=[...(p.positions||[]).filter(row=>row.signal_type==="shock-fade-pilot"),...(p.shock_fade_shadows||[])];
|
||
const historic=[...(p.closed||[]).filter(row=>row.signal_type==="shock-fade-pilot"),...(p.shock_fade_outcomes||[])];
|
||
active.forEach(row=>shockFadeClaimKeys(row).forEach(key=>claims.add(key)));
|
||
historic.filter(row=>Number(row.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION).forEach(row=>shockFadeClaimKeys(row,{includeRisk:false}).forEach(key=>claims.add(key)));
|
||
});
|
||
return claims;
|
||
}
|
||
function sharedShockFadePilotProfile(st){
|
||
const portfolios=[...SHOCK_FADE_ADOPTER_IDS].map(id=>st&&st.agents&&st.agents[id]).filter(Boolean);
|
||
const combined={starting_balance:portfolios.reduce((sum,p)=>sum+Math.max(1,Number(p.starting_balance||STARTING_BALANCE)),0),
|
||
shock_fade_outcomes:portfolios.flatMap(p=>p.shock_fade_outcomes||[]),
|
||
shock_fade_shadows:portfolios.flatMap(p=>p.shock_fade_shadows||[]),
|
||
closed:portfolios.flatMap(p=>p.closed||[]),
|
||
shock_fade_expired_ungraded:portfolios.reduce((sum,p)=>sum+Number(p.shock_fade_expired_ungraded||0),0)};
|
||
return Object.assign(shockFadePilotProfile(combined),{scope:"shared-adopters",adopters:portfolios.length});
|
||
}
|
||
function resolutionWeekNoPilotProfile(p){
|
||
const grouped=new Map();
|
||
(p&&p.resolution_week_no_outcomes||[]).filter(outcome=>Number(outcome.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION)
|
||
.forEach((outcome,index)=>{
|
||
const key=String(outcome.event_key||outcome.market_id||`resolution-shadow-${index}`),row=grouped.get(key)||{values:[],horizon:Number(outcome.target_horizon_days||4)};
|
||
row.values.push(clamp(Number(outcome.net_return||0),-1,2));grouped.set(key,row);
|
||
});
|
||
(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="resolution-week-no-pilot"
|
||
&&Number(trade.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION).forEach((trade,index)=>{
|
||
const key=String(trade.event_key||trade.market_id||`resolution-week-${index}`),basis=Math.max(1,Number(trade.original_cost||trade.cost||0));
|
||
const row=grouped.get(key)||{values:[],horizon:Number(trade.target_horizon_days||7)};
|
||
row.values.push(clamp(Number(trade.realized_pnl||0)/basis,-1,2));grouped.set(key,row);
|
||
});
|
||
const eventRows=[...grouped.values()].map(row=>({horizon:row.horizon,value:row.values.reduce((sum,value)=>sum+value,0)/row.values.length}));
|
||
const stats=rows=>{const values=rows.map(row=>row.value),events=values.length,mean=events?values.reduce((sum,value)=>sum+value,0)/events:0;
|
||
const variance=events>1?values.reduce((sum,value)=>sum+(value-mean)**2,0)/(events-1):0,margin=1.645*Math.sqrt(variance/Math.max(1,events));
|
||
return {events,mean:+mean.toFixed(4),lower:+(mean-margin).toFixed(4),upper:+(mean+margin).toFixed(4)};};
|
||
const overall=stats(eventRows),horizons=Object.fromEntries(RESOLUTION_WEEK_NO_WINDOWS.map(window=>{
|
||
const result=stats(eventRows.filter(row=>row.horizon===window.days)),demoted=result.events>=10&&(result.mean<=0||result.upper<=0);
|
||
return [window.days,Object.assign(result,{demoted,allowed:!demoted})];
|
||
}));
|
||
const pnl=(p&&p.closed||[]).filter(trade=>(trade.signal_type||"")==="resolution-week-no-pilot"
|
||
&&Number(trade.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION).reduce((sum,trade)=>sum+Number(trade.realized_pnl||0),0);
|
||
const starting=Math.max(1,Number(p&&p.starting_balance||STARTING_BALANCE));
|
||
const demoted=(overall.events>=RESOLUTION_WEEK_NO_VERDICT_EVENTS&&(overall.mean<=0||overall.upper<=0))||pnl<=-starting*0.01;
|
||
const promoted=!demoted&&overall.events>=RESOLUTION_WEEK_NO_PROMOTION_EVENTS&&overall.lower>0.01;
|
||
const allowed=!demoted&&(RESOLUTION_WEEK_NO_BACKTEST_APPROVED||promoted);
|
||
const shadowPnl=(p&&p.resolution_week_no_outcomes||[]).filter(outcome=>Number(outcome.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION)
|
||
.reduce((sum,outcome)=>sum+Number(outcome.pnl_per_100||0),0);
|
||
return {state:demoted?"demoted":(promoted?"forward-promoted":"shadow"),allowed,promoted,demoted,events:overall.events,
|
||
mean:overall.mean,lower:overall.lower,upper:overall.upper,horizons,pnl:+pnl.toFixed(2),
|
||
shadow_pnl_per_100:+shadowPnl.toFixed(2),active_shadows:(p&&p.resolution_week_no_shadows||[]).length,
|
||
position_pct:allowed?(promoted?RESOLUTION_WEEK_NO_PROMOTED_POSITION_PCT:RESOLUTION_WEEK_NO_POSITION_PCT):0};
|
||
}
|
||
function sharedResolutionWeekNoPilotProfile(st){
|
||
const portfolios=[...RESOLUTION_WEEK_NO_ADOPTER_IDS].map(id=>st&&st.agents&&st.agents[id]).filter(Boolean);
|
||
const combined={starting_balance:portfolios.reduce((sum,p)=>sum+Math.max(1,Number(p.starting_balance||STARTING_BALANCE)),0),
|
||
closed:portfolios.flatMap(p=>p.closed||[]),resolution_week_no_shadows:portfolios.flatMap(p=>p.resolution_week_no_shadows||[]),
|
||
resolution_week_no_outcomes:portfolios.flatMap(p=>p.resolution_week_no_outcomes||[])};
|
||
return Object.assign(resolutionWeekNoPilotProfile(combined),{scope:"shared-adopters",adopters:portfolios.length});
|
||
}
|
||
function applyResolutionWeekNoProfile(s,profile){
|
||
if(!s||(s.signal_type||"")!=="resolution-week-no-pilot")return s;
|
||
const horizon=profile&&profile.horizons&&profile.horizons[Number(s.target_horizon_days||7)],horizonAllowed=!horizon||horizon.allowed;
|
||
if(profile&&profile.allowed&&horizonAllowed)return Object.assign({},s,{trade_ready:true,entry_candidate:true,watch_only:false,audited_observation_only:false,
|
||
adaptive_promotion:Boolean(profile.promoted),
|
||
rationale:`Shared forward state: ${profile.state}, ${profile.events} independent settled events; ${s.target_horizon_days||7}-day cohort ${horizon&&horizon.events||0} events with ${((horizon&&horizon.mean||0)*100).toFixed(2)}% mean. ${s.rationale||""}`});
|
||
return Object.assign({},s,{trade_ready:false,entry_candidate:false,watch_only:true,audited_observation_only:true,
|
||
rationale:`Capital remains disabled for the ${s.target_horizon_days||4}-day forward learner after ${horizon&&horizon.events||profile&&profile.events||0} independent settled events. ${s.rationale||""}`});
|
||
}
|
||
function stageResolutionWeekNoShadows(p,suggestions,{execute=true,usedEventKeys=null}={}){
|
||
p.resolution_week_no_shadows=Array.isArray(p.resolution_week_no_shadows)?p.resolution_week_no_shadows:[];
|
||
p.resolution_week_no_outcomes=Array.isArray(p.resolution_week_no_outcomes)?p.resolution_week_no_outcomes:[];
|
||
if(!execute)return {active:p.resolution_week_no_shadows.length,staged:0,candidates:0};
|
||
const used=new Set([...p.resolution_week_no_shadows,...p.resolution_week_no_outcomes,...(p.positions||[]),...(p.closed||[])]
|
||
.filter(row=>Number(row.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION)
|
||
.map(row=>String(row.event_key||row.market_id)));
|
||
(usedEventKeys||[]).forEach(key=>used.add(String(key)));
|
||
const candidates=(suggestions||[]).filter(s=>s.signal_type==="resolution-week-no-pilot");let staged=0;
|
||
for(const s of candidates){
|
||
if(p.resolution_week_no_shadows.length>=RESOLUTION_WEEK_NO_SHADOW_LIMIT)break;
|
||
const eventKey=String(s.event_key||s.market_id),marketId=String(s.market_id);if(used.has(eventKey))continue;
|
||
p.resolution_week_no_shadows.push({id:`resolution-shadow:${marketId}:${Date.now()}:${staged}`,market_id:marketId,event_key:eventKey,
|
||
question:s.question,event:s.event||"",side:"NO",entry_price:Number(s.entry_price),market_price:Number(s.market_price),current_price:Number(s.market_price),
|
||
target_horizon_days:Number(s.target_horizon_days||4),opened_at:cycleIso(),resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,
|
||
modeled_cost:+Math.max(0,Number(s.entry_price)-Number(s.market_price)).toFixed(4),url:s.url||""});
|
||
p.history.push({date:logDay(),action:"RESOLUTION_SHADOW",question:s.question,side:"NO",
|
||
detail:`Zero-capital 4-day NO observation recorded at modeled ${pct(s.entry_price)} · one observation for event ${eventKey} · cash unchanged`});
|
||
used.add(eventKey);staged++;
|
||
}
|
||
return {active:p.resolution_week_no_shadows.length,staged,candidates:candidates.length};
|
||
}
|
||
function manageResolutionWeekNoShadows(p,priceMap,{execute=true}={}){
|
||
p.resolution_week_no_shadows=Array.isArray(p.resolution_week_no_shadows)?p.resolution_week_no_shadows:[];
|
||
p.resolution_week_no_outcomes=Array.isArray(p.resolution_week_no_outcomes)?p.resolution_week_no_outcomes:[];
|
||
if(!execute)return {active:p.resolution_week_no_shadows.length,completed:0};
|
||
const keep=[];let completed=0;
|
||
for(const row of p.resolution_week_no_shadows){
|
||
const fresh=priceMap&&priceMap[String(row.market_id)];if(!fresh){keep.push(row);continue;}
|
||
const price=Number(fresh.no_price);if(Number.isFinite(price))row.current_price=+price.toFixed(4);
|
||
if(!marketIsSettled(fresh)){if(marketIsSuspended(fresh))row.price_status="orders-paused";keep.push(row);continue;}
|
||
const entry=Math.max(0.01,Number(row.entry_price||0)),netReturn=clamp(price/entry-1,-1,2),pnlPer100=netReturn*100;
|
||
if(!p.resolution_week_no_outcomes.some(outcome=>outcome.id===row.id))p.resolution_week_no_outcomes.push(Object.assign({},row,{closed_at:cycleIso(),
|
||
exit_price:+price.toFixed(4),net_return:+netReturn.toFixed(4),pnl_per_100:+pnlPer100.toFixed(2)}));
|
||
p.history.push({date:logDay(),action:"RESOLUTION_GRADE",question:row.question,side:"NO",
|
||
detail:`Forward resolution observation settled at ${pct(price)} · modeled net return ${(netReturn*100).toFixed(2)}% · portfolio cash unchanged`});completed++;
|
||
}
|
||
p.resolution_week_no_shadows=keep;p.resolution_week_no_outcomes=p.resolution_week_no_outcomes.slice(-RESOLUTION_WEEK_NO_OUTCOME_LIMIT);
|
||
return {active:keep.length,completed};
|
||
}
|
||
function currentResolutionWeekEventKeys(st){
|
||
const keys=new Set();
|
||
AGENTS.forEach(agent=>{
|
||
const p=st&&st.agents&&st.agents[agent.id]||{};
|
||
const active=(p.positions||[]).filter(row=>row.signal_type==="resolution-week-no-pilot");
|
||
const currentEvidence=[...(p.closed||[]),...(p.resolution_week_no_shadows||[]),...(p.resolution_week_no_outcomes||[])]
|
||
.filter(row=>(row.signal_type==="resolution-week-no-pilot"||row.resolution_week_strategy_version)
|
||
&&Number(row.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION);
|
||
[...active,...currentEvidence]
|
||
.forEach(row=>keys.add(String(row.event_key||row.market_id)));
|
||
});
|
||
return keys;
|
||
}
|
||
function stableHash(text){
|
||
let h=2166136261;
|
||
for(let i=0;i<text.length;i++){h^=text.charCodeAt(i);h=Math.imul(h,16777619);}
|
||
return h>>>0;
|
||
}
|
||
function stableExploration(agentId,marketId){
|
||
const id=String(marketId||"");
|
||
if(stableHash(`lane:${id}`)%100>=15)return false;
|
||
const owner=AGENTS[stableHash(`owner:${id}`)%AGENTS.length];
|
||
return Boolean(owner&&owner.id===agentId);
|
||
}
|
||
const DIRECTIONAL_REPLAY_AUDIT=Object.freeze({generated_at:"2026-08-22T00:28:00Z",markets:1000,events:208,
|
||
cost_model:"exact Gamma entry/exit taker fees plus 0.5c round-trip slippage",
|
||
broad_trend:Object.freeze({events:204,event_mean:-0.03595,lower_90:-0.05143,upper_90:-0.02046}),
|
||
reversal:Object.freeze({events:122,event_mean:-0.03022,lower_90:-0.04306,upper_90:-0.01738}),
|
||
favorite_trend:Object.freeze({hard_blocked:true,events:173,event_mean:-0.02886,lower_90:-0.03575,upper_90:-0.02196})});
|
||
function historicalOpportunityPrior(s){
|
||
const rows=[],resolutionDays=Number(s.days_to_resolution),hasResolution=s.days_to_resolution!=null&&s.days_to_resolution!==""&&Number.isFinite(resolutionDays);
|
||
if((s.signal_type||"")==="trend")rows.push({feature:"broad-trend",score:DIRECTIONAL_REPLAY_AUDIT.broad_trend.event_mean,samples:7191,blocked:true,requiresPromotion:true,
|
||
requiredPromotionFeatures:["signal:trend",`side:${s.side||"unknown"}`,`category:${s.category||"Other"}`]});
|
||
if((s.signal_type||"")==="trend"&&["favorite","heavy-favorite"].includes(entryBand(s.entry_price)))rows.push({feature:"favorite-trend-replay",score:-0.0071,
|
||
samples:DIRECTIONAL_REPLAY_AUDIT.favorite_trend.events,blocked:true,hardBlocked:true});
|
||
if((s.signal_type||"")==="reversal")rows.push({feature:"reversal",score:DIRECTIONAL_REPLAY_AUDIT.reversal.event_mean,samples:1224,blocked:true,requiresPromotion:true,
|
||
requiredPromotionFeatures:["signal:reversal","quality:reversal"]});
|
||
if((s.side||"")==="NO"&&hasResolution&&resolutionDays<=21)rows.push({feature:"short-no",score:-0.3733,samples:64,blocked:true,requiresPromotion:true,
|
||
requiredPromotionFeatures:["side:NO","duration:short"]});
|
||
if((s.signal_type||"")==="trend"&&(s.category||"Other")==="Sports")rows.push({feature:"sports-trend",score:-0.0544,samples:307,blocked:true,hardBlocked:true});
|
||
if((s.signal_type||"")==="trend"&&(s.category||"Other")==="Crypto")rows.push({feature:"crypto-trend",score:-0.0383,samples:341,blocked:true,hardBlocked:true});
|
||
if((s.category||"Other")==="Crypto")rows.push({feature:"crypto",score:-0.0344,samples:140,blocked:false});
|
||
if(entryBand(s.entry_price)==="longshot")rows.push({feature:"longshot",score:-0.0327,samples:277,blocked:false});
|
||
if((s.side||"")==="YES")rows.push({feature:"yes-side",score:-0.0190,samples:954,blocked:false});
|
||
if(!rows.length)return {score:0,confidence:0,multiplier:1,blocked:false,hardBlocked:false,requiresPromotion:false,requiredPromotionFeatures:[],features:[]};
|
||
const score=rows.reduce((sum,row)=>sum+row.score,0)/rows.length;
|
||
const samples=Math.max(...rows.map(row=>row.samples)),confidence=samples/(samples+200);
|
||
return {score:+score.toFixed(4),confidence:+confidence.toFixed(3),
|
||
multiplier:+clamp(1+score*confidence*2.5,0.82,1).toFixed(3),blocked:rows.some(row=>row.blocked),
|
||
hardBlocked:rows.some(row=>row.hardBlocked),requiresPromotion:rows.some(row=>row.requiresPromotion),
|
||
requiredPromotionFeatures:[...new Set(rows.flatMap(row=>row.requiredPromotionFeatures||[]))],features:rows.map(row=>row.feature)};
|
||
}
|
||
function historicalPromotionMet(historical,calibrationModel){
|
||
const buckets=calibrationModel&&calibrationModel.buckets||{};
|
||
return !historical.requiresPromotion||historical.requiredPromotionFeatures.every(key=>SIGNAL_PROMOTION_HORIZONS.every(horizon=>{
|
||
const row=buckets[`${key}|horizon:${horizon}`];
|
||
return Number(row&&row.weight||0)>=8&&Number(row&&row.current_weight||0)>=5&&Number(row&&row.lower_bound||0)>0.003;
|
||
}));
|
||
}
|
||
function applyAdaptiveMarketPromotion(s,calibrationModel){
|
||
if(!s||s.trade_ready||!s.entry_candidate)return s;
|
||
const historical=historicalOpportunityPrior(s);
|
||
if(historical.hardBlocked||!historical.requiresPromotion||!historicalPromotionMet(historical,calibrationModel))return s;
|
||
return Object.assign({},s,{trade_ready:true,watch_only:false,audited_observation_only:false,adaptive_promotion:true,
|
||
rationale:`Adaptive promotion: this ${s.signal_type||"signal"} remained observation-only until its recent independent cohorts accumulated enough positive net-of-cost evidence. ${s.rationale||""}`});
|
||
}
|
||
function directionalObservationBase(s){
|
||
if(!s||!["trend","reversal"].includes(s.signal_type||""))return s;
|
||
return Object.assign({},s,{trade_ready:false,watch_only:true,adaptive_promotion:false});
|
||
}
|
||
function prepareSuggestionForAgent(s,cfg,calibrationModel){
|
||
const base=directionalObservationBase(s);
|
||
if(base===s)return s;
|
||
const promoted=applyAdaptiveMarketPromotion(base,calibrationModel);
|
||
return promoted.trade_ready&&agentAcceptsSuggestion(cfg,promoted)?promoted:base;
|
||
}
|
||
function applyAgentSpecificPromotions(s,calibrationByAgent){
|
||
const base=directionalObservationBase(s);if(base===s)return s;
|
||
const promotedFor=[];let promotedSuggestion=null;
|
||
AGENTS.forEach(cfg=>{
|
||
const candidate=prepareSuggestionForAgent(base,cfg,calibrationByAgent&&calibrationByAgent[cfg.id]);
|
||
if(candidate.trade_ready){promotedFor.push(cfg.id);if(!promotedSuggestion)promotedSuggestion=candidate;}
|
||
});
|
||
return promotedSuggestion?Object.assign({},promotedSuggestion,{promoted_for_agents:promotedFor}):base;
|
||
}
|
||
function learnedOpportunity(cfg,p,s,profile=null,calibration=null){
|
||
if((s&&s.signal_type||"")==="sports-contest-no-pilot"){
|
||
const pilot=sportsFavoritePilotProfile(p);
|
||
return {score:pilot.mean,confidence:pilot.events/(pilot.events+12),market_score:0,market_confidence:0,
|
||
personal_state:pilot.state,market_state:"settlement-pilot",historical_score:0,historical_confidence:0,
|
||
historical_features:["sports-contest-no-5000-market-replay"],historical_requires_promotion:false,historical_proof_met:true,
|
||
multiplier:1,exploration:true,allowed:pilot.allowed,blocked_by:pilot.allowed?null:"pilot-settlement",features:["signal:sports-contest-no-pilot"]};
|
||
}
|
||
const model=profile||buildAdaptiveProfile(p),features=learningFeatures(s);
|
||
const rows=features.map(k=>(model.current_buckets||{})[k]).filter(Boolean);
|
||
const independentWeight=rows.length?Math.max(...rows.map(r=>Number(r.weight||0))):0;
|
||
const score=rows.length?rows.reduce((sum,r)=>sum+r.score,0)/rows.length:0;
|
||
const confidence=independentWeight/(independentWeight+12),exploration=stableExploration(cfg.id,s.market_id);
|
||
const calibrationModel=calibration||{samples:0,buckets:{}},market=calibratedOpportunity(s,calibrationModel);
|
||
const historical=historicalOpportunityPrior(s);
|
||
const historicalProofMet=historicalPromotionMet(historical,calibrationModel);
|
||
const positiveRows=rows.filter(r=>Number(r.weight||0)>=6&&Number(r.lower_bound||0)>0.005);
|
||
const negativeRows=rows.filter(r=>Number(r.weight||0)>=6&&Number(r.upper_bound||0)<-0.01);
|
||
const personalPromoted=positiveRows.length>=2&&negativeRows.length===0;
|
||
const personalBlocked=negativeRows.length>=2&&positiveRows.length===0;
|
||
const personalEvidence=personalPromoted?positiveRows.reduce((sum,r)=>sum+Number(r.lower_bound||0),0)/positiveRows.length
|
||
:(personalBlocked?negativeRows.reduce((sum,r)=>sum+Number(r.upper_bound||0),0)/negativeRows.length:score*0.10);
|
||
let blockedBy=null,blocked=false;
|
||
if(historical.hardBlocked){blockedBy="historical";blocked=true;}
|
||
else if(historical.requiresPromotion&&!historicalProofMet){blockedBy="historical";blocked=true;}
|
||
else if(historical.blocked&&!historical.requiresPromotion&&!exploration){blockedBy="historical";blocked=true;}
|
||
else if(personalBlocked&&!exploration){blockedBy="personal";blocked=true;}
|
||
else if(!market.allowed&&!exploration){blockedBy="walk-forward";blocked=true;}
|
||
return {score:+score.toFixed(4),confidence:+confidence.toFixed(3),market_score:market.score,market_confidence:market.confidence,
|
||
personal_state:personalPromoted?"promoted":(personalBlocked?"demoted":"observing"),market_state:market.state,
|
||
historical_score:historical.score,historical_confidence:historical.confidence,historical_features:historical.features,
|
||
historical_requires_promotion:Boolean(historical.requiresPromotion),historical_proof_met:Boolean(historicalProofMet),
|
||
multiplier:+clamp((1+personalEvidence*2.4)*market.multiplier*historical.multiplier,0.65,1.30).toFixed(3),
|
||
exploration,allowed:!blocked,blocked_by:blocked?blockedBy:null,features};
|
||
}
|
||
function emotionalState(ret,trail,trend,rank){
|
||
if(ret<-18)return {mood:"alarmed",urgency:0.95,label:"Crisis pressure"};
|
||
if(ret<-8&&trail>10)return {mood:"frustrated",urgency:0.82,label:"Comeback urgency"};
|
||
if(trail>14)return {mood:"impatient",urgency:0.78,label:"Leaderboard envy"};
|
||
if(trend>2)return {mood:"confident",urgency:0.62,label:"Hot hand"};
|
||
if(rank===1)return {mood:"protective",urgency:0.38,label:"Defending lead"};
|
||
if(ret<0)return {mood:"restless",urgency:0.58,label:"Need progress"};
|
||
return {mood:"focused",urgency:0.50,label:"Focused"};
|
||
}
|
||
function peerEvidenceSizeMultiplier(s){
|
||
return Number(s&&s.peer_boost||0)>0&&s&&s.learning_state==="promoted"&&s.market_learning_state==="promoted"?1.05:1;
|
||
}
|
||
function adaptiveDecision(cfg,p,rank,total,leaderEq,marketLearning=null){
|
||
const eq=equity(p),ret=(eq/p.starting_balance-1)*100,trail=((leaderEq||eq)-eq)/p.starting_balance*100;
|
||
const positionValue=(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||pos.shares*pos.current_price||0),0);
|
||
const currentExposure=eq>0?positionValue/eq:0;
|
||
const trend=recentReturnDelta(p);
|
||
const emo=emotionalState(ret,trail,trend,rank);
|
||
const aggressive=!!cfg.aggressive,profile=buildAdaptiveProfile(p);
|
||
let minConv=Math.max(cfg.minConv??0,aggressive?60:64),maxNew=Math.min(cfg.maxNew??4,aggressive?4:3),maxFrac=Math.min(cfg.maxFrac??0.05,aggressive?0.07:0.045),reserve=aggressive?0.10:0.18;
|
||
let targetExposure=cfg.targetExposure??(aggressive?0.80:0.62);
|
||
let mode=aggressive?"Confirmed Opportunity":"Quality First";
|
||
let reason=`exposure is capped at ${Math.round(targetExposure*100)}%; cash is acceptable when no independently confirmed setup clears this strategy's rules.`;
|
||
if(ret<-5){
|
||
mode="Drawdown Quality Control";minConv+=3;maxNew=Math.min(maxNew,2);maxFrac*=0.78;reserve=Math.max(reserve,aggressive?0.16:0.24);
|
||
targetExposure=Math.max(aggressive?0.52:0.38,targetExposure-0.10);
|
||
reason="in drawdown, so it requires stronger confirmation and smaller entries instead of attempting a forced recovery.";
|
||
}
|
||
if(ret<-18){
|
||
mode="Drawdown Repair";minConv+=4;maxNew=1;maxFrac=Math.min(maxFrac,aggressive?0.045:0.03);reserve=aggressive?0.25:0.32;
|
||
targetExposure=Math.min(targetExposure,cfg.drawdownExposure??(aggressive?0.55:0.40));
|
||
reason="down more than 18%, so it can add only one small, strongly confirmed position. It will not revenge trade.";
|
||
}
|
||
if(profile.current_effective_samples>=4){
|
||
if(profile.current_global_score>0.015){maxFrac*=1.08;reason+=" Its current-strategy closed-trade evidence is positive, so independently promoted setups receive a small bounded size increase.";}
|
||
else if(profile.current_global_score<-0.015){maxFrac*=0.88;reason+=" Its current-strategy closed-trade evidence is negative, so weak regimes are down-weighted while research continues without risking extra capital.";}
|
||
}
|
||
if(profile.current_effective_samples>=1.5&&(profile.current_global_score<-0.04||profile.worst_return<=-0.30)){
|
||
mode="Loss Regime Containment";minConv+=2;maxNew=Math.min(maxNew,1);maxFrac*=0.72;
|
||
reason+=" A severe realized loss was detected, so new risk is cut immediately while the learner identifies which signal regime failed.";
|
||
}
|
||
reason+=` Emotion is reported as ${emo.label.toLowerCase()} but cannot increase size, reduce confirmation, or add attack slots.`;
|
||
const todaySnaps=(p.snapshots||[]).filter(s=>s.date===todayStr());
|
||
const cycleStart=todaySnaps.length?Number(todaySnaps[0].equity||eq):eq;
|
||
const cycleDrawdown=cycleStart>0?(eq/cycleStart-1)*100:0;
|
||
const drawdownLimit=cfg.maxCycleDrawdown||MAX_CYCLE_DRAWDOWN_PCT;
|
||
if(cycleDrawdown<=-drawdownLimit){
|
||
mode="Daily Risk Pause";maxNew=0;reserve=1;targetExposure=currentExposure;
|
||
reason=`down ${Math.abs(cycleDrawdown).toFixed(1)}% since today's first snapshot, so it will not add risk until the next trading day.`;
|
||
}
|
||
const maxPositionPct=aggressive?0.10:MAX_NEW_POSITION_PCT;
|
||
targetExposure=clamp(targetExposure,0,1-reserve);
|
||
const minExposure=0,belowFloor=false;
|
||
return {mode,reason,emotion:emo.mood,urgency:+emo.urgency.toFixed(2),minConv:Math.max(0,Math.round(minConv)),maxNew:Math.max(0,Math.round(maxNew)),maxFrac:+Math.min(maxPositionPct,Math.max(0.01,maxFrac)).toFixed(3),reserve,learning:profile,marketLearning:calibrationDecisionRecord(marketLearning),sportsFavoritePilot:cfg.id==="favorite"?sportsFavoritePilotProfile(p):null,
|
||
currentExposure:+currentExposure.toFixed(3),targetExposure:+targetExposure.toFixed(3),minExposure:+minExposure.toFixed(3),belowFloor};
|
||
}
|
||
const stopKey=(posOrId)=>typeof posOrId==="string"?posOrId:String(posOrId.asset||posOrId.market_id||"");
|
||
function stoppedAt(value){
|
||
if(!value)return null;
|
||
const raw=String(value),ts=new Date(raw.length===10?`${raw}T00:00:00-04:00`:raw).getTime();
|
||
return Number.isFinite(ts)?ts:null;
|
||
}
|
||
function hasRecentStop(p,posOrId){
|
||
const ts=stoppedAt(p.stopped&&p.stopped[stopKey(posOrId)]);
|
||
return ts!=null&&(Date.now()-ts)<STOP_COOLDOWN_HOURS*3600000;
|
||
}
|
||
function rememberStop(p,pos){
|
||
if(!p.stopped)p.stopped={};
|
||
const key=stopKey(pos);
|
||
if(key)p.stopped[key]=cycleIso();
|
||
}
|
||
function doneStopLosses(pos){
|
||
return pos.stop_losses&&typeof pos.stop_losses==="object"?Object.assign({},pos.stop_losses):{};
|
||
}
|
||
function stopLossTarget(pos,tier){
|
||
const entry=Number(pos.entry_price);
|
||
if(Number.isFinite(entry)&&entry>0)return +(entry*(1-tier.drawdown)).toFixed(4);
|
||
if(pos.original_shares>0&&pos.original_cost>0)return +((pos.original_cost/pos.original_shares)*(1-tier.drawdown)).toFixed(4);
|
||
if(pos.shares>0&&pos.cost>0)return +((pos.cost/pos.shares)*(1-tier.drawdown)).toFixed(4);
|
||
return null;
|
||
}
|
||
function triggeredStopLosses(pos){
|
||
const done=doneStopLosses(pos);
|
||
return STOP_LOSS_TIERS.filter(t=>{
|
||
if(done[t.id])return false;
|
||
const target=stopLossTarget(pos,t);
|
||
return target!=null&&(pos.current_price||0)<=target;
|
||
});
|
||
}
|
||
function doneGainStops(pos){
|
||
const done=pos.gain_stops&&typeof pos.gain_stops==="object"?Object.assign({},pos.gain_stops):{};
|
||
if(done["75"]&&!done.lock1)done.lock1=done["75"];
|
||
if(done["150"]&&!done.lock2)done.lock2=done["150"];
|
||
if(done["300"]&&!done.lock3)done.lock3=done["300"];
|
||
if(pos.took_profit_at&&!done.lock1)done.lock1=pos.took_profit_at;
|
||
return done;
|
||
}
|
||
function gainStopReturn(pos,tier){
|
||
const entry=Number(pos.entry_price);
|
||
if(!Number.isFinite(entry)||entry<=0||entry>=1)return null;
|
||
const maximumReturn=(1-entry)/entry;
|
||
return Math.max(0.01,Math.min(tier.gain,maximumReturn*tier.maxGainFrac));
|
||
}
|
||
function gainStopTarget(pos,tier){
|
||
const entry=Number(pos.entry_price);
|
||
const targetReturn=gainStopReturn(pos,tier);
|
||
if(Number.isFinite(entry)&&entry>0&&targetReturn!=null)return +Math.min(0.999,entry*(1+targetReturn)).toFixed(4);
|
||
return null;
|
||
}
|
||
function nextGainStop(pos){
|
||
if(!pos.cost||pos.cost<=0)return null;
|
||
const done=doneGainStops(pos);
|
||
return GAIN_STOP_TIERS.find(t=>!done[t.id])||null;
|
||
}
|
||
function triggeredGainStops(pos){
|
||
if(!pos.cost||pos.cost<=0)return [];
|
||
const done=doneGainStops(pos);
|
||
return GAIN_STOP_TIERS.filter(t=>{
|
||
if(done[t.id])return false;
|
||
const target=gainStopTarget(pos,t);
|
||
return target!=null&&target<=1&&(pos.current_price||0)>=target;
|
||
});
|
||
}
|
||
function gainStopLabel(pos){
|
||
const done=doneGainStops(pos),doneCount=GAIN_STOP_TIERS.filter(t=>done[t.id]).length;
|
||
const tier=nextGainStop(pos);
|
||
if(!tier)return "Gain stop: 75% sold, 25% left riding";
|
||
const target=gainStopTarget(pos,tier);
|
||
const prefix=doneCount?`Gain stop: ${doneCount*25}% sold; next `:"Gain stop: ";
|
||
if(target==null)return `${prefix}waiting for ${tier.label}`;
|
||
const gap=Math.max(0,target-(pos.current_price||0));
|
||
const targetReturn=gainStopReturn(pos,tier);
|
||
return `${prefix}sell 25% at ${pct(target)} (${tier.label}, +${(targetReturn*100).toFixed(1)}%)${gap>0?` — ${(gap*100).toFixed(1)}c away`:" — armed"}`;
|
||
}
|
||
function stopLossLabel(pos){
|
||
const done=doneStopLosses(pos),doneCount=STOP_LOSS_TIERS.filter(t=>done[t.id]).length;
|
||
const tier=STOP_LOSS_TIERS.find(t=>!done[t.id]);
|
||
if(!tier)return "Stop loss: fired at -18%";
|
||
const target=stopLossTarget(pos,tier);
|
||
const action=tier.sellFrac>=1?"sell all":`sell ${Math.round(tier.sellFrac*100)}%`;
|
||
return `Stop loss: ${action} at ${pct(target)} (${tier.label})`;
|
||
}
|
||
function stopOutPosition(p,pos){
|
||
const proceeds=+(pos.shares*pos.current_price).toFixed(2);
|
||
p.cash=+(p.cash+proceeds).toFixed(2);
|
||
pos.exit_price=pos.current_price;pos.closed_at=cycleIso();pos.close_reason="Stop loss -18%";pos.close_action="STOP";
|
||
pos.realized_pnl=+(proceeds-pos.cost+Number(pos.partial_realized_pnl||0)).toFixed(2);
|
||
p.closed.push(pos);rememberStop(p,pos);
|
||
p.history.push({date:logDay(),action:"STOP",question:pos.question,side:pos.side,
|
||
detail:`Stop-loss sold '${pos.question.slice(0,40)}' at ${pct(pos.current_price)} for ${fmtUSD(proceeds)} (P&L ${fmtUSD(pos.realized_pnl)})`});
|
||
}
|
||
function scaleStopLossPosition(p,pos,tier){
|
||
const originalShares=+(pos.original_shares||pos.shares).toFixed(2);
|
||
const originalCost=+(pos.original_cost||pos.cost).toFixed(2);
|
||
pos.original_shares=originalShares;
|
||
pos.original_cost=originalCost;
|
||
if(tier.sellFrac>=1||tier.id==="45"){
|
||
stopOutPosition(p,pos);
|
||
pos._closedByStop=true;
|
||
return;
|
||
}
|
||
const soldShares=+Math.min(pos.shares,originalShares*tier.sellFrac).toFixed(2);
|
||
if(soldShares<=0)return;
|
||
const proceeds=+(soldShares*pos.current_price).toFixed(2);
|
||
const realizedCost=+Math.min(pos.cost,originalCost*tier.sellFrac).toFixed(2);
|
||
const realizedPnl=+(proceeds-realizedCost).toFixed(2);
|
||
p.cash=+(p.cash+proceeds).toFixed(2);
|
||
pos.shares=+(pos.shares-soldShares).toFixed(2);
|
||
pos.cost=+(pos.cost-realizedCost).toFixed(2);
|
||
pos.value=+(pos.shares*pos.current_price).toFixed(2);
|
||
pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
pos.stop_losses=doneStopLosses(pos);
|
||
pos.stop_losses[tier.id]=cycleIso();
|
||
pos.partial_realized_pnl=+(Number(pos.partial_realized_pnl||0)+realizedPnl).toFixed(2);
|
||
p.history.push({date:logDay(),action:"STOP",question:pos.question,side:pos.side,
|
||
detail:`Stop-loss ${tier.label} sold 33% of '${pos.question.slice(0,40)}' at ${pct(pos.current_price)} for ${fmtUSD(proceeds)} (realized ${fmtUSD(realizedPnl)})`});
|
||
}
|
||
function closePosition(p,pos,reason,action="CLOSE"){
|
||
const proceeds=+(pos.shares*pos.current_price).toFixed(2);
|
||
p.cash=+(p.cash+proceeds).toFixed(2);
|
||
pos.exit_price=pos.current_price;pos.closed_at=cycleIso();pos.close_reason=reason;pos.close_action=action;
|
||
pos.realized_pnl=+(proceeds-pos.cost+Number(pos.partial_realized_pnl||0)).toFixed(2);
|
||
p.closed.push(pos);
|
||
p.history.push({date:logDay(),action,question:pos.question,side:pos.side,
|
||
detail:`${reason} '${pos.question.slice(0,40)}' at ${pct(pos.current_price)} for ${fmtUSD(proceeds)} (P&L ${fmtUSD(pos.realized_pnl)})`});
|
||
}
|
||
function takeProfitPosition(p,pos,tier){
|
||
const legacyMultiplier=pos.took_profit_at&&!pos.original_shares?2:1;
|
||
const originalShares=+(pos.original_shares||pos.shares*legacyMultiplier).toFixed(2);
|
||
const originalCost=+(pos.original_cost||pos.cost*legacyMultiplier).toFixed(2);
|
||
pos.original_shares=originalShares;
|
||
pos.original_cost=originalCost;
|
||
const soldShares=+Math.min(pos.shares,originalShares*tier.sellFrac).toFixed(2);
|
||
if(soldShares<=0)return;
|
||
const proceeds=+(soldShares*pos.current_price).toFixed(2);
|
||
const realizedCost=+Math.min(pos.cost,originalCost*tier.sellFrac).toFixed(2);
|
||
const realizedPnl=+(proceeds-realizedCost).toFixed(2);
|
||
p.cash=+(p.cash+proceeds).toFixed(2);
|
||
pos.shares=+(pos.shares-soldShares).toFixed(2);
|
||
pos.cost=+(pos.cost-realizedCost).toFixed(2);
|
||
pos.value=+(pos.shares*pos.current_price).toFixed(2);
|
||
pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
pos.gain_stops=doneGainStops(pos);
|
||
pos.gain_stops[tier.id]=cycleIso();
|
||
pos.partial_realized_pnl=+(Number(pos.partial_realized_pnl||0)+realizedPnl).toFixed(2);
|
||
p.history.push({date:logDay(),action:"GAIN",question:pos.question,side:pos.side,
|
||
detail:`${tier.label} sold 25% of '${pos.question.slice(0,40)}' at ${pct(pos.current_price)} for ${fmtUSD(proceeds)} (realized ${fmtUSD(realizedPnl)})`});
|
||
}
|
||
function normalizeLegacyPositionRisk(p,pos,cfg,fresh){
|
||
if(!cfg||normalizedStrategyVersion(pos.strategy_version)>=SUGGESTION_ENGINE_VERSION||!(pos.shares>0)||!(pos.current_price>0))return 0;
|
||
const eq=equity(p),riskContext=Object.assign({},fresh||{},pos,{entry_price:pos.entry_price});
|
||
const riskCap=tradeLossBudgetPct(cfg,riskContext),allowedValue=eq*riskCap,currentValue=Number(pos.shares)*Number(pos.current_price);
|
||
if(!(eq>0)||currentValue<=allowedValue*1.10)return 0;
|
||
const originalShares=Number(pos.original_shares||pos.shares),originalCost=Number(pos.original_cost||pos.cost);
|
||
const sharesBefore=Number(pos.shares),costBefore=Number(pos.cost),sellValue=currentValue-allowedValue;
|
||
const soldShares=+Math.min(sharesBefore,Math.max(0,sellValue/Number(pos.current_price))).toFixed(2);
|
||
if(soldShares<=0)return 0;
|
||
const soldFraction=Math.min(1,soldShares/sharesBefore),realizedCost=+(costBefore*soldFraction).toFixed(2);
|
||
const proceeds=+(soldShares*Number(pos.current_price)).toFixed(2),realizedPnl=+(proceeds-realizedCost).toFixed(2);
|
||
p.cash=+(Number(p.cash)+proceeds).toFixed(2);
|
||
pos.original_shares=originalShares;pos.original_cost=originalCost;
|
||
pos.shares=+(sharesBefore-soldShares).toFixed(2);pos.cost=+(costBefore-realizedCost).toFixed(2);
|
||
pos.value=+(pos.shares*Number(pos.current_price)).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
pos.partial_realized_pnl=+(Number(pos.partial_realized_pnl||0)+realizedPnl).toFixed(2);
|
||
pos.risk_budget_pct=+(riskCap*100).toFixed(2);pos.risk_rebalanced_at=cycleIso();
|
||
p.history.push({date:logDay(),action:"RISK",question:pos.question,side:pos.side,
|
||
detail:`Settlement-gap budget trimmed '${pos.question.slice(0,40)}' to ${(riskCap*100).toFixed(1)}% of equity at ${pct(pos.current_price)} for ${fmtUSD(proceeds)} (realized ${fmtUSD(realizedPnl)})`});
|
||
return proceeds;
|
||
}
|
||
function daysHeld(pos){
|
||
if(!pos.opened_at)return 0;
|
||
const start=new Date(pos.opened_at).getTime(),end=new Date(cycleIso()).getTime();
|
||
if(!Number.isFinite(start)||!Number.isFinite(end))return 0;
|
||
return Math.max(0,(end-start)/86400000);
|
||
}
|
||
function hoursSince(iso){
|
||
const start=new Date(iso||"").getTime(),end=new Date(cycleIso()).getTime();
|
||
return Number.isFinite(start)&&Number.isFinite(end)?Math.max(0,(end-start)/3600000):0;
|
||
}
|
||
function trailingProfitReason(pos){
|
||
const entry=Number(pos.entry_price),current=Number(pos.current_price),peak=Number(pos.peak_price||current);
|
||
if(!entry||!current||!peak)return null;
|
||
const peakReturn=peak/entry-1,currentReturn=current/entry-1;
|
||
const firstTarget=gainStopReturn(pos,GAIN_STOP_TIERS[0]);
|
||
if(firstTarget==null||peakReturn<firstTarget)return null;
|
||
const retainedFloor=Math.max(0.02,peakReturn*0.55);
|
||
return currentReturn<=retainedFloor?`Trailing profit lock after a ${(peakReturn*100).toFixed(1)}% peak`:null;
|
||
}
|
||
function policyHoldHours(pos){
|
||
return (pos.category||"Other")==="Politics"&&(pos.signal_type||"")==="trend"
|
||
?POLITICS_TREND_MIN_HOLD_HOURS:MIN_POLICY_HOLD_HOURS;
|
||
}
|
||
function exitReason(pos,fresh,analysis,cfg){
|
||
if((pos.signal_type||"")!=="sports-contest-no-pilot"&&fastSettlementRisk(fresh))return "Risk policy removed fast-settling event exposure";
|
||
const trail=trailingProfitReason(pos);if(trail)return trail;
|
||
const heldHours=daysHeld(pos)*24;
|
||
if(daysHeld(pos)>=EXIT_STALE_DAYS&&(pos.unrealized_pnl||0)<=0)return `Stale losing exit after ${Math.floor(daysHeld(pos))} days`;
|
||
if(!analysis)return null;
|
||
const net=Math.abs(analysis.net_edge!=null?analysis.net_edge:analysis.edge||0);
|
||
const minimumHold=policyHoldHours(pos);
|
||
const conflicting=analysis.trade_ready&&analysis.side&&analysis.side!==pos.side&&net>=MIN_SELECTIVE_ENTRY_EDGE;
|
||
if(conflicting){
|
||
if(!pos.signal_conflict_since)pos.signal_conflict_since=cycleIso();
|
||
if(heldHours>=minimumHold&&hoursSince(pos.signal_conflict_since)>=EXIT_CONFIRM_HOURS)return `Confirmed signal reversal to ${analysis.side}`;
|
||
}else delete pos.signal_conflict_since;
|
||
const minExit=Math.max(30,(cfg&&cfg.minConv?cfg.minConv:45)-8);
|
||
const faded=(pos.unrealized_pnl||0)<0&&analysis.conviction<minExit&&!analysis.trade_ready;
|
||
if(faded){
|
||
if(!pos.signal_fade_since)pos.signal_fade_since=cycleIso();
|
||
if(heldHours>=Math.max(48,minimumHold)&&hoursSince(pos.signal_fade_since)>=12)return `Signal remained unconfirmed for 12 hours`;
|
||
}else delete pos.signal_fade_since;
|
||
return null;
|
||
}
|
||
function marketIsSettled(fresh){return Boolean(fresh&&fresh.closed);}
|
||
function marketIsSuspended(fresh){return Boolean(fresh&&!fresh.closed&&fresh.accepting_orders===false);}
|
||
function markToMarket(p,priceMap,cfg=null,{policyExits=false,executeTrades=true}={}){
|
||
const stillOpen=[];
|
||
for(const pos of p.positions){
|
||
const fresh=priceMap[pos.market_id];
|
||
if(fresh==null){
|
||
pos.price_status="unavailable";
|
||
stillOpen.push(pos);
|
||
continue;
|
||
}
|
||
const price=pos.side==="YES"?fresh.yes_price:fresh.no_price;
|
||
pos.current_price=+price.toFixed(4);pos.value=+(pos.shares*price).toFixed(2);
|
||
pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
pos.peak_price=+Math.max(Number(pos.peak_price||pos.entry_price||price),price).toFixed(4);
|
||
if(executeTrades)delete pos.price_status;else pos.price_status="cached-mark-only";
|
||
if(!executeTrades){stillOpen.push(pos);continue;}
|
||
if(pos.signal_type==="resolution-week-no-pilot"){
|
||
if(marketIsSettled(fresh)){closePosition(p,pos,"Resolution-window contract settled","CLOSE");continue;}
|
||
if(marketIsSuspended(fresh))pos.price_status="orders-paused-awaiting-settlement";
|
||
stillOpen.push(pos);continue;
|
||
}
|
||
if(pos.signal_type==="shock-fade-pilot"){
|
||
if(marketIsSettled(fresh)){closePosition(p,pos,"Shock fade market settled before its timed exit","CLOSE");continue;}
|
||
if(!shockFadeContractEligible(fresh)){
|
||
const executableExit=shockFadeExecutableExit(fresh,pos.side);
|
||
if(executableExit>0&&executableExit<1){
|
||
pos.current_price=+executableExit.toFixed(4);pos.value=+(pos.shares*executableExit).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
closePosition(p,pos,"Shock contract failed the full-horizon settlement safety gate","EXIT");continue;
|
||
}
|
||
pos.price_status="awaiting-contract-safety-exit";stillOpen.push(pos);continue;
|
||
}
|
||
const targetHours=shockFadeTargetHours(pos);
|
||
if(hoursSince(pos.opened_at)<targetHours){stillOpen.push(pos);continue;}
|
||
const executableExit=shockFadeExecutableExit(fresh,pos.side);
|
||
if(executableExit>0&&executableExit<1){
|
||
pos.current_price=+executableExit.toFixed(4);pos.value=+(pos.shares*executableExit).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
closePosition(p,pos,`Fixed ${targetHours}h shock-fade exit at executable book price`,"EXIT");continue;
|
||
}
|
||
pos.price_status="awaiting-executable-exit";stillOpen.push(pos);continue;
|
||
}
|
||
if(pos.signal_type==="maker-pair"&&pos.maker_pair_pending){
|
||
if(marketIsSettled(fresh)){closePosition(p,pos,"Paired maker inventory settled before the second fill","CLOSE");continue;}
|
||
if(marketIsSuspended(fresh))pos.price_status="orders-paused";
|
||
stillOpen.push(pos);continue;
|
||
}
|
||
if(pos.requires_complete_bundle){
|
||
if(marketIsSettled(fresh)){closePosition(p,pos,"Complete bundle leg settled","CLOSE");continue;}
|
||
if(marketIsSuspended(fresh))pos.price_status="orders-paused";
|
||
stillOpen.push(pos);continue;
|
||
}
|
||
if(marketIsSettled(fresh)){
|
||
closePosition(p,pos,"Market settled","CLOSE");
|
||
continue;
|
||
}
|
||
if(marketIsSuspended(fresh)){
|
||
pos.price_status="orders-paused";
|
||
stillOpen.push(pos);
|
||
continue;
|
||
}
|
||
if(policyExits&&normalizedStrategyVersion(pos.strategy_version)<SUGGESTION_ENGINE_VERSION){
|
||
closePosition(p,pos,`Strategy ${SUGGESTION_ENGINE_VERSION} retired this legacy directional holding`,"EXIT");continue;
|
||
}
|
||
for(const stopTier of triggeredStopLosses(pos))scaleStopLossPosition(p,pos,stopTier);
|
||
if(pos._closedByStop){delete pos._closedByStop;continue;}
|
||
for(const gainTier of triggeredGainStops(pos))takeProfitPosition(p,pos,gainTier);
|
||
if(policyExits)normalizeLegacyPositionRisk(p,pos,cfg,fresh);
|
||
if(policyExits){
|
||
const reason=exitReason(pos,fresh,analyzeMarket(fresh),cfg);
|
||
if(reason){closePosition(p,pos,reason,"EXIT");continue;}
|
||
}
|
||
stillOpen.push(pos);
|
||
}
|
||
p.positions=stillOpen;
|
||
}
|
||
function makerSpreadBand(spread){return Number(spread)<=0.03?"tight":Number(spread)<=0.05?"medium":"wide";}
|
||
function makerRewardBand(row){
|
||
const daily=Number(row&&row.estimated_reward_floor_daily),capital=Number(row&&(row.required_capital||row.reserved_capital));
|
||
if(!(daily>0&&capital>0))return "unmodeled";
|
||
const dailyYield=daily/capital;
|
||
return dailyYield<0.02?"low":dailyYield<0.10?"medium":"high";
|
||
}
|
||
function summarizeMakerOutcomes(rows){
|
||
const weighted=[];
|
||
(rows||[]).forEach(row=>{
|
||
const version=Number(row.maker_strategy_version||1),weight=version===MAKER_STRATEGY_VERSION?1:0.2;
|
||
const reserved=Math.max(0.01,Number(row.reserved_capital||row.deployed_capital||0.01));
|
||
weighted.push({value:Number(row.pnl||0)/reserved,weight,status:row.status,shadow:Boolean(row.shadow_only),
|
||
current:version===MAKER_STRATEGY_VERSION,eventKey:String(row.event_key||row.market_id||row.quote_id)});
|
||
});
|
||
const clusters=new Map();
|
||
weighted.forEach(row=>{const cluster=clusters.get(row.eventKey)||{weighted:0,weight:0,evidenceWeight:0,current:false,locked:false,currentLocked:false};
|
||
cluster.weighted+=row.value*row.weight;cluster.weight+=row.weight;cluster.evidenceWeight=Math.max(cluster.evidenceWeight,row.weight);
|
||
const locked=row.status==="locked"||row.status==="hedged-lock";
|
||
cluster.current=cluster.current||row.current;cluster.locked=cluster.locked||locked;
|
||
cluster.currentLocked=cluster.currentLocked||(row.current&&locked);clusters.set(row.eventKey,cluster);});
|
||
const evidence=[...clusters.values()].map(cluster=>({value:cluster.weighted/Math.max(0.01,cluster.weight),weight:cluster.evidenceWeight,
|
||
current:cluster.current,locked:cluster.locked,currentLocked:cluster.currentLocked}));
|
||
const totalWeight=evidence.reduce((sum,row)=>sum+row.weight,0),attempts=weighted.length;
|
||
const mean=totalWeight?evidence.reduce((sum,row)=>sum+row.value*row.weight,0)/totalWeight:0;
|
||
const variance=totalWeight?evidence.reduce((sum,row)=>sum+row.weight*(row.value-mean)**2,0)/totalWeight:0;
|
||
const margin=evidence.length>1?1.645*Math.sqrt(variance/Math.max(1,totalWeight)):1;
|
||
return {attempts,events:evidence.length,current_events:evidence.filter(row=>row.current).length,effective_attempts:+totalWeight.toFixed(2),
|
||
locked:weighted.filter(row=>row.status==="locked"||row.status==="hedged-lock").length,
|
||
current_locked:weighted.filter(row=>row.current&&(row.status==="locked"||row.status==="hedged-lock")).length,
|
||
locked_events:evidence.filter(row=>row.locked).length,current_locked_events:evidence.filter(row=>row.currentLocked).length,
|
||
adverse:weighted.filter(row=>row.status==="single-exit"||row.status==="immediate-exit").length,unfilled:weighted.filter(row=>row.status==="unfilled").length,
|
||
pnl:+(rows||[]).filter(row=>!row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),
|
||
shadow_pnl:+(rows||[]).filter(row=>row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),mean:+mean.toFixed(5),
|
||
lower:+(mean-margin).toFixed(5),upper:+(mean+margin).toFixed(5),confidence:+(totalWeight/(totalWeight+8)).toFixed(4)};
|
||
}
|
||
function buildMakerProfile(p,limit=MAKER_OUTCOME_LIMIT){
|
||
const outcomes=(p&&p.maker_outcomes||[]).slice(-limit),buckets={};
|
||
const add=(key,row)=>{if(!buckets[key])buckets[key]=[];buckets[key].push(row);};
|
||
outcomes.forEach(row=>{add(`category:${row.category||"Other"}`,row);add(`spread:${row.spread_band||makerSpreadBand(row.spread)}`,row);
|
||
add(`reward:${row.reward_yield_band||makerRewardBand(row)}`,row);});
|
||
return {version:MAKER_STRATEGY_VERSION,outcomes:outcomes.length,global:summarizeMakerOutcomes(outcomes),
|
||
buckets:Object.fromEntries(Object.entries(buckets).map(([key,rows])=>[key,summarizeMakerOutcomes(rows)]))};
|
||
}
|
||
function buildSharedMakerProfile(st){
|
||
const outcomes=AGENTS.flatMap(agent=>(st.agents[agent.id].maker_outcomes||[])).slice(-MAKER_OUTCOME_LIMIT*AGENTS.length);
|
||
return buildMakerProfile({maker_outcomes:outcomes},MAKER_OUTCOME_LIMIT*AGENTS.length);
|
||
}
|
||
function makerCandidateLearning(candidate,profile){
|
||
const category=profile.buckets[`category:${candidate.category}`],spread=profile.buckets[`spread:${makerSpreadBand(candidate.spread)}`];
|
||
const rewardBand=candidate.reward_yield_band||makerRewardBand(candidate),reward=profile.buckets[`reward:${rewardBand}`];
|
||
const matching=[profile.global,category,spread,reward].filter(Boolean),mature=matching.filter(stats=>stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS);
|
||
const losing=mature.some(stats=>stats.upper<0),evidencePromoted=!losing&&category&&spread&&[category,spread].every(stats=>
|
||
stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&stats.current_locked_events>=MAKER_MIN_PROMOTION_LOCKS&&stats.lower>0)
|
||
&&reward&&reward.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&reward.current_locked_events>=MAKER_MIN_PROMOTION_LOCKS&&reward.lower>0;
|
||
const promoted=MAKER_CAPITAL_BACKTEST_APPROVED&&evidencePromoted;
|
||
const positive=[category,spread,reward].filter(Boolean).sort((a,b)=>b.lower-a.lower)[0];
|
||
const multiplier=promoted?clamp(1+Math.max(0,positive.lower)*6*positive.confidence,1,1.25):1;
|
||
const units=+(candidate.units*multiplier).toFixed(2),requiredCapital=+(units*candidate.paired_cost).toFixed(2),lockedProfit=+(units*(1-candidate.paired_cost)).toFixed(2);
|
||
const learned=mature.length?mature.reduce((sum,stats)=>sum+stats.mean,0)/mature.length:0;
|
||
return Object.assign({},candidate,{units,required_capital:requiredCapital,locked_profit:lockedProfit,reward_yield_band:rewardBand,
|
||
maker_learning_multiplier:+multiplier.toFixed(3),maker_learning_state:promoted?"promoted":losing?"shadow-rejected":evidencePromoted?"shadow-backtest-blocked":"shadow-observing",
|
||
maker_capital_enabled:promoted,maker_expected_return:+learned.toFixed(5),score:+(candidate.score+learned*60+(promoted?positive.lower*100:0)).toFixed(4)});
|
||
}
|
||
function recordMakerOutcome(p,quote,status,pnl,deployedCapital){
|
||
p.maker_outcomes=Array.isArray(p.maker_outcomes)?p.maker_outcomes:[];
|
||
if(p.maker_outcomes.some(row=>row.quote_id===quote.id))return false;
|
||
const reserved=Math.max(0.01,Number(quote.required_capital||deployedCapital||0.01));
|
||
p.maker_outcomes.push({quote_id:quote.id,market_id:quote.market_id,condition_id:quote.condition_id||"",question:quote.question,
|
||
event_key:String(quote.event_key||quote.url||quote.event||quote.market_id),category:quote.category||"Other",spread:Number(quote.spread||0),spread_band:makerSpreadBand(quote.spread),
|
||
reward_yield_band:quote.reward_yield_band||makerRewardBand(quote),estimated_reward_floor_daily:+Number(quote.estimated_reward_floor_daily||0).toFixed(4),
|
||
reward_share_floor:+Number(quote.reward_share_floor||0).toFixed(6),status,pnl:+Number(pnl||0).toFixed(2),shadow_only:Boolean(quote.shadow_only),
|
||
deployed_capital:+Number(deployedCapital||0).toFixed(2),reserved_capital:+reserved.toFixed(2),return_on_reserved:+(Number(pnl||0)/reserved).toFixed(5),
|
||
created_at:quote.created_at,completed_at:cycleIso(),hours_to_outcome:+hoursSince(quote.created_at).toFixed(2),
|
||
strategy_version:Number(quote.strategy_version||SUGGESTION_ENGINE_VERSION),maker_strategy_version:Number(quote.maker_strategy_version||MAKER_STRATEGY_VERSION),
|
||
build_version:Number(quote.build_version||BUILD_VERSION)});
|
||
p.maker_outcomes=p.maker_outcomes.slice(-MAKER_OUTCOME_LIMIT);
|
||
return true;
|
||
}
|
||
function makerPairCandidate(m){
|
||
if(!m||m.closed||m.accepting_orders===false)return null;
|
||
if(Number(m.audit_version)===1){
|
||
const pairedCost=Number(m.paired_cost),units=Number(m.reward_min_size),requiredCapital=Number(m.required_capital);
|
||
const estimatedReward=Number(m.estimated_reward_floor_daily),rewardShare=Number(m.reward_share_floor),hoursToEnd=Number(m.hours_to_end);
|
||
if(!m.shadow_qualified||!(units>0&&requiredCapital>0&&estimatedReward>=1&&rewardShare>=0.0025&&hoursToEnd>=48)
|
||
||pairedCost>1-MAKER_MIN_LOCK_MARGIN||!Array.isArray(m.clob_token_ids)||m.clob_token_ids.length!==2)return null;
|
||
const rewardYield=estimatedReward/requiredCapital;
|
||
return Object.assign({},m,{units:+units.toFixed(2),required_capital:+requiredCapital.toFixed(2),locked_profit:+Number(m.locked_profit||0).toFixed(2),
|
||
reward_daily_rate:+Number(m.reward_daily_rate||0).toFixed(3),reward_share_floor:+rewardShare.toFixed(6),estimated_reward_floor_daily:+estimatedReward.toFixed(4),
|
||
maximum_one_leg_loss:+Number(m.maximum_one_leg_loss||0).toFixed(2),competitive:+Math.max(0,1-rewardShare).toFixed(4),day_move:0,
|
||
reward_yield_band:makerRewardBand(m),score:+(Math.log10(estimatedReward+1)*4+rewardYield*30+(1-pairedCost)*20).toFixed(4)});
|
||
}
|
||
const bid=Number(m.best_bid),ask=Number(m.best_ask),spread=ask-bid,tick=Math.max(0.001,Number(m.tick_size||0.01));
|
||
const reward=Number(m.rewards_daily_rate||0),minSize=Math.max(5,Number(m.rewards_min_size||0),Number(m.order_min_size||0));
|
||
const maxSpread=Number(m.rewards_max_spread||0)/100,dayMove=Math.abs(Number(m.price_change_1d||0));
|
||
const yesQuote=bid,noQuote=1-ask,pairedCost=yesQuote+noQuote,requiredCapital=minSize*pairedCost;
|
||
if(!(reward>=1&&spread>=0.02&&spread<=0.08&&maxSpread>0&&spread/2<=maxSpread&&yesQuote>=tick&&noQuote>=tick
|
||
&&pairedCost<=1-MAKER_MIN_LOCK_MARGIN&&dayMove<=Math.max(0.02,spread)&&Number(m.liquidity||0)>=5000
|
||
&&Number(m.volume_24hr||0)>=2000&&requiredCapital>0))return null;
|
||
const competition=clamp(Number(m.competitive||0),0,1),activity=Math.log10(Math.max(1,Number(m.volume_24hr||0)))+Math.log10(Math.max(1,Number(m.liquidity||0)));
|
||
return {market_id:String(m.id),condition_id:m.condition_id||"",event_key:String(m.url||m.event||m.id),question:m.question,event:m.event,url:m.url||"",category:m.category||"Other",
|
||
clob_token_ids:(m.clob_token_ids||[]).slice(0,2),
|
||
yes_quote:+yesQuote.toFixed(4),no_quote:+noQuote.toFixed(4),paired_cost:+pairedCost.toFixed(4),units:+minSize.toFixed(2),
|
||
required_capital:+requiredCapital.toFixed(2),locked_profit:+(minSize*(1-pairedCost)).toFixed(2),reward_daily_rate:+reward.toFixed(3),
|
||
competitive:+competition.toFixed(4),spread:+spread.toFixed(4),tick_size:tick,day_move:+dayMove.toFixed(4),
|
||
score:+(activity*0.25+(1-competition)*1.5+spread*20-dayMove*12+Math.log10(reward+1)*0.2).toFixed(4)};
|
||
}
|
||
function stageMakerQuotes(p,markets,{profile=null,agentIndex=null,agentCount=1,globalMarketIds=null,globalEventKeys=null}={}){
|
||
p.maker_quotes=Array.isArray(p.maker_quotes)?p.maker_quotes:[];
|
||
const eq=equity(p),existing=new Set(p.maker_quotes.map(quote=>String(quote.market_id))),held=new Set((p.positions||[]).map(pos=>String(pos.market_id)));
|
||
let reserved=p.maker_quotes.filter(quote=>!quote.shadow_only).reduce((sum,quote)=>sum+Number(quote.required_capital||0),0),staged=0,shadowStaged=0,capitalStaged=0;
|
||
const makerProfile=profile||buildMakerProfile(p);
|
||
let candidates=(markets||[]).map(makerPairCandidate).filter(Boolean).map(candidate=>makerCandidateLearning(candidate,makerProfile)).filter(Boolean).sort((a,b)=>b.score-a.score);
|
||
if(Number.isInteger(agentIndex)&&agentCount>1)candidates=candidates.filter((candidate,index)=>index%agentCount===agentIndex);
|
||
for(const candidate of candidates){
|
||
if(p.maker_quotes.length>=MAKER_MAX_QUOTES)break;
|
||
const capitalEnabled=Boolean(candidate.maker_capital_enabled);
|
||
if(existing.has(candidate.market_id)||held.has(candidate.market_id)||(globalMarketIds&&globalMarketIds.has(candidate.market_id))
|
||
||(globalEventKeys&&globalEventKeys.has(String(candidate.event_key))))continue;
|
||
if(capitalEnabled&&(reserved>=eq*MAKER_TOTAL_CAPITAL_PCT||candidate.required_capital>eq*MAKER_MAX_QUOTE_CAPITAL_PCT
|
||
||reserved+candidate.required_capital>Math.min(p.cash,eq*MAKER_TOTAL_CAPITAL_PCT)))continue;
|
||
const createdAt=cycleIso(),quote=Object.assign({},candidate,{id:`maker:${candidate.market_id}:${Date.now()}:${p.maker_quotes.length}`,
|
||
created_at:createdAt,yes_quote_updated_at:createdAt,no_quote_updated_at:createdAt,strategy_version:SUGGESTION_ENGINE_VERSION,
|
||
maker_strategy_version:MAKER_STRATEGY_VERSION,build_version:BUILD_VERSION,
|
||
shadow_only:!capitalEnabled,shadow_horizon_hours:MAKER_SHADOW_HORIZON_HOURS,
|
||
yes_filled_at:null,no_filled_at:null,yes_fill_price:null,no_fill_price:null});
|
||
p.maker_quotes.push(quote);existing.add(candidate.market_id);if(globalMarketIds)globalMarketIds.add(candidate.market_id);
|
||
if(globalEventKeys)globalEventKeys.add(String(candidate.event_key));staged++;
|
||
if(capitalEnabled){reserved+=candidate.required_capital;capitalStaged++;}else shadowStaged++;
|
||
p.history.push({date:logDay(),action:capitalEnabled?"QUOTE":"SHADOW_QUOTE",question:candidate.question,side:"PAIR",
|
||
detail:capitalEnabled
|
||
?`Staged ${candidate.units} capital-backed resting YES @ ${pct(candidate.yes_quote)} and NO @ ${pct(candidate.no_quote)} · paired cost ${pct(candidate.paired_cost)} · ${fmtUSD(candidate.locked_profit)} locked settlement margin only if both legs fill · no hypothetical reward credited`
|
||
:`Watching resting YES @ ${pct(candidate.yes_quote)} and NO @ ${pct(candidate.no_quote)} for ${MAKER_SHADOW_HORIZON_HOURS}h with zero capital · ${candidate.estimated_reward_floor_daily?`${fmtUSD(candidate.estimated_reward_floor_daily)}/day snapshot reward estimate versus ${fmtUSD(candidate.maximum_one_leg_loss)} maximum one-leg loss; estimate is not P&L · `:""}outcome will train the category, spread, and reward-yield gate`});
|
||
}
|
||
return {staged,shadowStaged,capitalStaged,active:p.maker_quotes.length,shadowActive:p.maker_quotes.filter(quote=>quote.shadow_only).length,
|
||
capitalActive:p.maker_quotes.filter(quote=>!quote.shadow_only).length,candidates:candidates.length,reserved:+reserved.toFixed(2),profile:makerProfile};
|
||
}
|
||
function makerQuotePosition(p,quote,side){return (p.positions||[]).find(pos=>pos.maker_quote_id===quote.id&&pos.side===side);}
|
||
async function fetchMakerTouchHistory(quotes,marketMap){
|
||
const active=(quotes||[]).filter(quote=>hoursSince(quote.created_at)<=MAKER_QUOTE_EXPIRY_HOURS+1);
|
||
if(!active.length)return {};
|
||
const tokens=new Set(),created=[];
|
||
active.forEach(quote=>{
|
||
const fresh=marketMap&&marketMap[String(quote.market_id)],ids=(quote.clob_token_ids&"e.clob_token_ids.length===2)?quote.clob_token_ids:(fresh&&fresh.clob_token_ids||[]);
|
||
if(ids.length===2){quote.clob_token_ids=ids.slice(0,2).map(String);ids.slice(0,2).forEach(id=>tokens.add(String(id)));}
|
||
const ts=new Date(quote.created_at).getTime();if(Number.isFinite(ts))created.push(Math.floor(ts/1000));
|
||
});
|
||
if(!tokens.size||!created.length)return {};
|
||
try{
|
||
const endTs=Math.floor(Date.now()/1000),startTs=Math.max(endTs-(MAKER_QUOTE_EXPIRY_HOURS+1)*3600,Math.min(...created)-60);
|
||
const tokenList=[...tokens],history={};
|
||
for(let index=0;index<tokenList.length;index+=20){
|
||
const response=await fetchWithTimeout(`${CLOB}/batch-prices-history`,{method:"POST",headers:{"content-type":"application/json"},
|
||
body:JSON.stringify({markets:tokenList.slice(index,index+20),start_ts:startTs,end_ts:endTs,fidelity:MAKER_TOUCH_FIDELITY_MINUTES})},PRICE_REQUEST_TIMEOUT_MS*2);
|
||
if(!response.ok)continue;
|
||
const payload=await response.json();if(payload&&payload.history&&typeof payload.history==="object")Object.assign(history,payload.history);
|
||
}
|
||
return history;
|
||
}catch(e){return {};}
|
||
}
|
||
function makerTouchEvidence(quote,side,touchHistory){
|
||
const token=quote.clob_token_ids&"e.clob_token_ids[side==="YES"?0:1];if(!token)return null;
|
||
const updatedAt=quote[`${side.toLowerCase()}_quote_updated_at`]||quote.created_at;
|
||
const threshold=Number(side==="YES"?quote.yes_quote:quote.no_quote),created=Math.max(new Date(quote.created_at).getTime(),new Date(updatedAt).getTime())/1000+RUN_INTERVAL_MS/1000*0.8;
|
||
const points=(touchHistory&&touchHistory[String(token)]||[]).filter(point=>Number(point.t)>=created&&Number.isFinite(Number(point.p)));
|
||
const touched=points.filter(point=>Number(point.p)<=threshold+0.00001).sort((a,b)=>Number(a.t)-Number(b.t))[0];
|
||
return touched?{source:"clob-price-history",timestamp:new Date(Number(touched.t)*1000).toISOString(),price:+Number(touched.p).toFixed(4)}:null;
|
||
}
|
||
function fillMakerLeg(p,quote,side,fresh,evidence="live-book-cross"){
|
||
const price=Number(side==="YES"?quote.yes_quote:quote.no_quote),cost=+(Number(quote.units)*price).toFixed(2);
|
||
if(!(cost>0)||cost>p.cash)return false;
|
||
const current=side==="YES"?Number(fresh.yes_price):Number(fresh.no_price),tokenIndex=side==="YES"?0:1;
|
||
p.cash=+(p.cash-cost).toFixed(2);
|
||
p.positions.push({market_id:quote.market_id,question:quote.question,side,shares:quote.units,token_id:fresh.clob_token_ids&&fresh.clob_token_ids[tokenIndex]||null,
|
||
entry_price:+price.toFixed(4),current_price:+current.toFixed(4),cost,value:+(quote.units*current).toFixed(2),original_shares:quote.units,original_cost:cost,
|
||
unrealized_pnl:+(quote.units*current-cost).toFixed(2),conviction:80,peer_conviction:80,category:quote.category,opened_at:cycleIso(),url:quote.url||"",
|
||
peer_note:"paired resting quote",entry_reason:`Resting maker bid touch verified by ${evidence}; no reward assumed`,net_edge:1-quote.paired_cost,
|
||
evidence_score:1,evidence_source_count:0,friction:0,chase_penalty:0,quality:"maker-pair",strategy_version:SUGGESTION_ENGINE_VERSION,
|
||
build_version:BUILD_VERSION,momentum_strength:0,signal_strength:1,signal_confidence:1,signal_type:"maker-pair",price_change_1d:quote.day_move,
|
||
price_change_1w:0,days_to_resolution:null,jump_risk:false,maker_quote_id:quote.id,maker_pair_pending:true,requires_complete_bundle:false,
|
||
learning_score:0,learning_confidence:0,market_learning_score:0,market_learning_confidence:0,learning_state:"maker-fill",market_learning_state:"maker-fill",
|
||
historical_prior_score:0,historical_prior_confidence:0,historical_prior_features:[],historical_requires_promotion:false,learning_multiplier:1,
|
||
learning_exploration:false,risk_budget_pct:2,peak_price:+current.toFixed(4),gain_stops:{},stop_losses:{}});
|
||
quote[`${side.toLowerCase()}_filled_at`]=cycleIso();quote[`${side.toLowerCase()}_fill_price`]=+price.toFixed(4);quote[`${side.toLowerCase()}_fill_evidence`]=evidence;
|
||
p.history.push({date:logDay(),action:"MAKER_FILL",question:quote.question,side,
|
||
detail:`Resting ${side} bid filled at ${pct(price)} for ${fmtUSD(cost)}; the complementary quote remains live and this inventory is not counted as a locked pair yet`});
|
||
return true;
|
||
}
|
||
function completeMakerPair(p,quote){
|
||
const positions=[makerQuotePosition(p,quote,"YES"),makerQuotePosition(p,quote,"NO")];
|
||
if(positions.some(pos=>!pos))return false;
|
||
const margin=1-Number(quote.yes_fill_price)-Number(quote.no_fill_price);
|
||
if(margin<MAKER_MIN_LOCK_MARGIN-0.0001)return false;
|
||
positions.forEach(pos=>{pos.maker_pair_pending=false;pos.requires_complete_bundle=true;pos.bundle_id=quote.id;pos.bundle_name=quote.question;
|
||
pos.bundle_leg_count=2;pos.bundle_payout_per_unit=1;pos.bundle_net_profit_per_unit=+margin.toFixed(4);});
|
||
const deployed=Number(quote.units)*(Number(quote.yes_fill_price)+Number(quote.no_fill_price)),pnl=Number(quote.units)*margin;
|
||
recordMakerOutcome(p,quote,"locked",pnl,deployed);
|
||
p.history.push({date:logDay(),action:"MAKER_LOCK",question:quote.question,side:"PAIR",
|
||
detail:`Both resting legs filled for ${pct(1-margin)} combined · locked settlement value ${fmtUSD(Number(quote.units)*margin)} before any separately verified reward`});
|
||
return true;
|
||
}
|
||
function markShadowMakerLeg(quote,side,evidence="live-book-cross"){
|
||
const key=side.toLowerCase();
|
||
if(quote[`${key}_filled_at`])return false;
|
||
quote[`${key}_filled_at`]=cycleIso();quote[`${key}_fill_price`]=+Number(side==="YES"?quote.yes_quote:quote.no_quote).toFixed(4);
|
||
quote[`${key}_fill_evidence`]=evidence;return true;
|
||
}
|
||
function completeShadowMakerPair(p,quote){
|
||
const margin=1-Number(quote.yes_fill_price)-Number(quote.no_fill_price),deployed=Number(quote.units)*(1-margin),pnl=Number(quote.units)*margin;
|
||
recordMakerOutcome(p,quote,"locked",pnl,deployed);
|
||
p.history.push({date:logDay(),action:"SHADOW_LOCK",question:quote.question,side:"PAIR",
|
||
detail:`Shadow YES and NO bids both touched for ${pct(1-margin)} combined · simulated ${fmtUSD(pnl)} settlement margin · portfolio cash unchanged`});
|
||
return true;
|
||
}
|
||
function finishShadowMakerQuote(p,quote,fresh){
|
||
const side=quote.yes_filled_at&&!quote.no_filled_at?"YES":quote.no_filled_at&&!quote.yes_filled_at?"NO":null;
|
||
if(!side){
|
||
recordMakerOutcome(p,quote,"unfilled",0,0);
|
||
p.history.push({date:logDay(),action:"SHADOW_EXPIRE",question:quote.question,side:"PAIR",
|
||
detail:`Shadow bids completed their ${quote.shadow_horizon_hours||MAKER_SHADOW_HORIZON_HOURS}h window without a verified touch; portfolio cash unchanged`});
|
||
return true;
|
||
}
|
||
const entry=Number(side==="YES"?quote.yes_fill_price:quote.no_fill_price),executable=side==="YES"?Number(fresh.best_bid):1-Number(fresh.best_ask);
|
||
const complementSide=side==="YES"?"NO":"YES",complementAsk=side==="YES"?1-Number(fresh.best_bid):Number(fresh.best_ask);
|
||
const hedgedCost=entry+complementAsk+SIGNAL_ROUND_TRIP_COST;
|
||
if(complementAsk>0&&complementAsk<1&&hedgedCost<=1-MAKER_MIN_LOCK_MARGIN){
|
||
const deployed=Number(quote.units)*(entry+complementAsk),pnl=Number(quote.units)*(1-hedgedCost);
|
||
quote[`${complementSide.toLowerCase()}_filled_at`]=cycleIso();
|
||
quote[`${complementSide.toLowerCase()}_fill_price`]=+complementAsk.toFixed(4);
|
||
quote[`${complementSide.toLowerCase()}_fill_evidence`]="executable-complement-ask";
|
||
recordMakerOutcome(p,quote,"hedged-lock",pnl,deployed);
|
||
p.history.push({date:logDay(),action:"SHADOW_HEDGE",question:quote.question,side:"PAIR",
|
||
detail:`Only the shadow ${side} resting bid touched; buying ${complementSide} at the executable ${pct(complementAsk)} ask would lock ${fmtUSD(pnl)} after modeled cost · portfolio cash unchanged`});
|
||
return true;
|
||
}
|
||
if(!(executable>0&&executable<1))return false;
|
||
const deployed=Number(quote.units)*entry,pnl=Number(quote.units)*(executable-entry-SIGNAL_ROUND_TRIP_COST);
|
||
recordMakerOutcome(p,quote,"immediate-exit",pnl,deployed);
|
||
p.history.push({date:logDay(),action:"SHADOW_EXIT",question:quote.question,side,
|
||
detail:`Only the shadow ${side} resting bid touched and the complementary ask could not lock a profit; immediate executable exit grades ${fmtUSD(pnl)} after modeled cost · portfolio cash unchanged`});
|
||
return true;
|
||
}
|
||
function unwindMakerInventory(p,quote,fresh){
|
||
const side=quote.yes_filled_at&&!quote.no_filled_at?"YES":quote.no_filled_at&&!quote.yes_filled_at?"NO":null;
|
||
if(!side){
|
||
recordMakerOutcome(p,quote,"unfilled",0,0);
|
||
p.history.push({date:logDay(),action:"MAKER_EXPIRE",question:quote.question,side:"PAIR",detail:`Both resting bids expired after ${MAKER_QUOTE_EXPIRY_HOURS}h without a verified touch; no fill or profit was recorded`});
|
||
return true;
|
||
}
|
||
const pos=makerQuotePosition(p,quote,side);if(!pos)return true;
|
||
const executable=side==="YES"?Number(fresh.best_bid):1-Number(fresh.best_ask);
|
||
if(!(executable>0&&executable<1))return false;
|
||
pos.current_price=+executable.toFixed(4);pos.value=+(pos.shares*pos.current_price).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);
|
||
recordMakerOutcome(p,quote,"single-exit",pos.value-pos.cost,pos.cost);
|
||
closePosition(p,pos,`Paired maker quote expired after ${MAKER_QUOTE_EXPIRY_HOURS}h without the complementary fill`,"MAKER_EXIT");
|
||
p.positions=(p.positions||[]).filter(item=>item!==pos);
|
||
return true;
|
||
}
|
||
function manageMakerQuotes(p,marketMap,{executeTrades=false,touchHistory={}}={}){
|
||
p.maker_quotes=Array.isArray(p.maker_quotes)?p.maker_quotes:[];
|
||
if(!executeTrades)return {active:p.maker_quotes.length,fills:0,locked:0,expired:0};
|
||
const keep=[];let fills=0,locked=0,expired=0,historyTouches=0,shadowCompleted=0;
|
||
for(const quote of p.maker_quotes){
|
||
const legacyMaker=Number(quote.maker_strategy_version||1)<MAKER_STRATEGY_VERSION;
|
||
if(legacyMaker&"e.shadow_only){
|
||
p.history.push({date:logDay(),action:"SHADOW_RETIRE",question:quote.question,side:"PAIR",
|
||
detail:`Retired the old ${quote.shadow_horizon_hours||3}h maker-research window without grading it into the new one-hour learner`});
|
||
expired++;continue;
|
||
}
|
||
const legacyCapital=!quote.shadow_only&&legacyMaker;
|
||
if(legacyCapital&&!quote.yes_filled_at&&!quote.no_filled_at){
|
||
p.history.push({date:logDay(),action:"MAKER_RETIRE",question:quote.question,side:"PAIR",
|
||
detail:`Canceled untouched Strategy ${normalizedStrategyVersion(quote.strategy_version)} maker bids after the chronological audit rejected the rule; no fill or profit recorded`});
|
||
expired++;continue;
|
||
}
|
||
const fresh=marketMap[String(quote.market_id)];
|
||
if(!fresh){keep.push(quote);continue;}
|
||
if(legacyCapital&&Boolean(quote.yes_filled_at)!==Boolean(quote.no_filled_at)){
|
||
if(unwindMakerInventory(p,quote,fresh)){expired++;continue;}
|
||
keep.push(quote);continue;
|
||
}
|
||
if(legacyCapital&"e.yes_filled_at&"e.no_filled_at){if(completeMakerPair(p,quote)){locked++;continue;}}
|
||
const age=hoursSince(quote.created_at),oldEnough=age>=RUN_INTERVAL_MS/3600000*0.8;
|
||
const yesTouch=oldEnough&&!quote.yes_filled_at?makerTouchEvidence(quote,"YES",touchHistory):null;
|
||
if(oldEnough&&!quote.yes_filled_at&&(yesTouch||(Number(fresh.best_ask)>0&&Number(fresh.best_ask)<=Number(quote.yes_quote)+0.00001))){
|
||
const filled=quote.shadow_only?markShadowMakerLeg(quote,"YES",yesTouch?yesTouch.source:"live-book-cross"):fillMakerLeg(p,quote,"YES",fresh,yesTouch?yesTouch.source:"live-book-cross");
|
||
if(filled){fills++;if(yesTouch)historyTouches++;}
|
||
}
|
||
const noAsk=1-Number(fresh.best_bid);
|
||
const noTouch=oldEnough&&!quote.no_filled_at?makerTouchEvidence(quote,"NO",touchHistory):null;
|
||
if(oldEnough&&!quote.no_filled_at&&(noTouch||(Number(fresh.best_bid)>0&&noAsk<=Number(quote.no_quote)+0.00001))){
|
||
const filled=quote.shadow_only?markShadowMakerLeg(quote,"NO",noTouch?noTouch.source:"live-book-cross"):fillMakerLeg(p,quote,"NO",fresh,noTouch?noTouch.source:"live-book-cross");
|
||
if(filled){fills++;if(noTouch)historyTouches++;}
|
||
}
|
||
if(quote.shadow_only){
|
||
if(quote.yes_filled_at&"e.no_filled_at){completeShadowMakerPair(p,quote);locked++;shadowCompleted++;continue;}
|
||
if(Boolean(quote.yes_filled_at)!==Boolean(quote.no_filled_at)&&finishShadowMakerQuote(p,quote,fresh)){
|
||
if((p.maker_outcomes||[]).at(-1)?.status==="hedged-lock")locked++;
|
||
expired++;shadowCompleted++;continue;
|
||
}
|
||
if(age>=Number(quote.shadow_horizon_hours||MAKER_SHADOW_HORIZON_HOURS)&&finishShadowMakerQuote(p,quote,fresh)){expired++;shadowCompleted++;continue;}
|
||
keep.push(quote);continue;
|
||
}
|
||
if(quote.yes_filled_at&"e.no_filled_at){if(completeMakerPair(p,quote)){locked++;continue;}}
|
||
if(age>=MAKER_QUOTE_EXPIRY_HOURS){if(unwindMakerInventory(p,quote,fresh)){expired++;continue;}}
|
||
if(quote.yes_filled_at&&!quote.no_filled_at){
|
||
const cap=1-Number(quote.yes_fill_price)-MAKER_MIN_LOCK_MARGIN,currentNoBid=1-Number(fresh.best_ask);
|
||
const nextNo=+Math.min(cap,Math.max(Number(quote.no_quote),currentNoBid)).toFixed(4);
|
||
if(nextNo>Number(quote.no_quote)+0.00001){quote.no_quote=nextNo;quote.no_quote_updated_at=cycleIso();}
|
||
}else if(quote.no_filled_at&&!quote.yes_filled_at){
|
||
const cap=1-Number(quote.no_fill_price)-MAKER_MIN_LOCK_MARGIN,currentYesBid=Number(fresh.best_bid);
|
||
const nextYes=+Math.min(cap,Math.max(Number(quote.yes_quote),currentYesBid)).toFixed(4);
|
||
if(nextYes>Number(quote.yes_quote)+0.00001){quote.yes_quote=nextYes;quote.yes_quote_updated_at=cycleIso();}
|
||
}
|
||
keep.push(quote);
|
||
}
|
||
p.maker_quotes=keep;
|
||
return {active:keep.length,shadowActive:keep.filter(quote=>quote.shadow_only).length,capitalActive:keep.filter(quote=>!quote.shadow_only).length,
|
||
fills,locked,expired,historyTouches,shadowCompleted,profile:buildMakerProfile(p)};
|
||
}
|
||
function peerMarketStats(st,selfId){
|
||
const stats={};
|
||
AGENTS.forEach(a=>{
|
||
if(a.id===selfId)return;
|
||
const p=st.agents&&st.agents[a.id]; if(!p)return;
|
||
const ret=(equity(p)/Math.max(p.starting_balance||STARTING_BALANCE,1)-1)*100;
|
||
(p.positions||[]).forEach(pos=>{
|
||
if(pos.requires_complete_bundle)return;
|
||
if(!isMaterialPosition(p,pos))return;
|
||
const id=pos.market_id||pos.asset; if(!id)return;
|
||
const key=`${id}:${pos.side}`;
|
||
const opp=`${id}:${pos.side==="YES"?"NO":"YES"}`;
|
||
if(!stats[key])stats[key]={same:0,samePnl:0,bestRet:-Infinity,names:[]};
|
||
stats[key].same++;stats[key].samePnl+=Number(pos.unrealized_pnl||0);stats[key].bestRet=Math.max(stats[key].bestRet,ret);stats[key].names.push(a.name);
|
||
if(!a.aggressive)stats[key].coreSame=(stats[key].coreSame||0)+1;
|
||
if(!stats[opp])stats[opp]={same:0,samePnl:0,bestRet:-Infinity,names:[]};
|
||
stats[opp].opposite=(stats[opp].opposite||0)+1;
|
||
stats[opp].oppositePnl=(stats[opp].oppositePnl||0)+Number(pos.unrealized_pnl||0);
|
||
});
|
||
});
|
||
return stats;
|
||
}
|
||
function peerAdjustedSuggestion(s,peer){
|
||
const m=(peer&&peer[`${s.market_id}:${s.side}`])||{};
|
||
let boost=0,peerNote="";
|
||
if(m.same){
|
||
boost-=Math.min(10,2+m.same*2);
|
||
peerNote=`diversity guard: ${m.names.slice(0,3).join(", ")} already hold this side`;
|
||
}
|
||
if(m.same&&m.samePnl< -50){boost-=Math.min(8,2+m.same*1.5);peerNote=`peer warning: same-side holders are losing`;}
|
||
if(m.opposite&&m.oppositePnl>50){boost-=Math.min(12,4+m.opposite*2);peerNote=`peer warning: opposite-side holders are winning`;}
|
||
return Object.assign({},s,{peer_boost:boost,peer_note:peerNote,peer_conviction:+(Number(s.conviction||0)+boost).toFixed(1)});
|
||
}
|
||
function materialPositionThreshold(p){return Math.max(50,equity(p)*0.0125);}
|
||
function isMaterialPosition(p,pos){return Number(pos&&((pos.value!=null)?pos.value:Number(pos.shares||0)*Number(pos.current_price||0)))>=materialPositionThreshold(p);}
|
||
function addPortfolioMarketsToSet(set,p,{materialOnly=false}={}){
|
||
(p&&p.positions||[]).forEach(pos=>{const id=pos.market_id||pos.asset;
|
||
if(id&&(!materialOnly||isMaterialPosition(p,pos)||pos.signal_type==="shock-fade-pilot"))set.add(String(id));});
|
||
return set;
|
||
}
|
||
function occupiedStrategyMarkets(st,selfId=null){
|
||
const ids=new Set();
|
||
AGENTS.filter(a=>a.kind==="strategy").forEach(a=>{
|
||
if(a.id===selfId)return;
|
||
addPortfolioMarketsToSet(ids,st.agents&&st.agents[a.id],{materialOnly:true});
|
||
});
|
||
return ids;
|
||
}
|
||
function strategyExecutionOrder(st){
|
||
return [...AGENTS].sort((a,b)=>{
|
||
const score=cfg=>{
|
||
const p=st.agents&&st.agents[cfg.id]||defaultPortfolio(),eq=equity(p);
|
||
const invested=(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||pos.shares*pos.current_price||0),0);
|
||
return (eq>0?invested/eq:0)/Math.max(0.01,Number(cfg.targetExposure||0.62));
|
||
};
|
||
return score(a)-score(b)||AGENTS.indexOf(a)-AGENTS.indexOf(b);
|
||
});
|
||
}
|
||
function overlapKeeperScore(pos,cfg){
|
||
const conviction=Number(pos.conviction||cfg.minConv||50);
|
||
const pnlBoost=Math.max(-8,Math.min(8,Number(pos.unrealized_pnl||0)/75));
|
||
const valueBoost=Math.min(5,Number(pos.value||pos.cost||0)/1000);
|
||
return conviction+pnlBoost+valueBoost;
|
||
}
|
||
function reduceStrategyOverlap(st){
|
||
const groups={};
|
||
AGENTS.filter(a=>a.kind==="strategy").forEach(cfg=>{
|
||
const p=st.agents&&st.agents[cfg.id]; if(!p)return;
|
||
(p.positions||[]).forEach(pos=>{
|
||
if(pos.requires_complete_bundle)return;
|
||
if(!isMaterialPosition(p,pos))return;
|
||
const id=pos.market_id||pos.asset; if(!id)return;
|
||
if(!groups[id])groups[id]=[];
|
||
groups[id].push({cfg,p,pos,score:overlapKeeperScore(pos,cfg)});
|
||
});
|
||
});
|
||
Object.values(groups).filter(g=>g.length>1).forEach(group=>{
|
||
group.sort((a,b)=>(b.score-a.score)||((b.pos.value||0)-(a.pos.value||0)));
|
||
const cap=Math.min(...group.map(({pos})=>maxAgentOverlapForTrade(pos)));
|
||
group.slice(cap).forEach(({p,pos})=>closePosition(p,pos,`Overlap guard limited this ${cap===1?"high-risk ":""}market to ${cap===1?"one agent":"two agents"}`,"EXIT"));
|
||
});
|
||
AGENTS.filter(a=>a.kind==="strategy").forEach(cfg=>{
|
||
const p=st.agents&&st.agents[cfg.id]; if(p)p.positions=(p.positions||[]).filter(pos=>!pos.closed_at);
|
||
});
|
||
}
|
||
function agentObservesSuggestion(cfg,s){
|
||
if(!cfg||!s||s.jump_risk||!["trend","reversal"].includes(s.signal_type))return false;
|
||
const entry=Number(s.entry_price||0),confidence=Number(s.signal_confidence||0),strength=Number(s.signal_strength||0);
|
||
const evidence=Number(s.evidence_score||0),sources=Number(s.evidence_source_count||0),change=Math.abs(Number(s.price_change_1d||0));
|
||
const days=Number(s.days_to_resolution||0),conviction=Number(s.conviction||0);
|
||
if(!(entry>0&&entry<1)||confidence<0.56)return false;
|
||
if(cfg.id==="value")return entry>=0.15&&entry<=0.85;
|
||
if(cfg.id==="momentum")return s.signal_type==="trend"&&strength>=0.45;
|
||
if(cfg.id==="favorite")return s.signal_type==="trend"&&entry>=0.55;
|
||
if(cfg.id==="longshot")return entry<=0.55;
|
||
if(cfg.id==="diversifier")return true;
|
||
if(cfg.id==="catalyst")return sources>=1||evidence>=0.40||s.quality==="catalyst";
|
||
if(cfg.id==="reversal")return s.signal_type==="trend"&&change<=0.06&&days>=14;
|
||
if(cfg.id==="breakout")return s.signal_type==="trend"&&strength>=0.50;
|
||
if(cfg.id==="tailalpha")return entry<=0.50;
|
||
if(cfg.id==="conviction")return conviction>=65&&confidence>=0.58;
|
||
return false;
|
||
}
|
||
function agentAcceptsSuggestion(cfg,s){
|
||
const quality=s.quality||"watch",edge=effectiveEntryEdge(s),evidence=Number(s.evidence_score||0);
|
||
if(quality==="watch")return false;
|
||
if(quality==="bundle-arb")return cfg.id==="value";
|
||
if(quality==="sports-contest-no-pilot")return cfg.id==="favorite"&&s.signal_type==="sports-contest-no-pilot";
|
||
if(quality==="shock-fade-pilot")return SHOCK_FADE_ADOPTER_IDS.has(cfg.id)&&s.signal_type==="shock-fade-pilot";
|
||
if(quality==="resolution-week-no-pilot")return RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id)&&s.signal_type==="resolution-week-no-pilot";
|
||
if(cfg.id==="value")return ["confirmed","liquid-trend"].includes(quality)&&edge>=MIN_SELECTIVE_ENTRY_EDGE
|
||
&&Number(s.entry_price||0)>=0.20&&Number(s.entry_price||0)<=0.80;
|
||
if(cfg.id==="momentum")return ["confirmed","trend","liquid-trend","catalyst"].includes(quality)
|
||
&&s.signal_type==="trend"&&Number(s.signal_strength||0)>=0.60&&evidence>=0.48;
|
||
if(cfg.id==="favorite")return Number(s.entry_price||0)>=0.62
|
||
&&["confirmed","liquid-trend","catalyst"].includes(quality)&&edge>=MIN_LIQUIDITY_EDGE&&evidence>=0.50;
|
||
if(cfg.id==="longshot")return Number(s.entry_price||0)<=0.48
|
||
&&["confirmed","catalyst","reversal"].includes(quality)&&edge>=MIN_SELECTIVE_ENTRY_EDGE&&evidence>=0.52;
|
||
if(cfg.id==="diversifier")return ["confirmed","liquid-trend","catalyst","reversal"].includes(quality)
|
||
&&edge>=MIN_LIQUIDITY_EDGE&&evidence>=0.50;
|
||
if(cfg.id==="catalyst")return ["catalyst","confirmed"].includes(quality)&&edge>=MIN_SELECTIVE_ENTRY_EDGE
|
||
&&Number(s.evidence_source_count||0)>=1&&Number(s.signal_confidence||0)>=0.62;
|
||
if(cfg.id==="reversal")return ["confirmed","trend","liquid-trend"].includes(quality)&&s.signal_type==="trend"
|
||
&&Math.abs(Number(s.price_change_1d||0))<=0.04&&Number(s.days_to_resolution||0)>=21
|
||
&&edge>=MIN_LIQUIDITY_EDGE&&Number(s.signal_confidence||0)>=0.64;
|
||
if(cfg.id==="breakout")return ["confirmed","trend","liquid-trend","catalyst"].includes(quality)
|
||
&&s.signal_type==="trend"&&edge>=MIN_TREND_EDGE&&Number(s.signal_strength||0)>=0.64&&evidence>=0.48;
|
||
if(cfg.id==="tailalpha")return Number(s.entry_price||0)<=0.42
|
||
&&["confirmed","catalyst"].includes(quality)&&edge>=MIN_SELECTIVE_ENTRY_EDGE&&evidence>=0.55;
|
||
if(cfg.id==="conviction")return ["confirmed","catalyst"].includes(quality)
|
||
&&edge>=MIN_ENTRY_EDGE&&Number(s.conviction||0)>=72&&Number(s.signal_confidence||0)>=0.66&&evidence>=0.52;
|
||
return quality==="confirmed"&&edge>=MIN_ENTRY_EDGE;
|
||
}
|
||
function rankSuggestionsForAgent(cfg,suggestions){
|
||
const ranked=cfg.rank(suggestions||[]);if(!SHOCK_FADE_ADOPTER_IDS.has(cfg.id)&&!RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id))return ranked;
|
||
const rankedIds=new Set(ranked.map(s=>`${s.signal_type||""}:${s.market_id||""}:${s.side||""}`));
|
||
const adaptive=(suggestions||[]).filter(s=>s.trade_ready&&((SHOCK_FADE_ADOPTER_IDS.has(cfg.id)&&s.signal_type==="shock-fade-pilot")
|
||
||(RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id)&&s.signal_type==="resolution-week-no-pilot"))
|
||
&&!rankedIds.has(`${s.signal_type||""}:${s.market_id||""}:${s.side||""}`))
|
||
.sort((a,b)=>(Number(b.signal_type==="resolution-week-no-pilot")-Number(a.signal_type==="resolution-week-no-pilot"))
|
||
||Math.abs(Number(b.shock_move_3h||0))-Math.abs(Number(a.shock_move_3h||0)));
|
||
return [...adaptive,...ranked];
|
||
}
|
||
function openNegativeRiskBundle(p,cfg,s,stake,decision){
|
||
const legs=Array.isArray(s.bundle_legs)?s.bundle_legs:[],unitCost=Number(s.bundle_cost_per_unit),verifiedUnits=Number(s.execution_units);
|
||
if(legs.length<2||!s.depth_verified||!s.fees_verified||s.verification_status!=="executable"
|
||
||!(verifiedUnits>0)||!(unitCost>0)||!(Number(s.bundle_net_profit_per_unit)>0))return null;
|
||
const units=+Math.min(verifiedUnits,Math.max(0,stake)/unitCost).toFixed(2);if(units<=0)return null;
|
||
const bundleSide=s.bundle_side||s.side||"NO";
|
||
const openedAt=cycleIso(),positions=legs.map(leg=>{
|
||
const entry=Number(leg.entry_price),current=Number(leg.current_price),cost=+(units*entry).toFixed(2),side=leg.side||bundleSide;
|
||
return {market_id:String(leg.market_id),question:leg.question,side,shares:units,token_id:leg.token_id||null,
|
||
entry_price:+entry.toFixed(4),current_price:+current.toFixed(4),cost,value:+(units*current).toFixed(2),
|
||
original_shares:units,original_cost:cost,unrealized_pnl:+(units*current-cost).toFixed(2),conviction:s.conviction,peer_conviction:s.conviction,
|
||
category:s.category,opened_at:openedAt,url:leg.url||s.url||"",peer_note:"",entry_reason:s.rationale||"",net_edge:s.net_edge,evidence_score:1,evidence_source_count:0,
|
||
friction:s.friction,chase_penalty:0,quality:"bundle-arb",strategy_version:SUGGESTION_ENGINE_VERSION,build_version:BUILD_VERSION,
|
||
momentum_strength:0,signal_strength:1,signal_confidence:1,signal_type:"bundle-arb",price_change_1d:0,price_change_1w:0,
|
||
days_to_resolution:s.days_to_resolution,jump_risk:false,bundle_id:s.bundle_id,bundle_name:s.event||s.question,bundle_leg_count:legs.length,
|
||
bundle_payout_per_unit:s.bundle_payout_per_unit,bundle_net_profit_per_unit:s.bundle_net_profit_per_unit,requires_complete_bundle:true,
|
||
learning_score:0,learning_confidence:0,market_learning_score:0,market_learning_confidence:0,learning_state:"priced-bundle",market_learning_state:"priced-bundle",
|
||
historical_prior_score:0,historical_prior_confidence:0,historical_prior_features:[],historical_requires_promotion:false,
|
||
learning_multiplier:1,learning_exploration:false,risk_budget_pct:+(tradeLossBudgetPct(cfg,s)*100).toFixed(2),
|
||
peak_price:+current.toFixed(4),gain_stops:{},stop_losses:{}};
|
||
});
|
||
const totalCost=+positions.reduce((sum,pos)=>sum+pos.cost,0).toFixed(2);
|
||
if(totalCost>p.cash||totalCost<50)return null;
|
||
p.cash=+(p.cash-totalCost).toFixed(2);p.positions.push(...positions);
|
||
p.history.push({date:logDay(),action:"OPEN",question:s.question,side:bundleSide,
|
||
detail:`${decision?decision.mode+" mode — ":""}Opened complete ${legs.length}-leg ${bundleSide} bundle for ${fmtUSD(totalCost)} · ${units} bundle units · modeled settlement profit ${fmtUSD(units*Number(s.bundle_net_profit_per_unit))} after estimated costs · live prices required`});
|
||
return {cost:totalCost,marketIds:positions.map(pos=>pos.market_id)};
|
||
}
|
||
function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=null){
|
||
const d=decision||{minConv:Math.max(cfg.minConv||0,cfg.aggressive?60:64),maxNew:Math.min(cfg.maxNew||4,cfg.aggressive?4:3),maxFrac:Math.min(cfg.maxFrac||0.05,cfg.aggressive?0.07:0.045),reserve:cfg.aggressive?0.10:0.18,targetExposure:cfg.targetExposure??(cfg.aggressive?0.75:0.62),minExposure:0};
|
||
const learningProfile=d.learning||buildAdaptiveProfile(p);
|
||
const maxPositions=cfg.maxPositions||MAX_STRATEGY_POSITIONS;
|
||
const categoryCap=cfg.maxCategoryPct||MAX_CATEGORY_EXPOSURE_PCT;
|
||
const probeQualities=["trend","liquid-trend","reversal","catalyst","sports-contest-no-pilot","resolution-week-no-pilot"];
|
||
const eqBefore=equity(p);
|
||
const positionValueBefore=(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||pos.shares*pos.current_price||0),0);
|
||
const currentExposure=eqBefore>0?positionValueBefore/eqBefore:0;
|
||
const targetExposure=clamp(Number(d.targetExposure??cfg.targetExposure??0.62),0,1);
|
||
const minExposure=0,belowFloor=false;
|
||
const pilotProfile=d.sportsFavoritePilot||sportsFavoritePilotProfile(p);
|
||
const shockProfile=d.shockFadePilot||shockFadePilotProfile(p);
|
||
const resolutionProfile=d.resolutionWeekNoPilot||resolutionWeekNoPilotProfile(p);
|
||
const heldEventKeys=new Set([...(p.positions||[]),...(p.closed||[]).filter(pos=>["sports-contest-no-pilot","shock-fade-pilot","resolution-week-no-pilot"].includes(pos.signal_type)),
|
||
...(p.sports_favorite_outcomes||[]).filter(pos=>Number(pos.sports_contest_strategy_version||0)===SPORTS_CONTEST_NO_STRATEGY_VERSION),...(p.shock_fade_outcomes||[])]
|
||
.map(pos=>String(pos.event_key||"")).filter(Boolean));
|
||
const heldShockClaims=new Set([
|
||
...(p.positions||[]).filter(pos=>pos.signal_type==="shock-fade-pilot").flatMap(pos=>shockFadeClaimKeys(pos)),
|
||
...(p.closed||[]).filter(pos=>pos.signal_type==="shock-fade-pilot").flatMap(pos=>shockFadeClaimKeys(pos,{includeRisk:false})),
|
||
...(p.shock_fade_outcomes||[]).flatMap(pos=>shockFadeClaimKeys(pos,{includeRisk:false})),
|
||
...(p.shock_fade_shadows||[]).flatMap(pos=>shockFadeClaimKeys(pos)),
|
||
]);
|
||
const tradeReadyCount=rankedSugs.filter(s=>s.trade_ready).length;
|
||
const entryCandidateCount=rankedSugs.filter(s=>s.trade_ready||s.entry_candidate).length;
|
||
const strategyMatches=rankedSugs.filter(s=>s.trade_ready&&agentAcceptsSuggestion(cfg,s));
|
||
const strategyCount=strategyMatches.length;
|
||
if((p.positions||[]).length>=maxPositions||d.maxNew<=0){
|
||
p.lastDecision=Object.assign({},d,{currentExposure:+currentExposure.toFixed(3),targetExposure:+targetExposure.toFixed(3),minExposure:+minExposure.toFixed(3),tradeReadyCount,entryCandidateCount,strategyCandidates:strategyCount,eligibleCandidates:0,opened:0,
|
||
rejectionCounts:{portfolio_limit:strategyCount},allocationStatus:d.maxNew<=0?"New entries are paused by the daily drawdown limit.":`The ${maxPositions}-position portfolio limit is full.`});
|
||
return [];
|
||
}
|
||
const avoid=avoidMarketIds||new Set();
|
||
const rejectionCounts={};
|
||
const reject=(reason)=>{rejectionCounts[reason]=(rejectionCounts[reason]||0)+1;return false;};
|
||
const cands=strategyMatches.map(s=>{
|
||
if(s.signal_type==="resolution-week-no-pilot")return Object.assign({},s,{peer_conviction:Number(s.conviction||0),peer_boost:0,learning_score:resolutionProfile.mean,learning_confidence:Math.min(1,resolutionProfile.events/RESOLUTION_WEEK_NO_PROMOTION_EVENTS),
|
||
market_learning_score:resolutionProfile.mean,market_learning_confidence:Math.min(1,resolutionProfile.events/RESOLUTION_WEEK_NO_PROMOTION_EVENTS),learning_state:resolutionProfile.state,
|
||
market_learning_state:resolutionProfile.state,historical_prior_score:0,historical_prior_confidence:0,historical_prior_features:["recent-resolution-week-no"],
|
||
historical_requires_promotion:false,learning_multiplier:1,learning_exploration:false,learning_allowed:resolutionProfile.allowed});
|
||
if(s.signal_type==="shock-fade-pilot")return Object.assign({},s,{peer_conviction:Number(s.conviction||0),peer_boost:0,learning_score:shockProfile.mean,learning_confidence:Math.min(1,shockProfile.events/SHOCK_FADE_PROMOTION_EVENTS),
|
||
market_learning_score:shockProfile.mean,market_learning_confidence:Math.min(1,shockProfile.events/SHOCK_FADE_PROMOTION_EVENTS),learning_state:shockProfile.state,
|
||
market_learning_state:shockProfile.state,historical_prior_score:0,historical_prior_confidence:0,historical_prior_features:["forward-shock-fade"],
|
||
historical_requires_promotion:false,learning_multiplier:1,learning_exploration:false,learning_allowed:shockProfile.allowed});
|
||
const peerAdjusted=peerAdjustedSuggestion(s,peerStats),learning=learnedOpportunity(cfg,p,peerAdjusted,learningProfile,d.marketLearning);
|
||
return Object.assign({},peerAdjusted,{learning_score:learning.score,learning_confidence:learning.confidence,
|
||
market_learning_score:learning.market_score,market_learning_confidence:learning.market_confidence,
|
||
learning_state:learning.personal_state,market_learning_state:learning.market_state,
|
||
historical_prior_score:learning.historical_score,historical_prior_confidence:learning.historical_confidence,
|
||
historical_prior_features:learning.historical_features,learning_block_reason:learning.blocked_by,
|
||
historical_requires_promotion:learning.historical_requires_promotion,
|
||
learning_multiplier:learning.multiplier,learning_exploration:learning.exploration,learning_allowed:learning.allowed});
|
||
}).filter(s=>{
|
||
if(!s.learning_allowed)return reject(s.learning_block_reason==="historical"?"historical_prior":"learning");
|
||
if(s.quality==="bundle-arb"){
|
||
if(!Array.isArray(s.bundle_legs)||s.bundle_legs.length<2||!(Number(s.bundle_net_profit_per_unit)>0))return reject("bundle_integrity");
|
||
if((p.positions||[]).length+s.bundle_legs.length>maxPositions)return reject("portfolio_limit");
|
||
if((p.positions||[]).some(pos=>pos.bundle_id===s.bundle_id))return reject("already_held");
|
||
return true;
|
||
}
|
||
if(s.signal_type==="sports-contest-no-pilot"){
|
||
const lead=sportsFavoriteLeadHours(s),eventKey=String(s.event_key||s.event||s.market_id||"");
|
||
if(!pilotProfile.allowed)return reject("pilot_learning");
|
||
if(!Number.isFinite(lead)||Math.abs(lead-SPORTS_FAVORITE_TARGET_LEAD_HOURS)>SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS)return reject("timing");
|
||
if(heldEventKeys.has(eventKey))return reject("event_overlap");
|
||
if(Number(s.conviction||0)<d.minConv)return reject("confidence");
|
||
if(hasPosition(p,s.market_id))return reject("already_held");
|
||
if(hasRecentStop(p,s.market_id))return reject("cooldown");
|
||
if(focus!=="All"&&focus&&s.category!==focus)return reject("focus");
|
||
return true;
|
||
}
|
||
if(s.signal_type==="shock-fade-pilot"){
|
||
if(!shockProfile.allowed)return reject("shock_learning");
|
||
if(shockFadeClaimKeys(s).some(key=>heldShockClaims.has(key)))return reject("event_overlap");
|
||
if(Number(s.conviction||0)<d.minConv)return reject("confidence");
|
||
if(hasPosition(p,s.market_id))return reject("already_held");
|
||
if(hasRecentStop(p,s.market_id))return reject("cooldown");
|
||
if(focus!=="All"&&focus&&s.category!==focus)return reject("focus");
|
||
return true;
|
||
}
|
||
if(s.signal_type==="resolution-week-no-pilot"){
|
||
const eventKey=String(s.event_key||s.event||s.market_id||"");
|
||
if(!resolutionProfile.allowed)return reject("resolution_week_learning");
|
||
if(heldEventKeys.has(eventKey))return reject("event_overlap");
|
||
if(Number(s.conviction||0)<d.minConv)return reject("confidence");
|
||
if(hasPosition(p,s.market_id))return reject("already_held");
|
||
if(focus!=="All"&&focus&&s.category!==focus)return reject("focus");
|
||
return true;
|
||
}
|
||
if(s.side!=="YES"&&s.side!=="NO")return reject("direction");
|
||
if((cfg.aggressive?s.conviction:s.peer_conviction)<d.minConv||s.conviction<58)return reject("confidence");
|
||
if(s.entry_price<0.08||s.entry_price>0.92)return reject("price");
|
||
if(effectiveEntryEdge(s)<MIN_AGGRESSIVE_EDGE)return reject("edge");
|
||
if(s.days_to_resolution!=null&&s.days_to_resolution<MIN_ENTRY_DAYS)return reject("timing");
|
||
if(s.volume<MIN_VOLUME||s.liquidity<MIN_LIQUIDITY)return reject("liquidity");
|
||
if(s.volume_24hr<500&&s.conviction<62)return reject("activity");
|
||
if(s.evidence_score!=null&&s.evidence_score<0.46)return reject("evidence");
|
||
const peer=(peerStats&&peerStats[`${s.market_id}:${s.side}`])||{};
|
||
const overlapCap=maxAgentOverlapForTrade(s);
|
||
if(avoid.has(String(s.market_id))&&!(Number(peer.same||0)<overlapCap&&Number(peer.opposite||0)===0))return reject("overlap");
|
||
if(hasPosition(p,s.market_id))return reject("already_held");
|
||
if(hasRecentStop(p,s.market_id))return reject("cooldown");
|
||
if(focus!=="All"&&focus&&s.category!==focus)return reject("focus");
|
||
return true;
|
||
})
|
||
.sort((a,b)=>((b.peer_conviction*b.learning_multiplier)-(a.peer_conviction*a.learning_multiplier))||((b.peer_boost||0)-(a.peer_boost||0)));
|
||
let opened=0,openedIds=[],openedSports=0,openedShock=0,openedResolutionWeek=0;
|
||
let cycleBudgetRemaining=eqBefore*(cfg.aggressive?0.14:0.10);
|
||
for(const s of cands){
|
||
if(opened>=d.maxNew)break;
|
||
if((p.positions||[]).length>=maxPositions)break;
|
||
if(s.signal_type==="sports-contest-no-pilot"&&openedSports>=SPORTS_FAVORITE_MAX_NEW_PER_CYCLE)continue;
|
||
if(s.signal_type==="shock-fade-pilot"&&openedShock>=SHOCK_FADE_MAX_NEW_PER_AGENT_CYCLE)continue;
|
||
if(s.signal_type==="resolution-week-no-pilot"&&openedResolutionWeek>=RESOLUTION_WEEK_NO_MAX_NEW_PER_AGENT_CYCLE)continue;
|
||
const eq=equity(p);
|
||
const positionValue=(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||pos.shares*pos.current_price||0),0);
|
||
const exposureGap=Math.max(0,eq*targetExposure-positionValue);
|
||
const investable=Math.min(p.cash-eq*d.reserve,exposureGap,cycleBudgetRemaining);
|
||
if(investable<50)break;
|
||
if(s.quality==="bundle-arb"){
|
||
const bundleStake=boundedStakeForRisk(eq,Math.min(eq*Math.min(d.maxFrac,0.04),investable),cfg,s);
|
||
const bundle=openNegativeRiskBundle(p,cfg,s,bundleStake,decision);
|
||
if(!bundle)continue;
|
||
cycleBudgetRemaining=Math.max(0,cycleBudgetRemaining-bundle.cost);
|
||
opened++;openedIds.push(String(s.bundle_id||s.market_id));
|
||
continue;
|
||
}
|
||
if(["sports-contest-no-pilot","resolution-week-no-pilot"].includes(s.signal_type)&&heldEventKeys.has(String(s.event_key||s.event||s.market_id||"")))continue;
|
||
if(s.signal_type==="shock-fade-pilot"&&shockFadeClaimKeys(s).some(key=>heldShockClaims.has(key)))continue;
|
||
let frac;
|
||
if(s.signal_type==="sports-contest-no-pilot"){frac=Math.min(d.maxFrac,Number(pilotProfile.position_pct||0));}
|
||
else if(s.signal_type==="shock-fade-pilot"){frac=Math.min(d.maxFrac,Number(shockProfile.position_pct||0));}
|
||
else if(s.signal_type==="resolution-week-no-pilot"){frac=Math.min(d.maxFrac,Number(resolutionProfile.position_pct||0));}
|
||
else if(cfg.flat){frac=d.maxFrac;}
|
||
else{const base=(s.peer_conviction/100)*Math.min(1,effectiveEntryEdge(s)/EDGE_SCALE);frac=Math.min(d.maxFrac,cfg.kelly*base);}
|
||
if(s.peer_boost<0)frac*=0.82;
|
||
frac*=peerEvidenceSizeMultiplier(s);
|
||
frac*=Number(s.learning_multiplier||1);
|
||
let stake=Math.min(eq*frac,investable);
|
||
const riskBudgetPct=tradeLossBudgetPct(cfg,s);
|
||
stake=boundedStakeForRisk(eq,stake,cfg,s);
|
||
if(s.signal_type==="sports-contest-no-pilot"){
|
||
const pilotValue=(p.positions||[]).filter(pos=>pos.signal_type==="sports-contest-no-pilot").reduce((sum,pos)=>sum+Number(pos.value||0),0);
|
||
stake=Math.min(stake,Math.max(0,eq*SPORTS_FAVORITE_TOTAL_CAPITAL_PCT-pilotValue));
|
||
}
|
||
if(s.signal_type==="shock-fade-pilot"){
|
||
const shockValue=(p.positions||[]).filter(pos=>pos.signal_type==="shock-fade-pilot").reduce((sum,pos)=>sum+Number(pos.value||0),0);
|
||
stake=Math.min(stake,Math.max(0,eq*SHOCK_FADE_TOTAL_CAPITAL_PCT-shockValue));
|
||
}
|
||
if(s.signal_type==="resolution-week-no-pilot"){
|
||
const pilotValue=(p.positions||[]).filter(pos=>pos.signal_type==="resolution-week-no-pilot").reduce((sum,pos)=>sum+Number(pos.value||0),0);
|
||
stake=Math.min(stake,Math.max(0,eq*RESOLUTION_WEEK_NO_TOTAL_CAPITAL_PCT-pilotValue));
|
||
}
|
||
const categoryValue=(p.positions||[]).filter(pos=>(pos.category||"Other")===(s.category||"Other")).reduce((sum,pos)=>sum+Number(pos.value||0),0);
|
||
stake=Math.min(stake,Math.max(0,eq*categoryCap-categoryValue));
|
||
if(stake<50)continue;
|
||
const entry=s.entry_price; if(entry<=0||entry>=1)continue;
|
||
const shares=+(stake/entry).toFixed(2),cost=+(shares*entry).toFixed(2);
|
||
if(cost>p.cash)continue;
|
||
p.cash=+(p.cash-cost).toFixed(2);
|
||
cycleBudgetRemaining=Math.max(0,cycleBudgetRemaining-cost);
|
||
const currentMark=s.market_price!=null&&Number.isFinite(Number(s.market_price))?Number(s.market_price):entry;
|
||
const currentValue=+(shares*currentMark).toFixed(2),initialPnl=+(currentValue-cost).toFixed(2);
|
||
p.positions.push({market_id:s.market_id,question:s.question,event:s.event||"",event_key:s.event_key||s.event||s.market_id,shock_risk_key:s.signal_type==="shock-fade-pilot"?shockFadeRiskKey(s):null,side:s.side,shares,
|
||
token_id:(s.side==="YES"?s.clob_yes:s.clob_no)||null,
|
||
entry_price:+entry.toFixed(4),current_price:+currentMark.toFixed(4),cost,value:currentValue,
|
||
original_shares:shares,original_cost:cost,unrealized_pnl:initialPnl,conviction:s.conviction,peer_conviction:s.peer_conviction,category:s.category,opened_at:cycleIso(),url:s.url||"",
|
||
peer_note:s.peer_note||"",entry_reason:s.rationale||"",net_edge:s.net_edge,evidence_score:s.evidence_score,evidence_source_count:s.evidence_source_count||0,friction:s.friction,chase_penalty:s.chase_penalty,quality:s.quality,
|
||
strategy_version:SUGGESTION_ENGINE_VERSION,
|
||
build_version:BUILD_VERSION,
|
||
momentum_strength:s.momentum_strength,signal_strength:s.signal_strength,signal_confidence:s.signal_confidence,signal_type:s.signal_type,price_change_1d:s.price_change_1d,price_change_1w:s.price_change_1w,
|
||
days_to_resolution:s.days_to_resolution,jump_risk:Boolean(s.jump_risk),game_start:s.game_start,hours_to_start:s.hours_to_start,pilot_prior:s.pilot_prior,
|
||
target_horizon_days:s.target_horizon_days,
|
||
sports_contest_strategy_version:s.sports_contest_strategy_version,
|
||
shock_move_1h:s.shock_move_1h,shock_prior_move_1h:s.shock_prior_move_1h,shock_move_3h:s.shock_move_3h,shock_strategy_version:s.shock_strategy_version,
|
||
target_exit_at:s.signal_type==="shock-fade-pilot"?new Date(new Date(cycleIso()).getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString():(s.signal_type==="resolution-week-no-pilot"?s.end_date:null),
|
||
resolution_week_strategy_version:s.resolution_week_strategy_version,
|
||
learning_score:s.learning_score,learning_confidence:s.learning_confidence,market_learning_score:s.market_learning_score,market_learning_confidence:s.market_learning_confidence,
|
||
learning_state:s.learning_state,market_learning_state:s.market_learning_state,
|
||
historical_prior_score:s.historical_prior_score,historical_prior_confidence:s.historical_prior_confidence,historical_prior_features:s.historical_prior_features,
|
||
historical_requires_promotion:Boolean(s.historical_requires_promotion),
|
||
learning_multiplier:s.learning_multiplier,learning_exploration:s.learning_exploration,
|
||
risk_budget_pct:+(riskBudgetPct*100).toFixed(2),
|
||
peak_price:+entry.toFixed(4),gain_stops:{},stop_losses:{}});
|
||
const openDetail=s.signal_type==="sports-contest-no-pilot"
|
||
?`${decision?decision.mode+" mode — ":""}Bounded sports-contest exploration bought ${shares} NO '${s.question.slice(0,40)}' @ modeled ${pct(entry)} for ${fmtUSD(cost)} (${pct(Number(s.market_price||entry))} midpoint + ${((entry-Number(s.market_price||entry))*100).toFixed(2)}c exact-fee execution cost) · one position for contest ${s.event_key||s.market_id} · learner state ${pilotProfile.state} · ${(Number(s.hours_to_start)||0).toFixed(1)}h before start`
|
||
:s.signal_type==="shock-fade-pilot"
|
||
?`${decision?decision.mode+" mode — ":""}${shockProfile.promoted?"Forward-promoted":"Forward-qualified"} ${SHOCK_FADE_EXIT_HOURS}-hour shock fade bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ executable modeled ${pct(entry)} for ${fmtUSD(cost)} after a ${(Number(s.shock_move_3h||0)*100).toFixed(2)}-point move · learner state ${shockProfile.state} · fixed executable exit target in ${SHOCK_FADE_EXIT_HOURS}h`
|
||
:s.signal_type==="resolution-week-no-pilot"
|
||
?`${decision?decision.mode+" mode — ":""}${resolutionProfile.promoted?"Forward-promoted":"Bounded current-regime"} ${s.target_horizon_days||7}-day resolution pilot bought ${shares} NO '${s.question.slice(0,40)}' @ executable modeled ${pct(entry)} for ${fmtUSD(cost)} · one owner for event ${s.event_key||s.market_id} · hold to verified settlement · shared state ${resolutionProfile.state}`
|
||
:`${decision?decision.mode+" mode — ":""}Bought ${shares} ${s.side} '${s.question.slice(0,40)}' @ ${pct(entry)} for ${fmtUSD(cost)} · max binary loss budget ${(riskBudgetPct*100).toFixed(1)}% · signal margin ${((Math.abs(s.net_edge!=null?s.net_edge:s.edge))*100).toFixed(1)}c · learned weight ${Number(s.learning_multiplier||1).toFixed(2)}x (${s.learning_state||"observing"}/${s.market_learning_state||"observing"})${(s.historical_prior_features||[]).length?` · history prior ${(s.historical_prior_features||[]).join("+")}`:""} · evidence ${Math.round((s.evidence_score||0)*100)}${s.peer_note?` (${s.peer_note})`:""}`;
|
||
p.history.push({date:logDay(),action:"OPEN",question:s.question,side:s.side,detail:openDetail});
|
||
if(s.signal_type==="sports-contest-no-pilot"){heldEventKeys.add(String(s.event_key||s.event||s.market_id||""));openedSports++;}
|
||
if(s.signal_type==="resolution-week-no-pilot"){heldEventKeys.add(String(s.event_key||s.event||s.market_id||""));openedResolutionWeek++;}
|
||
if(s.signal_type==="shock-fade-pilot")shockFadeClaimKeys(s).forEach(key=>heldShockClaims.add(key));
|
||
if(s.signal_type==="shock-fade-pilot")openedShock++;
|
||
opened++;openedIds.push(String(s.market_id));
|
||
}
|
||
const eqAfter=equity(p);
|
||
const positionValueAfter=(p.positions||[]).reduce((sum,pos)=>sum+Number(pos.value||pos.shares*pos.current_price||0),0);
|
||
const exposureAfter=eqAfter>0?positionValueAfter/eqAfter:0;
|
||
let allocationStatus;
|
||
if(opened)allocationStatus=`Opened ${opened} confirmed position${opened===1?"":"s"}; exposure moved from ${Math.round(currentExposure*100)}% to ${Math.round(exposureAfter*100)}% under the ${Math.round(targetExposure*100)}% ceiling.`;
|
||
else if(exposureAfter+0.001>=targetExposure)allocationStatus=`The ${Math.round(targetExposure*100)}% exposure ceiling is full.`;
|
||
else if(!tradeReadyCount)allocationStatus="No independently confirmed markets cleared the portfolio checks this cycle; cash was retained.";
|
||
else if(!strategyCount)allocationStatus=`${tradeReadyCount} markets were trade-ready, but none fit this agent's strategy.`;
|
||
else if(!cands.length)allocationStatus=`${strategyCount} strategy matches were blocked by confidence, overlap, cooldown, or focus rules.`;
|
||
else allocationStatus="Confirmed candidates existed, but position, overlap, category, reserve, or minimum-size limits blocked an entry.";
|
||
p.lastDecision=Object.assign({},d,{currentExposure:+exposureAfter.toFixed(3),exposureBefore:+currentExposure.toFixed(3),targetExposure:+targetExposure.toFixed(3),minExposure:0,belowFloor:false,capacityAvailable:exposureAfter+0.001<targetExposure,
|
||
tradeReadyCount,entryCandidateCount,strategyCandidates:strategyCount,eligibleCandidates:cands.length,opened,rejectionCounts,allocationStatus});
|
||
return openedIds;
|
||
}
|
||
function recordSnapshot(p){
|
||
const eq=equity(p);
|
||
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=SIM_DAY?p.snapshots.filter(s=>s.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));
|
||
}
|
||
function agentMomentumStats(p){
|
||
const snaps=(p.snapshots||[]).slice().filter(s=>Number.isFinite(s.return_pct)).sort((a,b)=>snapTime(a)-snapTime(b));
|
||
const returns=snaps.map(s=>Number(s.return_pct||0));
|
||
const last=returns[returns.length-1]||0;
|
||
const prev=returns[returns.length-2]??last;
|
||
const recent=returns.length>4?last-returns[returns.length-4]:last-prev;
|
||
const emaFast=returns.length?ema(returns,3):[0];
|
||
const emaSlow=returns.length?ema(returns,7):[0];
|
||
const macd=emaFast[emaFast.length-1]-emaSlow[emaSlow.length-1];
|
||
const signalArr=ema(returns.map((_,i)=>emaFast[i]-emaSlow[i]),3);
|
||
const macdHist=macd-(signalArr[signalArr.length-1]||0);
|
||
const high=Math.max(...returns,last,0);
|
||
return {last,prev,recent,macd,macdHist,drawdown:last-high,snaps};
|
||
}
|
||
/* ---------- 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=1m&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];}}
|
||
return best!=null?best:fallback;
|
||
}
|
||
function offsetIsoDay(day,delta){
|
||
const d=new Date(`${day}T00:00:00Z`);d.setUTCDate(d.getUTCDate()+delta);return d.toISOString().slice(0,10);
|
||
}
|
||
function historicalPriceFeatures(map,day,currentPrice){
|
||
const current=Number(currentPrice),previous=priceOnDay(map,offsetIsoDay(day,-1),null),week=priceOnDay(map,offsetIsoDay(day,-7),null);
|
||
return {price_change_1h:0,
|
||
price_change_1d:Number.isFinite(previous)?+(current-previous).toFixed(4):0,
|
||
price_change_1w:Number.isFinite(week)?+(current-week).toFixed(4):0};
|
||
}
|
||
/* Full day-by-day backtest: for each of the past 7 days, rebuild each sampled market's
|
||
state from that day's real price (+ correct time-to-resolution), re-run the
|
||
scoring engine, and step every strategy agent through a day of trading.
|
||
NOTE: volume/liquidity signals use current values as a proxy — Polymarket
|
||
does not expose historical volume. Daily and weekly price changes use only
|
||
prices available by the simulated day; hourly reversal signals stay disabled. */
|
||
async function backtestWeek(st){
|
||
const dates=lastNDates(7), start=dates[0];
|
||
AGENTS.forEach(a=>{st.agents[a.id]=defaultPortfolio();});
|
||
setStatus("backtest: loading markets…",true);
|
||
const markets=await fetchMarkets(2,100);
|
||
const uni=markets.filter(m=>m.clob_token_ids&&m.clob_token_ids.length===2&&(!m.end_date||m.end_date>=start)).slice(0,100);
|
||
const hist={}; let n=0;
|
||
for(const m of uni){setStatus(`backtest: price history ${++n}/${uni.length}…`,true);hist[m.id]=await fetchPriceHistory(m.clob_token_ids[0]);}
|
||
const focus=getFocus();
|
||
for(const day of dates){
|
||
SIM_DAY=day;
|
||
setStatus(`backtest: simulating ${day}…`,true);
|
||
const snaps=[];
|
||
for(const m of uni){
|
||
const yp=priceOnDay(hist[m.id],day,null);
|
||
if(yp==null||yp<=0.02||yp>=0.98)continue;
|
||
const dtr=m.end_date?(new Date(m.end_date)-new Date(day+"T12:00:00Z"))/86400000:null;
|
||
if(dtr!=null&&dtr<-0.5)continue;
|
||
snaps.push(Object.assign({},m,historicalPriceFeatures(hist[m.id],day,yp),{yes_price:+yp.toFixed(4),no_price:+(1-yp).toFixed(4),days_to_resolution:dtr}));
|
||
}
|
||
const sugs=generateSuggestions(snaps);
|
||
const priceMap={}; snaps.forEach(s=>priceMap[s.id]={yes_price:s.yes_price,no_price:s.no_price});
|
||
for(const cfg of AGENTS){
|
||
const p=st.agents[cfg.id];
|
||
markToMarket(p,priceMap,cfg,{policyExits:true});
|
||
}
|
||
reduceStrategyOverlap(st);
|
||
const claimedMarkets=new Set();
|
||
for(const cfg of strategyExecutionOrder(st)){
|
||
const p=st.agents[cfg.id];
|
||
const occupied=occupiedStrategyMarkets(st,cfg.id);
|
||
claimedMarkets.forEach(id=>occupied.add(id));
|
||
openPositions(p,cfg,cfg.rank(sugs),focus,null,occupied,peerMarketStats(st,cfg.id)).forEach(id=>claimedMarkets.add(id));
|
||
recordSnapshot(p);
|
||
}
|
||
if(day===dates[dates.length-1])saveSuggestions(sugs,markets.length); // latest live-ish board for the Suggestions tab
|
||
}
|
||
SIM_DAY=null;
|
||
st.seeded=true;
|
||
}
|
||
|
||
async function runDailyCycle(){
|
||
if(autonomousRuntimeControlsCycle())return {suggestions:(loadSuggestions().suggestions||[]).length,skipped:true,readOnly:true,hour:currentCycleHour()};
|
||
if(CYCLE_RUNNING)return {suggestions:(loadSuggestions().suggestions||[]).length,skipped:true,busy:true,hour:currentCycleHour()};
|
||
CYCLE_RUNNING=true;
|
||
try{
|
||
const cloud=await loadAuthoritativeCloudState();
|
||
let st=loadState();
|
||
const hour=currentCycleHour();
|
||
if(st.seeded&&st.last_cycle_hour===hour){
|
||
setStatus("up to date",false);
|
||
return {suggestions:(loadSuggestions().suggestions||[]).length,skipped:true,hour};
|
||
}
|
||
SNAP_TS=nowIso();
|
||
let markets=[],analysisMarkets=[],sugs=[],bundleSugs=[],shockSugs=[],makerAuditCandidates=[],bundleAudit=null,cache=loadMarketCache(),runMode="live",cacheAgeMs=0;
|
||
try{
|
||
setStatus("loading the 500 most active eligible markets…",true);
|
||
markets=await fetchMarkets(20,100,count=>setStatus(`loaded ${Math.min(count,ACTIVE_MARKET_FETCH_LIMIT).toLocaleString()} of 500 eligible active markets…`,true),ACTIVE_MARKET_FETCH_LIMIT);
|
||
analysisMarkets=selectMarketsForAnalysis(markets);
|
||
if(!analysisMarkets.length)throw new Error("No active markets returned");
|
||
setStatus(`analyzing ${analysisMarkets.length.toLocaleString()} most-active markets…`,true);
|
||
setStatus("checking complete live-priced event bundles…",true);
|
||
try{const bundleScan=await fetchNegativeRiskBundles();bundleSugs=bundleScan.candidates;bundleAudit=bundleScan.audit;}
|
||
catch(e){bundleSugs=[];bundleAudit={status:"unavailable",scanned_at:nowIso(),event_scan_limit:NEG_RISK_EVENT_SCAN_LIMIT,events_scanned:0,evaluated_structures:0,actionable_bundles:0,error:String(e&&e.message||e)};}
|
||
setStatus("measuring reward-book competition…",true);
|
||
try{makerAuditCandidates=await fetchRewardMakerCandidates();}catch(e){makerAuditCandidates=[];}
|
||
setStatus("checking real-world context…",true);
|
||
const realWorldSignals=await fetchRealWorldSignals(analysisMarkets);
|
||
setStatus("scanning accelerating three-hour shocks for 24-hour fades…",true);
|
||
try{shockSugs=await fetchShockFadeSuggestions(analysisMarkets);}catch(e){shockSugs=[];}
|
||
setStatus("analyzing expected value…",true);
|
||
sugs=[...bundleSugs,...shockSugs,...generateSuggestions(analysisMarkets,SUGGESTION_TOTAL,SUGGESTION_PER_CATEGORY,realWorldSignals)]
|
||
.sort((a,b)=>(Number(b.quality==="bundle-arb")-Number(a.quality==="bundle-arb"))
|
||
||(Number(b.quality==="resolution-week-no-pilot")-Number(a.quality==="resolution-week-no-pilot"))
|
||
||(Number(b.quality==="shock-fade-shadow")-Number(a.quality==="shock-fade-shadow"))||(b.conviction-a.conviction)).slice(0,SUGGESTION_TOTAL);
|
||
saveSuggestions(sugs,markets.length,analysisMarkets.length,bundleAudit);
|
||
}catch(networkError){
|
||
cache=cache||loadMarketCache();
|
||
const stored=loadSuggestions();
|
||
sugs=(cache&&cache.suggestions&&cache.suggestions.length?cache.suggestions:stored.suggestions)||[];
|
||
analysisMarkets=(cache&&cache.markets)||[];
|
||
cacheAgeMs=cache?cache.age_ms:Infinity;
|
||
if(!sugs.length||!offlineCachePolicy(cacheAgeMs).usable)throw new Error("No usable market snapshot is cached yet. Connect once to seed offline mode.");
|
||
runMode="offline-cache";
|
||
const cachePolicy=offlineCachePolicy(cacheAgeMs);
|
||
setStatus(`${cachePolicy.markOnly?"offline mark-only":"offline snapshot"} · ${Math.max(1,Math.round(cacheAgeMs/60000))}m old`,false);
|
||
}
|
||
st=loadState();
|
||
const emailOffsets=agentHistoryOffsets(st);
|
||
const focus=getFocus();
|
||
// Price map for all open strategy positions.
|
||
const positionIds=new Set();
|
||
AGENTS.forEach(a=>
|
||
(st.agents[a.id].positions||[]).forEach(pos=>{if(pos.market_id)positionIds.add(String(pos.market_id));}));
|
||
AGENTS.forEach(a=>(st.agents[a.id].maker_quotes||[]).forEach(quote=>{if(quote.market_id)positionIds.add(String(quote.market_id));}));
|
||
AGENTS.forEach(a=>(st.agents[a.id].sports_favorite_shadows||[]).forEach(row=>{if(row.market_id)positionIds.add(String(row.market_id));}));
|
||
AGENTS.forEach(a=>(st.agents[a.id].shock_fade_shadows||[]).forEach(row=>{if(row.market_id)positionIds.add(String(row.market_id));}));
|
||
AGENTS.forEach(a=>(st.agents[a.id].resolution_week_no_shadows||[]).forEach(row=>{if(row.market_id)positionIds.add(String(row.market_id));}));
|
||
setStatus("marking positions…",true);
|
||
const priceMap={};
|
||
const cachedById={};
|
||
(analysisMarkets||[]).forEach(m=>{cachedById[String(m.id)]=m;});
|
||
Object.entries((cache&&cache.price_map)||{}).forEach(([id,m])=>{cachedById[String(id)]=m;});
|
||
const analysisIds=new Set((analysisMarkets||[]).map(m=>String(m.id)));
|
||
const dueSignalIds=runMode==="live"?pendingSignalMarketIds(st.signal_ledger,analysisIds):[];
|
||
const ids=new Set([...positionIds,...dueSignalIds]);
|
||
if(runMode==="live"&&dueSignalIds.length)setStatus(`refreshing positions and ${dueSignalIds.length} matured signal market${dueSignalIds.length===1?"":"s"}…`,true);
|
||
const freshPrices=runMode==="live"
|
||
?await mapWithConcurrency([...ids],12,async id=>[String(id),await fetchMarketPrice(id)])
|
||
:[];
|
||
const freshById=Object.fromEntries(freshPrices);
|
||
for(const id of ids)priceMap[id]=runMode==="live"
|
||
?(freshById[String(id)]||((analysisIds.has(String(id)))?cachedById[String(id)]:null))
|
||
:(cachedById[String(id)]||null);
|
||
if(runMode==="live")saveMarketCache(analysisMarkets,sugs,priceMap);
|
||
if(runMode==="live"){
|
||
const supplemental=dueSignalIds.map(id=>freshById[String(id)]).filter(Boolean);
|
||
updateSignalLedger(st,[...analysisMarkets,...supplemental],sugs);
|
||
}
|
||
const marketLearning=buildSignalCalibration(st.signal_ledger);
|
||
const marketLearningByAgent=Object.fromEntries(AGENTS.map(agent=>[agent.id,buildSignalCalibration(st.signal_ledger,agent.id)]));
|
||
if(runMode==="live"){
|
||
sugs=sugs.map(s=>applyAgentSpecificPromotions(s,marketLearningByAgent));
|
||
saveSuggestions(sugs,markets.length,analysisMarkets.length,bundleAudit);
|
||
saveMarketCache(analysisMarkets,sugs,priceMap);
|
||
}
|
||
const cachePolicy=offlineCachePolicy(cacheAgeMs);
|
||
const executeTrades=runMode==="live"||cachePolicy.exitsAllowed;
|
||
const favoritePortfolio=st.agents.favorite;
|
||
const sportsShadowActivity=manageSportsFavoriteShadows(favoritePortfolio,priceMap,{execute:runMode==="live"&&executeTrades});
|
||
const shockPortfolio=st.agents.reversal;
|
||
const shockShadowActivity=manageShockFadeShadows(shockPortfolio,priceMap,{execute:runMode==="live"&&executeTrades});
|
||
const resolutionShadowPortfolio=st.agents.catalyst;
|
||
const resolutionShadowActivity=manageResolutionWeekNoShadows(resolutionShadowPortfolio,priceMap,{execute:runMode==="live"&&executeTrades});
|
||
setStatus("ten strategy agents trading…",true);
|
||
const preBoard=AGENTS.map(a=>({id:a.id,eq:equity(st.agents[a.id])})).sort((x,y)=>y.eq-x.eq);
|
||
const leaderEq=preBoard[0]?preBoard[0].eq:STARTING_BALANCE;
|
||
const quoteMarketMap=Object.assign({},cachedById,freshById);
|
||
const allMakerQuotes=AGENTS.flatMap(agent=>st.agents[agent.id].maker_quotes||[]);
|
||
if(runMode==="live"&&allMakerQuotes.length)setStatus("verifying resting quote touches…",true);
|
||
const makerTouchHistory=runMode==="live"?await fetchMakerTouchHistory(allMakerQuotes,quoteMarketMap):{};
|
||
const makerActivityByAgent={};
|
||
AGENTS.forEach(agent=>{makerActivityByAgent[agent.id]=manageMakerQuotes(st.agents[agent.id],quoteMarketMap,
|
||
{executeTrades:runMode==="live"&&executeTrades,touchHistory:makerTouchHistory});});
|
||
const sharedMakerProfile=buildSharedMakerProfile(st);
|
||
for(const cfg of AGENTS){
|
||
const p=st.agents[cfg.id];
|
||
markToMarket(p,priceMap,cfg,{policyExits:executeTrades,executeTrades});
|
||
}
|
||
if(executeTrades)reduceStrategyOverlap(st);
|
||
const sharedShockProfile=sharedShockFadePilotProfile(st);
|
||
const sharedResolutionWeekProfile=sharedResolutionWeekNoPilotProfile(st);
|
||
const entriesAllowed=runMode==="live"||cachePolicy.entriesAllowed;
|
||
const cycleSuggestions=prepareCycleSuggestions(sugs,runMode,entriesAllowed);
|
||
const claimedMarkets=new Set();
|
||
const claimedShockEvents=currentShockClaimKeys(st);
|
||
const claimedResolutionWeekEvents=currentResolutionWeekEventKeys(st);
|
||
const globalMakerMarkets=new Set(AGENTS.flatMap(agent=>[
|
||
...(st.agents[agent.id].maker_quotes||[]).map(quote=>String(quote.market_id)),
|
||
...(st.agents[agent.id].positions||[]).filter(pos=>pos.signal_type==="maker-pair").map(pos=>String(pos.market_id)),
|
||
]));
|
||
const globalMakerEvents=new Set(AGENTS.flatMap(agent=>(st.agents[agent.id].maker_quotes||[]).map(quote=>String(quote.event_key))));
|
||
for(const cfg of strategyExecutionOrder(st)){
|
||
const p=st.agents[cfg.id];
|
||
const rank=preBoard.findIndex(x=>x.id===cfg.id)+1||preBoard.length;
|
||
const agentMarketLearning=marketLearningByAgent[cfg.id]||marketLearning;
|
||
const decision=adaptiveDecision(cfg,p,rank,preBoard.length,leaderEq,agentMarketLearning);
|
||
if(SHOCK_FADE_ADOPTER_IDS.has(cfg.id))decision.shockFadePilot=sharedShockProfile;
|
||
if(RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id))decision.resolutionWeekNoPilot=sharedResolutionWeekProfile;
|
||
const occupied=occupiedStrategyMarkets(st,cfg.id);
|
||
claimedMarkets.forEach(id=>occupied.add(id));
|
||
let agentCycleSuggestions=cycleSuggestions.map(s=>prepareSuggestionForAgent(s,cfg,agentMarketLearning));
|
||
if(cfg.id==="favorite")agentCycleSuggestions=agentCycleSuggestions.map(s=>applySportsFavoriteForwardPromotion(s,p));
|
||
if(SHOCK_FADE_ADOPTER_IDS.has(cfg.id))agentCycleSuggestions=agentCycleSuggestions.map(s=>applyShockFadeForwardPromotion(s,p,sharedShockProfile));
|
||
if(RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id))agentCycleSuggestions=agentCycleSuggestions.map(s=>applyResolutionWeekNoProfile(s,sharedResolutionWeekProfile));
|
||
agentCycleSuggestions=agentCycleSuggestions.filter(s=>s.signal_type!=="shock-fade-pilot"
|
||
||!shockFadeClaimKeys(s).some(key=>claimedShockEvents.has(key)));
|
||
agentCycleSuggestions=agentCycleSuggestions.filter(s=>s.signal_type!=="resolution-week-no-pilot"
|
||
||!claimedResolutionWeekEvents.has(String(s.event_key||s.market_id)));
|
||
openPositions(p,cfg,rankSuggestionsForAgent(cfg,agentCycleSuggestions),focus,decision,occupied,peerMarketStats(st,cfg.id)).forEach(id=>claimedMarkets.add(id));
|
||
(p.positions||[]).filter(row=>row.signal_type==="resolution-week-no-pilot"
|
||
&&Number(row.resolution_week_strategy_version||0)===RESOLUTION_WEEK_NO_STRATEGY_VERSION)
|
||
.forEach(row=>claimedResolutionWeekEvents.add(String(row.event_key||row.market_id)));
|
||
if(RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id)){
|
||
p.lastDecision=Object.assign({},p.lastDecision,{resolutionWeekNoPilot:sharedResolutionWeekProfile});
|
||
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Corrected 4-day NO learner: shared state ${sharedResolutionWeekProfile.state}, ${sharedResolutionWeekProfile.events} settled events and ${(sharedResolutionWeekProfile.position_pct*100).toFixed(2)}% position cap; capital stays at zero before forward promotion.`.trim();
|
||
}
|
||
if(SHOCK_FADE_ADOPTER_IDS.has(cfg.id)){
|
||
(p.positions||[]).filter(row=>row.signal_type==="shock-fade-pilot"&&Number(row.shock_strategy_version||0)===SHOCK_FADE_STRATEGY_VERSION)
|
||
.forEach(row=>shockFadeClaimKeys(row).forEach(key=>claimedShockEvents.add(key)));
|
||
p.lastDecision=Object.assign({},p.lastDecision,{shockFadePilot:sharedShockProfile});
|
||
}
|
||
if(cfg.id==="favorite"){
|
||
const shadowStage=stageSportsFavoriteShadows(p,cycleSuggestions,{execute:runMode==="live"&&entriesAllowed});
|
||
p.lastDecision=Object.assign({},p.lastDecision,{sportsShadowActive:shadowStage.active,sportsShadowStaged:shadowStage.staged,
|
||
sportsShadowCandidates:shadowStage.candidates,sportsShadowCompleted:sportsShadowActivity.completed,
|
||
sportsFavoritePilot:sportsFavoritePilotProfile(p)});
|
||
const sportsProfile=p.lastDecision.sportsFavoritePilot;
|
||
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Sports contest NO lane: ${sportsProfile.state} at ${(sportsProfile.position_pct*100).toFixed(2)}% per position, ${sportsProfile.events} independent settled contest${sportsProfile.events===1?"":"s"}, ${shadowStage.active} zero-capital observation${shadowStage.active===1?"":"s"} active, and ${sportsShadowActivity.completed} graded this cycle.`.trim();
|
||
}
|
||
if(runMode==="live"&&entriesAllowed){
|
||
const agentIndex=AGENTS.findIndex(agent=>agent.id===cfg.id),makerActivity=makerActivityByAgent[cfg.id];
|
||
const staged=stageMakerQuotes(p,[...makerAuditCandidates,...analysisMarkets],{profile:sharedMakerProfile,agentIndex,agentCount:AGENTS.length,
|
||
globalMarketIds:globalMakerMarkets,globalEventKeys:globalMakerEvents});
|
||
p.lastDecision=Object.assign({},p.lastDecision,{makerQuotes:p.maker_quotes.length,makerCandidates:staged.candidates,makerStaged:staged.staged,
|
||
makerShadowStaged:staged.shadowStaged,makerCapitalStaged:staged.capitalStaged,makerShadowActive:staged.shadowActive,makerCapitalActive:staged.capitalActive,
|
||
makerFills:makerActivity.fills,makerLocked:makerActivity.locked,makerExpired:makerActivity.expired,makerHistoryTouches:makerActivity.historyTouches,
|
||
makerShadowCompleted:makerActivity.shadowCompleted,
|
||
makerReserved:staged.reserved,makerProfile:sharedMakerProfile});
|
||
if(staged.staged||makerActivity.fills||makerActivity.locked||makerActivity.expired){
|
||
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Maker research: ${staged.shadowActive} zero-capital lock-or-exit observation${staged.shadowActive===1?"":"s"}, ${makerActivity.shadowCompleted||0} outcome${makerActivity.shadowCompleted===1?"":"s"} graded this cycle; capital remains disabled after the chronological holdout failure, and displayed reward estimates are never credited as P&L.`.trim();
|
||
}
|
||
}
|
||
}
|
||
const shockStage=stageShockFadeShadows(shockPortfolio,cycleSuggestions,{execute:runMode==="live"&&entriesAllowed,usedClaimKeys:claimedShockEvents});
|
||
const shockProfile=sharedShockFadePilotProfile(st);
|
||
[...SHOCK_FADE_ADOPTER_IDS].forEach(id=>{
|
||
const adopter=st.agents[id];if(!adopter||!adopter.lastDecision)return;
|
||
adopter.lastDecision=Object.assign({},adopter.lastDecision,{shockSharedLearning:true,shockShadowActive:shockStage.active,shockShadowStaged:shockStage.staged,
|
||
shockShadowCandidates:shockStage.candidates,shockShadowCompleted:shockShadowActivity.completed,shockShadowExpired:shockShadowActivity.expired,
|
||
shockFadePilot:shockProfile});
|
||
adopter.lastDecision.allocationStatus=`${adopter.lastDecision.allocationStatus||""} Shared shock strategy ${SHOCK_FADE_STRATEGY_VERSION}: ${shockStage.active} zero-capital observation${shockStage.active===1?"":"s"} active, ${shockShadowActivity.completed} ${SHOCK_FADE_EXIT_HOURS}-hour outcome${shockShadowActivity.completed===1?"":"s"} graded this cycle, ${shockProfile.events}/${SHOCK_FADE_QUALIFICATION_EVENTS} events needed for 1% capital and ${SHOCK_FADE_PROMOTION_EVENTS} for 1.5%; current cap ${(shockProfile.position_pct*100).toFixed(2)}%.`.trim();
|
||
});
|
||
const resolutionStage=stageResolutionWeekNoShadows(resolutionShadowPortfolio,cycleSuggestions,{execute:runMode==="live"&&entriesAllowed,usedEventKeys:claimedResolutionWeekEvents});
|
||
const resolutionProfile=sharedResolutionWeekNoPilotProfile(st);
|
||
[...RESOLUTION_WEEK_NO_ADOPTER_IDS].forEach(id=>{
|
||
const adopter=st.agents[id];if(!adopter||!adopter.lastDecision)return;
|
||
adopter.lastDecision=Object.assign({},adopter.lastDecision,{resolutionSharedLearning:true,resolutionShadowActive:resolutionStage.active,
|
||
resolutionShadowStaged:resolutionStage.staged,resolutionShadowCandidates:resolutionStage.candidates,
|
||
resolutionShadowCompleted:resolutionShadowActivity.completed,resolutionWeekNoPilot:resolutionProfile});
|
||
adopter.lastDecision.allocationStatus=`${adopter.lastDecision.allocationStatus||""} Shared 4-day NO learner: ${resolutionStage.active} zero-capital observation${resolutionStage.active===1?"":"s"} active, ${resolutionShadowActivity.completed} settled outcome${resolutionShadowActivity.completed===1?"":"s"} graded this cycle, ${resolutionProfile.events}/${RESOLUTION_WEEK_NO_PROMOTION_EVENTS} events needed before a 0.5% paper position; current cap ${(resolutionProfile.position_pct*100).toFixed(2)}%.`.trim();
|
||
});
|
||
AGENTS.forEach(agent=>recordSnapshot(st.agents[agent.id]));
|
||
st.date=todayStr();st.last_run=SNAP_TS;st.last_cycle_hour=hour;st.run_mode=runMode;
|
||
st.offline_cache_age_minutes=runMode==="offline-cache"?Math.round(cacheAgeMs/60000):0;
|
||
st.offline_execution_mode=runMode==="live"?"live":(executeTrades?"fresh-cache":"mark-only");
|
||
st.pending_sync=!cloud.ok;
|
||
const emailEvents=collectNewAgentEvents(st,emailOffsets);
|
||
saveState(st);
|
||
const pushed=cloud.ok?await pushCloudState():false;
|
||
if(pushed){st.pending_sync=false;saveState(st);setStatus(runMode==="live"?"up to date":(executeTrades?"offline cycle synced":"offline mark-only synced"),false);await notifyTradeDigest(emailEvents);}
|
||
else{st.pending_sync=true;saveState(st);setStatus(localOnlyStatus(runMode==="live"?"live data":(executeTrades?"offline cycle":"offline mark-only")),false);}
|
||
return {suggestions:sugs.length,offline:runMode!=="live",pendingSync:!pushed,entriesAllowed,executeTrades};
|
||
}finally{SNAP_TS=null;CYCLE_RUNNING=false;}
|
||
}
|
||
|
||
/* ---------- Leaderboard helpers ---------- */
|
||
function portfolioChange24h(p,currentEquity=null){
|
||
const snaps=(p&&p.snapshots||[]).filter(s=>{
|
||
const t=new Date(s.timestamp||`${s.date}T12:00:00Z`).getTime();
|
||
return Number.isFinite(t)&&Number.isFinite(Number(s.equity));
|
||
}).sort((a,b)=>new Date(a.timestamp||`${a.date}T12:00:00Z`)-new Date(b.timestamp||`${b.date}T12:00:00Z`));
|
||
if(snaps.length<2)return {available:false,pct:null,delta:null,hours:0};
|
||
const latest=snaps[snaps.length-1],latestTime=new Date(latest.timestamp||`${latest.date}T12:00:00Z`).getTime();
|
||
const cutoff=latestTime-86400000;
|
||
let baseline=null;
|
||
for(const snap of snaps){
|
||
const t=new Date(snap.timestamp||`${snap.date}T12:00:00Z`).getTime();
|
||
if(t<=cutoff)baseline=snap;
|
||
else break;
|
||
}
|
||
if(!baseline)baseline=snaps[0];
|
||
const base=Number(baseline.equity),current=Number.isFinite(Number(currentEquity))?Number(currentEquity):Number(latest.equity);
|
||
const baseTime=new Date(baseline.timestamp||`${baseline.date}T12:00:00Z`).getTime();
|
||
if(!(base>0)||!Number.isFinite(current))return {available:false,pct:null,delta:null,hours:0};
|
||
return {available:true,pct:(current/base-1)*100,delta:current-base,hours:Math.max(0,(latestTime-baseTime)/3600000)};
|
||
}
|
||
function format24h(change){
|
||
if(!change||!change.available)return "—";
|
||
const value=fmtPct(change.pct);
|
||
return change.hours>=20?value:`${value} (${Math.max(1,Math.round(change.hours))}h)`;
|
||
}
|
||
function rankBoardRows(rows){
|
||
return [...rows].sort((a,b)=>(b.engineRet-a.engineRet)||(b.enginePnl-a.enginePnl)||(b.eq-a.eq));
|
||
}
|
||
function board(){
|
||
const st=loadState();
|
||
return rankBoardRows(AGENTS.map(c=>{const p=st.agents[c.id]||defaultPortfolio();const eq=equity(p);
|
||
const baseline=Number(p.engine_baseline&&normalizedStrategyVersion(p.engine_baseline.version)===SUGGESTION_ENGINE_VERSION?p.engine_baseline.equity:eq);
|
||
return {c,p,eq,pnl:eq-STARTING_BALANCE,ret:(eq/STARTING_BALANCE-1)*100,enginePnl:eq-baseline,engineRet:baseline>0?(eq/baseline-1)*100:0,change24h:portfolioChange24h(p,eq)};})
|
||
);
|
||
}
|
||
const MEDALS=["🥇","🥈","🥉"];
|
||
|
||
/* ---------- Rendering ---------- */
|
||
function renderAll(){renderOverview();renderEmailAlerts();renderLeaderboard();renderSuggestions();renderPortfolioTab();renderInvestTab();renderPaperTab();renderLiveMoneyTab();renderAbout();}
|
||
|
||
function renderOverview(){
|
||
const b=board(); const lead=b[0];
|
||
const avgRet=b.reduce((s,r)=>s+r.ret,0)/b.length;
|
||
const engineAvg=b.reduce((s,r)=>s+r.engineRet,0)/b.length;
|
||
const core=b.filter(r=>!r.c.aggressive),coreAvg=core.reduce((s,r)=>s+r.engineRet,0)/Math.max(1,core.length);
|
||
const aggressive=b.filter(r=>r.c.aggressive),aggressiveAvg=aggressive.reduce((s,r)=>s+r.engineRet,0)/Math.max(1,aggressive.length);
|
||
const sugCount=(loadSuggestions().suggestions||[]).length;
|
||
const stats=[
|
||
{ic:lead.c.emoji,label:"Leader",value:lead.c.name.split(" ")[0]},
|
||
{ic:"📈",label:"Leader adaptive",value:fmtPct(lead.engineRet),cls:signClass(lead.enginePnl)},
|
||
{ic:"⚖️",label:"Legacy / replay avg",value:fmtPct(avgRet),cls:signClass(avgRet)},
|
||
{ic:"🧪",label:"Adaptive avg",value:fmtPct(engineAvg),cls:signClass(engineAvg)},
|
||
{ic:"🧠",label:"Core adaptive avg",value:fmtPct(coreAvg),cls:signClass(coreAvg)},
|
||
{ic:"⚡",label:"Aggressive adaptive",value:fmtPct(aggressiveAvg),cls:signClass(aggressiveAvg)},
|
||
{ic:"💡",label:"Scored ideas",value:sugCount},
|
||
{ic:"🏆",label:"Agents",value:AGENTS.length},
|
||
];
|
||
$("stats").innerHTML=stats.map(s=>`<div class="stat"><div class="ic">${s.ic}</div><div class="label">${s.label}</div><div class="value ${s.cls||""}">${s.value}</div></div>`).join("");
|
||
const st=loadState();
|
||
if(st.last_run)$("lastRun").textContent="updated "+new Date(st.last_run).toLocaleString();
|
||
drawCombo("chart","comboLegend");
|
||
renderTopPicks();
|
||
}
|
||
function renderTopPicks(){
|
||
const root=$("topPicks"); if(!root)return;
|
||
const top=((loadSuggestions().suggestions)||[]).filter(s=>s.trade_ready).slice(0,5);
|
||
if(!top.length){root.innerHTML=`<div class="empty">Run a cycle to see top picks.</div>`;return;}
|
||
root.innerHTML=top.map(s=>`<div class="pos"><div>
|
||
<div class="pq">${s.url?`<a class="market-title" href="${esc(s.url)}" target="_blank" rel="noopener">${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}</a>`:`${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}`}</div>
|
||
<div class="sub"><span class="cat-badge" style="background:${catColor(s.category)}22;color:${catColor(s.category)}">${esc(s.category)}</span> ${s.quality==="bundle-arb"?`COMPLETE ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE · ${(s.bundle_net_profit_per_unit*100).toFixed(1)}c modeled profit/unit`:`BUY ${s.side} @ ${Math.round(s.entry_price*100)}¢`}</div>
|
||
</div><div class="right"><div style="font-family:'Space Grotesk';font-weight:700">${Math.round(s.conviction)}</div><div class="sub">conviction</div></div></div>`).join("");
|
||
}
|
||
function renderEmailAlerts(){
|
||
const input=$("emailAlertInput"),eventsRoot=$("emailAlertEvents"),agentsRoot=$("emailAlertAgents"),meta=$("emailAlertMeta"),status=$("emailAlertStatus");
|
||
if(!input||!eventsRoot||!agentsRoot)return;
|
||
const cfg=loadEmailAlerts();
|
||
input.value=cfg.email||"";
|
||
const selectedCount=AGENTS.filter(a=>cfg.agents&&cfg.agents[a.id]).length;
|
||
meta.textContent=cfg.enabled?`on for ${cfg.email} · ${selectedCount}/${AGENTS.length} agents`:"off";
|
||
eventsRoot.innerHTML=EMAIL_ALERT_TYPES.map(t=>`
|
||
<label class="chip ${cfg.events&&cfg.events[t.id]?"active":""}" style="cursor:pointer">
|
||
<input type="checkbox" data-email-event="${t.id}" ${cfg.events&&cfg.events[t.id]?"checked":""} style="display:none"> ${t.label}
|
||
</label>`).join("");
|
||
agentsRoot.innerHTML=AGENTS.map(a=>`
|
||
<label class="chip ${cfg.agents&&cfg.agents[a.id]?"active":""}" style="cursor:pointer;border-color:${cfg.agents&&cfg.agents[a.id]?a.color:""}">
|
||
<input type="checkbox" data-email-agent="${esc(a.id)}" ${cfg.agents&&cfg.agents[a.id]?"checked":""} style="display:none"> ${a.emoji} ${esc(a.name)}
|
||
</label>`).join("");
|
||
const providerLine=TRADE_EMAIL_STATUS
|
||
? (TRADE_EMAIL_STATUS.configured
|
||
? `Email backend ready via ${TRADE_EMAIL_STATUS.provider}.`
|
||
: `Email backend missing: add Resend, SMTP, or webhook env vars in Vercel.`)
|
||
: "Checking email backend...";
|
||
const sentLine=cfg.last_sent_at?` Last sent ${new Date(cfg.last_sent_at).toLocaleString()}.`:"";
|
||
status.textContent=cfg.last_error
|
||
? `Last error: ${cfg.last_error}`
|
||
: `${providerLine}${sentLine} Sends a digest whenever selected agents trade during a cycle.`;
|
||
const readAlertForm=()=>{
|
||
const email=input.value.trim();
|
||
const events={};
|
||
const agents={};
|
||
document.querySelectorAll("[data-email-event]").forEach(el=>{events[el.dataset.emailEvent]=el.checked;});
|
||
document.querySelectorAll("[data-email-agent]").forEach(el=>{agents[el.dataset.emailAgent]=el.checked;});
|
||
return {email,events,agents};
|
||
};
|
||
const hasSelectedAgent=(agents)=>Object.values(agents||{}).some(Boolean);
|
||
$("saveEmailAlertsBtn").onclick=()=>{
|
||
const form=readAlertForm();
|
||
const anyAgent=hasSelectedAgent(form.agents);
|
||
const saved=saveEmailAlerts({enabled:validEmail(form.email)&&anyAgent,email:form.email,events:form.events,agents:form.agents,last_error:validEmail(form.email)?(anyAgent?null:"Select at least one agent."):"Enter a valid email address."});
|
||
toast(saved.enabled?"Trade email alerts saved.":(anyAgent?"Enter a valid email to turn alerts on.":"Select at least one agent."));
|
||
renderEmailAlerts();pushCloudState(true);
|
||
};
|
||
$("testEmailAlertsBtn").onclick=async()=>{
|
||
const form=readAlertForm();
|
||
const anyAgent=hasSelectedAgent(form.agents);
|
||
const saved=saveEmailAlerts({enabled:validEmail(form.email)&&anyAgent,email:form.email,events:form.events,agents:form.agents,last_error:anyAgent?null:"Select at least one agent."});
|
||
if(!saved.enabled){toast(anyAgent?"Enter a valid email first.":"Select at least one agent.");renderEmailAlerts();return;}
|
||
const testAgent=AGENTS.find(a=>saved.agents&&saved.agents[a.id])||AGENTS[0];
|
||
await notifyTradeDigest([{
|
||
agent_id:testAgent.id,
|
||
agent_name:testAgent.name,
|
||
agent_emoji:testAgent.emoji,
|
||
action:"OPEN",
|
||
date:todayStr(),
|
||
question:"Test alert from Poly Arena",
|
||
side:"YES",
|
||
detail:`This confirms trade email alerts are connected for ${testAgent.name}.`,
|
||
equity:STARTING_BALANCE,
|
||
return_pct:0,
|
||
}],{test:true});
|
||
await refreshTradeEmailStatus();
|
||
pushCloudState(true);
|
||
};
|
||
document.querySelectorAll("[data-email-event],[data-email-agent]").forEach(el=>el.onchange=()=>{
|
||
const form=readAlertForm();
|
||
saveEmailAlerts({enabled:validEmail(form.email)&&hasSelectedAgent(form.agents),email:form.email,events:form.events,agents:form.agents,last_error:null});
|
||
renderEmailAlerts();pushCloudState(true);
|
||
});
|
||
}
|
||
function miniSpark(snaps,color){
|
||
if(!snaps||snaps.length<2)return `<svg width="120" height="34"></svg>`;
|
||
const pts=snaps.map(s=>s.equity),W=120,H=34,p=3;
|
||
const mn=Math.min(...pts),mx=Math.max(...pts),sp=(mx-mn)||1;
|
||
const x=i=>p+(i/(pts.length-1))*(W-2*p),y=v=>H-p-((v-mn)/sp)*(H-2*p);
|
||
const d=pts.map((v,i)=>`${i?"L":"M"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
|
||
return `<svg width="${W}" height="${H}"><path d="${d}" fill="none" stroke="${color}" stroke-width="2"/></svg>`;
|
||
}
|
||
function agentBlurb(cfg,st){
|
||
return cfg.blurb;
|
||
}
|
||
function agentPlainBlurb(cfg,st){
|
||
return agentBlurb(cfg,st).replace(/<[^>]*>/g,"");
|
||
}
|
||
function bestAndWorst(p){
|
||
const open=p.positions||[];
|
||
if(!open.length)return {best:"No open trades yet",worst:"No drawdowns yet"};
|
||
const sorted=[...open].sort((a,b)=>(b.unrealized_pnl||0)-(a.unrealized_pnl||0));
|
||
const label=x=>`${x.question.slice(0,34)}${x.question.length>34?"...":""} (${(x.unrealized_pnl||0)>=0?"+":""}${fmtUSD(x.unrealized_pnl||0)})`;
|
||
return {best:label(sorted[0]),worst:label(sorted[sorted.length-1])};
|
||
}
|
||
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)}%; ceiling ${Math.round(d.targetExposure*100)}%.`:"";
|
||
const allocation=d.allocationStatus?` ${d.allocationStatus}`:"";
|
||
const candidates=d.tradeReadyCount!=null?` Candidate audit: ${d.tradeReadyCount} globally trade-ready, ${d.entryCandidateCount||d.tradeReadyCount||0} technical candidates, ${d.strategyCandidates||0} strategy matches, ${d.eligibleCandidates||0} fully eligible, ${d.opened||0} opened.`:"";
|
||
const blockerLabels={historical_prior:"history-tested losing setup",learning:"live learned losing regime",pilot_learning:"sports pilot demoted",shock_learning:"shock pilot unpromoted",resolution_week_learning:"resolution-week pilot demoted",event_overlap:"same event",confidence:"confidence",overlap:"material overlap",already_held:"already held",cooldown:"stop cooldown",focus:"category focus",price:"entry price",edge:"edge",timing:"timing",liquidity:"liquidity",activity:"activity",evidence:"evidence",direction:"direction",portfolio_limit:"portfolio limit"};
|
||
const blockerRows=Object.entries(d.rejectionCounts||{}).filter(([,count])=>count>0).sort((a,b)=>b[1]-a[1]);
|
||
const blockers=blockerRows.length?` Blocks: ${blockerRows.slice(0,4).map(([key,count])=>`${blockerLabels[key]||key} ${count}`).join(", ")}.`:"";
|
||
const learning=d.learning?` Learning: ${d.learning.samples} completed trades retained as historical context, ${(d.learning.global_score*100).toFixed(2)}% all-version shrunk expectancy; ${d.learning.current_samples||0} completed under adaptive strategy ${SUGGESTION_ENGINE_VERSION} with ${((d.learning.current_global_score||0)*100).toFixed(2)}% current-strategy shrunk expectancy. Only current-strategy closed trades can change sizing or personal cohort promotion${d.learning.best?`; historical strongest ${d.learning.best.feature.replace(":"," ")}`:""}${d.learning.worst?`; historical weakest ${d.learning.worst.feature.replace(":"," ")}`:""}.`:"";
|
||
let calibration="";
|
||
if(d.marketLearning){
|
||
const ml=d.marketLearning,hasCounts=Number.isFinite(ml.events)&&Number.isFinite(ml.markets),hasCurrentCounts=Number.isFinite(ml.current_events);
|
||
const countText=hasCounts?`across ${ml.events} event clusters / ${ml.markets} markets`:`with event and market counts unavailable in this older saved report`;
|
||
const currentText=hasCurrentCounts?`${ml.current_samples||0} observations / ${ml.current_events} events`:`${ml.current_samples||0} observations; independent-event count unavailable`;
|
||
const learningScope=ml.agent_id?`${agentById(ml.agent_id).name}'s eligible strategy universe`:"the shared research universe";
|
||
const queueAudit=Number.isFinite(ml.pending_global)?` Queue audit: ${ml.pending_legacy||0} legacy shared + ${ml.pending_agent_scoped||0} tagged for this agent; ${ml.pending_scoped||0} strategy-tagged and ${ml.pending_unassigned||0} unassigned across ${ml.pending_global||0} global pending records.`:"";
|
||
calibration=` Walk-forward calibration for ${learningScope}: ${ml.samples||0} net-of-cost checkpoint observations ${countText}, graded at ${SIGNAL_EARLY_RISK_HORIZONS.join("h, ")}h for early loss vetoes and ${SIGNAL_PROMOTION_HORIZONS.join("h and ")}h for promotion (${currentText} under directional signal policy ${ml.policy_version||DIRECTIONAL_SIGNAL_POLICY_VERSION}), ${ml.pending||0} awaiting a future checkpoint${ml.expired_ungraded?`, ${ml.expired_ungraded} expired checkpoints`:""}.${queueAudit} ${ml.promoted_buckets||0} horizon-specific feature cohorts promoted and ${ml.demoted_buckets||0} demoted. Promotion requires positive compatible-policy evidence at both promotion horizons across independent events; one mature negative cohort at any checkpoint can veto risk. Each agent studies a broader strategy-specific research universe before the stricter capital-entry gate is applied, while unlabeled legacy evidence remains readable. Sports pilots and priced bundles stay outside directional calibration. Missed windows expire rather than borrowing a later price. New observations prioritize under-sampled signal/side/category cohorts and independent events before repeats. Correlated outcome markets in one event count as one effective outcome. Historical prior: every directional trend and reversal remains observation-only until its exact recent cohorts independently promote; settlement-jump barriers stay excluded.`;
|
||
}
|
||
const makerStats=d.makerProfile&&d.makerProfile.global;
|
||
const maker=d.makerQuotes!=null?` Maker research: ${d.makerShadowActive||0} zero-capital lock-or-exit observations active; ${d.makerFills||0} verified touches and ${d.makerShadowCompleted||0} outcomes completed this cycle.${makerStats?` Event-clustered ledger: ${makerStats.attempts} attempts / ${makerStats.events} events, ${makerStats.locked} locked pairs, ${makerStats.adverse} immediate adverse exits, ${makerStats.unfilled} unfilled, and ${fmtUSD(makerStats.shadow_pnl)} simulated shadow net.`:""} Capital is disabled because neither the 6,048-rule chronological maker audit nor the reward stress test produced a validated holdout winner. Hypothetical rewards are excluded.`:"";
|
||
const sportsPilot=d.sportsFavoritePilot?` Sports contest NO learner: ${d.sportsFavoritePilot.state}, ${d.sportsFavoritePilot.active_shadows||0} zero-capital observations active and ${d.sportsFavoritePilot.events} independent settled contests, ${d.sportsFavoritePilot.events?`${(d.sportsFavoritePilot.mean*100).toFixed(2)}% mean with ${d.sportsFavoritePilot.lower>=0?"+":""}${(d.sportsFavoritePilot.lower*100).toFixed(2)}% to ${d.sportsFavoritePilot.upper>=0?"+":""}${(d.sportsFavoritePilot.upper*100).toFixed(2)}% 90% interval; `:""}${fmtUSD(d.sportsFavoritePilot.shadow_pnl_per_100||0)} simulated per-$100 net and ${fmtUSD(d.sportsFavoritePilot.pnl)} realized capital P&L. Current per-position cap ${(d.sportsFavoritePilot.position_pct*100).toFixed(2)}%; 20 positive independent outcomes can raise it to 0.75%, 40 can raise it to 1%, and a rejected forward interval suspends capital.`:"";
|
||
const shockPilot=d.shockFadePilot?` Shared shock strategy ${SHOCK_FADE_STRATEGY_VERSION}: ${d.shockFadePilot.state}, ${d.shockFadePilot.active_shadows||0} ${SHOCK_FADE_EXIT_HOURS}-hour observations active and ${d.shockFadePilot.events} deduplicated forward events complete across five adopters. Mean ${(d.shockFadePilot.mean*100).toFixed(2)}%; 90% interval ${(d.shockFadePilot.lower*100).toFixed(2)}% to ${(d.shockFadePilot.upper*100).toFixed(2)}%; ${d.shockFadePilot.expired_ungraded||0} missed windows were discarded. Position cap ${(d.shockFadePilot.position_pct*100).toFixed(2)}%, total cap ${SHOCK_FADE_TOTAL_CAPITAL_PCT*100}%; ${SHOCK_FADE_QUALIFICATION_EVENTS} positive events can qualify 1% capital and ${SHOCK_FADE_PROMOTION_EVENTS} stronger events can raise size to ${SHOCK_FADE_PROMOTED_POSITION_PCT*100}%.`:"";
|
||
const resolutionWeek=d.resolutionWeekNoPilot?` Corrected 4-day NO learner: ${d.resolutionWeekNoPilot.state}, ${d.resolutionWeekNoPilot.events} shared settled events, ${(d.resolutionWeekNoPilot.mean*100).toFixed(2)}% mean and ${(d.resolutionWeekNoPilot.lower*100).toFixed(2)}% lower 90% bound. Position cap ${(d.resolutionWeekNoPilot.position_pct*100).toFixed(2)}%; capital remains zero until 40 independent outcomes clear the forward confidence gate.`:"";
|
||
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}+`:""}.${learning}${calibration}${maker}${sportsPilot}${shockPilot}${resolutionWeek}${exposure}${allocation}${candidates}${blockers}`;
|
||
}
|
||
function renderAgentBrief(cfg,p,st){
|
||
const root=$("agentBrief"); if(!root)return;
|
||
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.id==="favorite"?"One contest-level sports NO entry per cycle; 0.5% initial size, 3% lane cap, and automatic evidence-based promotion or suspension":RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id)?`Zero-capital 4-day NO observations until 40 independent settlements promote 0.5% positions; plus zero-capital shock research`:`${cfg.maxNew||0} new trades max per cycle`;
|
||
const exitRule=cfg.id==="favorite"?"Grade zero-capital observations only when Polymarket closes the market; bounded capital positions retain the 18% stop and profit locks":RESOLUTION_WEEK_NO_ADOPTER_IDS.has(cfg.id)?`Grade 4-day NO observations only at verified settlement; after promotion, capital positions also hold to settlement; shock observations exit at the executable bid after ${SHOCK_FADE_EXIT_HOURS} hours`
|
||
:`Exit after a confirmed reversal, a 12h unconfirmed fade after 48h, a trailing-profit retrace, or ${EXIT_STALE_DAYS}d stale while losing; Politics trends get 72h before ordinary signal exits`;
|
||
const thesis=agentPlainBlurb(cfg,st);
|
||
const quoteSummary=(p.maker_quotes||[]).slice(0,3).map(quote=>`${quote.shadow_only?"Shadow":"Capital"} · ${quote.question.slice(0,34)}${quote.question.length>34?"...":""}: YES ${pct(quote.yes_quote)} + NO ${pct(quote.no_quote)}${quote.yes_filled_at||quote.no_filled_at?" (one leg touched)":""}`).join("; ");
|
||
root.innerHTML=`
|
||
<div class="brief-main" style="border-top:3px solid ${cfg.color}">
|
||
<div class="brief-title"><span>${cfg.emoji}</span><span>${cfg.name}</span></div>
|
||
<p>${esc(thesis)}</p>
|
||
<p style="margin-top:9px"><b>Adaptation:</b> ${esc(decisionSummary(p))}</p>
|
||
${quoteSummary?`<p style="margin-top:9px"><b>Maker observations:</b> ${esc(quoteSummary)}</p>`:""}
|
||
</div>
|
||
<div class="brief-mini"><div class="k">Style</div><div class="v">${esc(risk)}</div></div>
|
||
<div class="brief-mini"><div class="k">Trade Rule</div><div class="v">${esc(cadence)}</div></div>
|
||
<div class="brief-mini"><div class="k">Stop Loss</div><div class="v">Sell all at -18%</div></div>
|
||
<div class="brief-mini"><div class="k">Profit Locks</div><div class="v">Three reachable, entry-aware 25% sales</div></div>
|
||
<div class="brief-mini"><div class="k">Policy Exit</div><div class="v">${esc(exitRule)}</div></div>
|
||
<div class="brief-mini"><div class="k">Best / Worst</div><div class="v"><span class="pos-val">${esc(bw.best)}</span><br><span class="neg-val">${esc(bw.worst)}</span></div></div>`;
|
||
}
|
||
function renderLeaderboard(){
|
||
const b=board(); const st=loadState();
|
||
$("lbSub").textContent=`· ${b[0].c.name} leads Strategy ${SUGGESTION_ENGINE_VERSION}`;
|
||
$("leaderboard").innerHTML=b.map((r,i)=>`
|
||
<div class="lb-card" data-agent="${r.c.id}" style="border-top:3px solid ${r.c.color}">
|
||
<span class="rankpill">#${i+1}</span>
|
||
<div class="lb-top"><span class="medal">${MEDALS[i]||""}</span><span class="lb-emoji">${r.c.emoji}</span>
|
||
<div><div class="lb-name">${r.c.name}</div><div class="lb-blurb">${agentBlurb(r.c,st)}</div></div></div>
|
||
<div class="lb-mid"><div class="lb-eq">${fmtUSD(r.eq)}</div><div><div class="lb-ret ${signClass(r.enginePnl)}">${fmtPct(r.engineRet)}</div><div class="small ${signClass(r.pnl)}">legacy/replay ${fmtPct(r.ret)}</div><div class="small ${signClass(r.change24h.pct)}">24h ${format24h(r.change24h)}</div></div></div>
|
||
<div class="lb-foot"><span class="muted small">${r.p.positions.length} open · ${r.p.closed.length} closed</span>${miniSpark(r.p.snapshots,r.c.color)}</div>
|
||
</div>`).join("");
|
||
document.querySelectorAll("#leaderboard .lb-card").forEach(el=>{
|
||
el.addEventListener("click",()=>{localStorage.setItem(VIEW_KEY,el.dataset.agent);renderPortfolioTab();showTab("portfolio");});
|
||
});
|
||
drawCombo("chart2","comboLegend2");
|
||
}
|
||
const CHART_RANGES=[
|
||
{id:"1d",label:"1D",days:1},
|
||
{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 zoomChartRange(direction){
|
||
const idx=CHART_RANGES.findIndex(r=>r.id===chartRange());
|
||
const next=clamp(idx+direction,0,CHART_RANGES.length-1);
|
||
localStorage.setItem(CHART_RANGE_KEY,CHART_RANGES[next].id);
|
||
renderAll();
|
||
}
|
||
function renderChartRanges(){
|
||
const active=chartRange();
|
||
document.querySelectorAll("[data-chart-ranges]").forEach(root=>{
|
||
root.innerHTML=`<button class="rangebtn" data-chart-zoom="-1" type="button" title="Zoom in">-</button>`+
|
||
CHART_RANGES.map(r=>`<button class="rangebtn ${r.id===active?"active":""}" data-range="${r.id}" type="button">${r.label}</button>`).join("")+
|
||
`<button class="rangebtn" data-chart-zoom="1" type="button" title="Zoom out">+</button>`;
|
||
});
|
||
document.querySelectorAll(".rangebtn").forEach(btn=>btn.onclick=()=>{localStorage.setItem(CHART_RANGE_KEY,btn.dataset.range);renderAll();});
|
||
document.querySelectorAll("[data-chart-zoom]").forEach(btn=>btn.onclick=()=>zoomChartRange(Number(btn.dataset.chartZoom||0)));
|
||
}
|
||
const snapTime=(s)=>new Date(s.timestamp||`${s.date}T12:00:00Z`).getTime();
|
||
function chartDateLabel(t,compact=false){
|
||
const d=new Date(t);
|
||
if(isNaN(d))return "";
|
||
const sameYear=d.getUTCFullYear()===new Date().getUTCFullYear();
|
||
return d.toLocaleDateString("en-US",compact
|
||
? {month:"short",day:"numeric"}
|
||
: (sameYear?{month:"short",day:"numeric"}:{month:"short",day:"numeric",year:"2-digit"}));
|
||
}
|
||
function chartTicks(times,maxTicks=5){
|
||
if(!times.length)return [];
|
||
if(times.length===1)return [{i:0,t:times[0]}];
|
||
const count=Math.min(maxTicks,times.length),seen=new Set(),ticks=[];
|
||
for(let n=0;n<count;n++){
|
||
const i=Math.round(n*(times.length-1)/(count-1));
|
||
if(!seen.has(i)){seen.add(i);ticks.push({i,t:times[i]});}
|
||
}
|
||
return ticks;
|
||
}
|
||
function niceChartBounds(vals){
|
||
let min=Math.min(...vals,0),max=Math.max(...vals,0);
|
||
if(min===max){min-=1;max+=1;}
|
||
const span=max-min,pad=Math.max(0.5,span*0.12);
|
||
min-=pad;max+=pad;
|
||
const step=Math.max(0.5,Math.pow(10,Math.floor(Math.log10(max-min)))/2);
|
||
min=Math.floor(min/step)*step;max=Math.ceil(max/step)*step;
|
||
return {min,max};
|
||
}
|
||
function smoothPath(points){
|
||
if(points.length<2)return points.map((p,i)=>`${i?"L":"M"}${p.x},${p.y}`).join(" ");
|
||
let d=`M${points[0].x},${points[0].y}`;
|
||
for(let i=1;i<points.length;i++){
|
||
const prev=points[i-1],cur=points[i];
|
||
const midX=((+prev.x)+(+cur.x))/2;
|
||
d+=` C${midX.toFixed(1)},${prev.y} ${midX.toFixed(1)},${cur.y} ${cur.x},${cur.y}`;
|
||
}
|
||
return d;
|
||
}
|
||
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();
|
||
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(!allTimes.length){svg.innerHTML="";if($(legendId))$(legendId).innerHTML=`<span class="muted small">No history yet — click "Run cycle".</span>`;return;}
|
||
const W=960,H=320,padL=72,padR=36,padT=36,padB=54; const vals=[0];
|
||
const lines=series.map(s=>{
|
||
let last=0,idx=0;
|
||
const pts=allTimes.map(t=>{
|
||
while(idx<s.snaps.length&&snapTime(s.snaps[idx])<=t){
|
||
last=Number.isFinite(s.snaps[idx].return_pct)?s.snaps[idx].return_pct:((s.snaps[idx].equity||STARTING_BALANCE)/STARTING_BALANCE-1)*100;
|
||
idx++;
|
||
}
|
||
vals.push(last);return last;
|
||
});
|
||
return {c:s.c,pts};
|
||
});
|
||
const avgPts=allTimes.map((_,i)=>+(lines.reduce((sum,l)=>sum+l.pts[i],0)/Math.max(1,lines.length)).toFixed(2));
|
||
vals.push(...avgPts);
|
||
const bounds=niceChartBounds(vals),min=bounds.min,max=bounds.max,span=(max-min)||1;
|
||
const x=i=>allTimes.length===1?(padL+(W-padR))/2:padL+(i/(allTimes.length-1))*(W-padL-padR);
|
||
const y=v=>H-padB-((v-min)/span)*(H-padT-padB);
|
||
const yTicks=Array.from({length:5},(_,i)=>+(min+(span*i/4)).toFixed(1));
|
||
const zeroY=y(0).toFixed(1);
|
||
let inner=`<defs>
|
||
<linearGradient id="${svgId}-panel" x1="0" x2="0" y1="0" y2="1">
|
||
<stop offset="0%" stop-color="rgba(255,255,255,.055)"/><stop offset="100%" stop-color="rgba(255,255,255,.018)"/>
|
||
</linearGradient>
|
||
<filter id="${svgId}-soft" x="-20%" y="-20%" width="140%" height="140%">
|
||
<feDropShadow dx="0" dy="4" stdDeviation="5" flood-color="rgba(0,0,0,.22)"/>
|
||
</filter>
|
||
</defs>`;
|
||
inner+=`<rect x="${padL}" y="${padT}" width="${W-padL-padR}" height="${H-padT-padB}" rx="12" fill="url(#${svgId}-panel)" stroke="rgba(255,255,255,.08)"/>`;
|
||
for(const v of yTicks){
|
||
const yy=y(v).toFixed(1);
|
||
inner+=`<line x1="${padL}" x2="${W-padR}" y1="${yy}" y2="${yy}" stroke="rgba(255,255,255,.075)" stroke-width="1"/>`;
|
||
inner+=`<text x="${padL-12}" y="${(+yy+4).toFixed(1)}" text-anchor="end" fill="rgba(224,234,255,.62)" font-size="11" font-family="Inter, sans-serif">${fmtPct(v)}</text>`;
|
||
}
|
||
inner+=`<line x1="${padL}" x2="${W-padR}" y1="${zeroY}" y2="${zeroY}" stroke="rgba(255,255,255,.28)" stroke-width="1.2"/>`;
|
||
for(const tick of chartTicks(allTimes,5)){
|
||
const xx=x(tick.i).toFixed(1);
|
||
inner+=`<line x1="${xx}" x2="${xx}" y1="${padT}" y2="${H-padB}" stroke="rgba(255,255,255,.055)" stroke-width="1"/>`;
|
||
inner+=`<text x="${xx}" y="${H-18}" text-anchor="middle" fill="rgba(224,234,255,.72)" font-size="11" font-family="Inter, sans-serif">${chartDateLabel(tick.t,true)}</text>`;
|
||
}
|
||
for(const ln of lines){
|
||
const points=ln.pts.map((v,i)=>({x:x(i).toFixed(1),y:y(v).toFixed(1)}));
|
||
const d=smoothPath(points);
|
||
const last=points[points.length-1];
|
||
inner+=`<path d="${d}" fill="none" stroke="${ln.c.color}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity=".92" filter="url(#${svgId}-soft)"/>`;
|
||
inner+=`<circle cx="${last.x}" cy="${last.y}" r="4.2" fill="${ln.c.color}" stroke="#101827" stroke-width="2"/>`;
|
||
}
|
||
const avgPoints=avgPts.map((v,i)=>({x:x(i).toFixed(1),y:y(v).toFixed(1)}));
|
||
inner+=`<path d="${smoothPath(avgPoints)}" fill="none" stroke="#ffffff" stroke-width="3.2" stroke-dasharray="8 7" stroke-linecap="round" opacity=".82"/>`;
|
||
inner+=`<text x="${padL}" y="21" fill="rgba(224,234,255,.64)" font-size="11.5" font-family="Inter, sans-serif">Return %</text>`;
|
||
inner+=`<text x="${W-padR}" y="21" text-anchor="end" fill="rgba(224,234,255,.58)" font-size="11.5" font-family="Inter, sans-serif">${chartDateLabel(allTimes[0])} - ${chartDateLabel(allTimes[allTimes.length-1])}</text>`;
|
||
svg.innerHTML=inner;
|
||
if($(legendId)){
|
||
const b=board();const avg=b.reduce((s,r)=>s+r.ret,0)/b.length;
|
||
const core=b.filter(r=>!r.c.aggressive),coreAvg=core.reduce((s,r)=>s+r.ret,0)/Math.max(1,core.length);
|
||
const aggressive=b.filter(r=>r.c.aggressive),aggressiveAvg=aggressive.reduce((s,r)=>s+r.ret,0)/Math.max(1,aggressive.length);
|
||
$(legendId).innerHTML=`<span class="leg"><span class="legdot" style="background:#fff"></span>Mixed-inception Avg <b class="${signClass(avg)}">${fmtPct(avg)}</b></span>`+
|
||
`<span class="leg"><span class="legdot" style="background:#38d2e6"></span>Core Avg <b class="${signClass(coreAvg)}">${fmtPct(coreAvg)}</b></span>`+
|
||
`<span class="leg"><span class="legdot" style="background:#ff8a4c"></span>Aggressive Avg <b class="${signClass(aggressiveAvg)}">${fmtPct(aggressiveAvg)}</b></span>`+
|
||
b.map(r=>`<span class="leg"><span class="legdot" style="background:${r.c.color}"></span>${r.c.emoji} ${r.c.name} <b class="${signClass(r.pnl)}">${fmtPct(r.ret)}</b></span>`).join("");
|
||
}
|
||
}
|
||
function renderFilterBar(all){
|
||
const focus=getFocus();
|
||
const counts={};CATEGORIES.forEach(c=>counts[c]=0);
|
||
all.forEach(s=>{const c=counts[s.category]!==undefined?s.category:"Other";counts[c]++;});
|
||
counts["All"]=all.length;
|
||
$("filterbar").innerHTML=CATEGORIES.map(c=>{const n=counts[c]||0;const dim=(c!=="All"&&n===0)?"opacity:.4;":"";
|
||
return `<span class="chip ${c===focus?"active":""}" data-cat="${c}" style="${dim}">${c}<span class="ct">${n}</span></span>`;}).join("");
|
||
document.querySelectorAll("#filterbar .chip").forEach(el=>el.addEventListener("click",()=>{setFocus(el.dataset.cat);renderSuggestions();}));
|
||
}
|
||
function renderSuggestions(){
|
||
const data=loadSuggestions();
|
||
$("sugDate").textContent=data.date?`· ${data.date}`:"";
|
||
const all=data.suggestions||[];const root=$("suggestions");
|
||
renderFilterBar(all);
|
||
if(!all.length){root.innerHTML=`<div class="empty" style="grid-column:1/-1">No suggestions yet. Click "Run cycle".</div>`;$("focusNote").textContent="";return;}
|
||
const focus=getFocus();
|
||
const pool=(focus==="All"?all:all.filter(s=>s.category===focus));
|
||
const filtered=pool.slice(0,VISIBLE_SUGGESTIONS);
|
||
const buyCount=all.filter(s=>s.trade_ready).length,watchCount=all.length-buyCount;
|
||
const evAvg=all.length?Math.round(all.reduce((s,x)=>s+Number(x.evidence_score||0),0)/all.length*100):0;
|
||
const scanned=Number(data.market_count||0).toLocaleString();
|
||
const analyzed=Number(data.analyzed_count||data.market_count||0).toLocaleString();
|
||
const audit=data.opportunity_audit||null;
|
||
const auditText=audit&&audit.status==="ok"
|
||
?` Bundle scanner checked ${Number(audit.events_scanned||0).toLocaleString()} events and ${Number(audit.evaluated_structures||0).toLocaleString()} complete/dominance structures, depth-tested ${Number(audit.depth_checked_structures||0)}, and found ${Number(audit.depth_verified_structures||0)} with $50 depth; ${Number(audit.actionable_bundles||0)} also cleared the market-specific fee and profit gates.${audit.closest_margin_cents!=null?` Closest depth-tested margin: ${Number(audit.closest_margin_cents)>=0?"+":""}${Number(audit.closest_margin_cents).toFixed(2)}c per bundle.`:""}`
|
||
:(audit?" Bundle scanner was unavailable during this cycle; the site did not treat cached bundle prices as executable.":"");
|
||
$("focusNote").textContent=focus==="All"
|
||
? `Loaded the ${scanned} most active markets, analyzed ${analyzed}, and kept ${all.length} ideas (${buyCount} TRADE READY, ${watchCount} WATCH). Showing ${filtered.length}; directional signals remain WATCH until their own event-clustered cohorts earn positive promotion. Complete bundles are trade ready only after every leg clears a live $50 order-book depth check and the CLOB fee state agrees with the market-specific taker curve while preserving a positive worst-case payout margin. Cash is retained when neither condition is met. Avg evidence ${evAvg}.${auditText}`
|
||
: `Loaded the ${scanned} most active markets and analyzed ${analyzed}. Showing ${filtered.length} of ${pool.length} ${focus} ideas; directional entries require adaptive promotion and complete bundles require live positive executable margin.`;
|
||
if(!filtered.length){root.innerHTML=`<div class="empty" style="grid-column:1/-1">No ${esc(focus)} suggestions in this live batch.</div>`;return;}
|
||
root.innerHTML=filtered.map(s=>{
|
||
const col=catColor(s.category);
|
||
const drivers=s.drivers.map(d=>`<span class="driver">${d}</span>`).join("");
|
||
const promotedNames=(s.promoted_for_agents||[]).map(id=>agentById(id).name);
|
||
const days=s.days_to_resolution!=null?`${s.days_to_resolution}d`:"—";
|
||
return `<div class="sug" style="border-left-color:${col}">
|
||
<div class="sug-row" style="margin-bottom:2px"><span class="cat-badge" style="background:${col}22;color:${col}">${esc(s.category||"Other")}</span></div>
|
||
<div class="q">${s.url?`<a class="market-title" href="${esc(s.url)}" target="_blank" rel="noopener">${esc(s.question)}</a>`:esc(s.question)}</div>
|
||
<div class="event">${esc(s.event||"")}</div>
|
||
<div class="sug-row">
|
||
<span class="pill ${s.side}">${s.quality==="bundle-arb"?`COMPLETE ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE`:`${s.trade_ready?"BUY":"WATCH"} ${s.side}`}</span>
|
||
<span class="muted small">${esc(String(s.quality||"watch").toUpperCase())}</span>
|
||
<span class="muted small">@ ${Math.round(s.entry_price*100)}¢</span>
|
||
<div class="conv-bar"><span style="width:${s.conviction}%"></span></div>
|
||
<span class="muted small">${Math.round(s.conviction)}</span>
|
||
</div>
|
||
<div class="rationale">${esc(s.rationale)}</div>
|
||
${promotedNames.length?`<div class="event">Eligible after strategy-specific calibration: ${esc(promotedNames.join(", "))}</div>`:""}
|
||
<div class="drivers">${drivers}</div>
|
||
<div class="metrics">
|
||
<span>${s.quality==="bundle-arb"?`Modeled return <b>${(s.net_edge*100).toFixed(2)}%</b>`:`Signal margin <b>${((Math.abs(s.net_edge!=null?s.net_edge:s.edge))*100).toFixed(1)}c</b>`}</span><span>Evidence <b>${Math.round((s.evidence_score||0)*100)}</b></span>
|
||
<span>Vol <b>${fmtUSD(s.volume)}</b></span><span>24h <b>${fmtUSD(s.volume_24hr)}</b></span><span>Resolves <b>${days}</b></span>
|
||
${s.url?`<a class="market-link" href="${esc(s.url)}" target="_blank" rel="noopener">Open on Polymarket ↗</a>`:""}
|
||
${PERSONAL_MODE&&s.quality!=="bundle-arb"?`<button class="btn ghost" data-stage-suggestion="${esc(s.market_id)}">Stage ticket</button>`:""}
|
||
</div></div>`;
|
||
}).join("");
|
||
document.querySelectorAll("[data-stage-suggestion]").forEach(btn=>btn.onclick=()=>stageSuggestionTicket(btn.dataset.stageSuggestion));
|
||
}
|
||
function renderPortfolioTab(){
|
||
const st=loadState();
|
||
let viewId=localStorage.getItem(VIEW_KEY); if(!AGENTS.some(a=>a.id===viewId))viewId="value";
|
||
const cfg=agentById(viewId);
|
||
$("agentSel").innerHTML=AGENTS.map(a=>{
|
||
const active=a.id===viewId;
|
||
const style=active?`background:linear-gradient(135deg,${a.color},${a.color});border-color:transparent`:"";
|
||
return `<span class="segbtn ${active?"active":""}" data-agent="${a.id}" style="${style}">${a.emoji} ${a.name}</span>`;}).join("");
|
||
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 makerProfile=buildMakerProfile(p);
|
||
const engineBase=Number(p.engine_baseline&&normalizedStrategyVersion(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)},
|
||
{ic:"💵",label:"Cash",value:fmtUSD(p.cash)},
|
||
{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:"Adaptive",value:fmtPct(engineBase>0?(eq/engineBase-1)*100:0),cls:signClass(enginePnl)},
|
||
{ic:"📂",label:"Open",value:p.positions.length},
|
||
{ic:"◌",label:"Sports shadow",value:(p.sports_favorite_shadows||[]).length},
|
||
{ic:"⧗",label:"Shock shadow",value:(p.shock_fade_shadows||[]).length},
|
||
{ic:"⇄",label:"Maker shadow",value:(p.maker_quotes||[]).filter(quote=>quote.shadow_only).length},
|
||
{ic:"Σ",label:"Maker paper net",value:fmtUSD(makerProfile.global.pnl),cls:signClass(makerProfile.global.pnl)},
|
||
];
|
||
$("statsPf").innerHTML=stats.map(s=>`<div class="stat"><div class="ic">${s.ic}</div><div class="label">${s.label}</div><div class="value ${s.cls||""}">${s.value}</div></div>`).join("");
|
||
const sort=$("positionSort");
|
||
if(sort){
|
||
sort.value=localStorage.getItem(PF_SORT_KEY)||"pnl_desc";
|
||
sort.onchange=()=>{localStorage.setItem(PF_SORT_KEY,sort.value);renderPositions(p.positions);};
|
||
}
|
||
renderPositions(p.positions);
|
||
renderHistory((p.history||[]).slice(-50).reverse());
|
||
renderAgentTechnical(viewId);
|
||
renderAgentReports();
|
||
renderAgentChat(viewId);
|
||
}
|
||
const avg=(arr)=>arr.length?arr.reduce((s,x)=>s+x,0)/arr.length:0;
|
||
function ema(vals,period){
|
||
const k=2/(period+1),out=[];let prev=vals[0]||0;
|
||
vals.forEach((v,i)=>{prev=i?((v*k)+(prev*(1-k))):v;out.push(prev);});
|
||
return out;
|
||
}
|
||
function renderAgentTechnical(agentId){
|
||
const svg=$("agentTAChart"),readout=$("agentTAReadout");if(!svg||!readout)return;
|
||
const st=loadState(),cfg=agentById(agentId),p=st.agents[agentId]||defaultPortfolio();
|
||
const snaps=(p.snapshots||[]).filter(s=>Number.isFinite(snapTime(s))).sort((a,b)=>snapTime(a)-snapTime(b)).slice(-60);
|
||
if(snaps.length<2){svg.innerHTML="";readout.innerHTML=`<div class="empty">Need at least two return snapshots for technical analysis.</div>`;return;}
|
||
const closes=snaps.map(s=>s.equity||STARTING_BALANCE),times=snaps.map(snapTime),rets=snaps.map(s=>s.return_pct||0);
|
||
const ma5=closes.map((_,i)=>avg(closes.slice(Math.max(0,i-4),i+1)));
|
||
const ma10=closes.map((_,i)=>avg(closes.slice(Math.max(0,i-9),i+1)));
|
||
const macd=ema(closes,6).map((v,i)=>v-ema(closes,13)[i]),signal=ema(macd,5),hist=macd.map((v,i)=>v-signal[i]);
|
||
const W=960,H=420,padL=64,padR=26,padT=28,padB=40,priceH=250,macdTop=300;
|
||
const vals=closes.concat(ma5,ma10),mn=Math.min(...vals),mx=Math.max(...vals),span=(mx-mn)||1;
|
||
const x=i=>padL+(i/(snaps.length-1))*(W-padL-padR),y=v=>padT+(mx-v)/span*(priceH-padT);
|
||
const mMax=Math.max(...hist.map(Math.abs),1),my=v=>macdTop+48-(v/mMax)*42;
|
||
let inner=`<rect x="${padL}" y="${padT}" width="${W-padL-padR}" height="${priceH-padT}" rx="12" fill="rgba(255,255,255,.025)" stroke="rgba(255,255,255,.08)"/>`;
|
||
for(let i=0;i<5;i++){const v=mn+(span*i/4),yy=y(v);inner+=`<line x1="${padL}" x2="${W-padR}" y1="${yy}" y2="${yy}" stroke="rgba(255,255,255,.06)"/><text x="${padL-10}" y="${yy+4}" text-anchor="end" fill="rgba(224,234,255,.62)" font-size="11">${fmtUSD(v)}</text>`;}
|
||
const w=Math.max(3,(W-padL-padR)/snaps.length*.48);
|
||
closes.forEach((c,i)=>{const o=i?closes[i-1]:c,up=c>=o,col=up?"#7c8cff":"#fb7185";const xx=x(i);
|
||
inner+=`<rect x="${xx-w/2}" y="${y(Math.max(o,c))}" width="${w}" height="${Math.max(2,Math.abs(y(c)-y(o)))}" rx="1.5" fill="${col}" opacity=".68"/>`;});
|
||
const path=(arr)=>arr.map((v,i)=>`${i?"L":"M"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
|
||
inner+=`<path d="${path(ma5)}" fill="none" stroke="#fbbf24" stroke-width="2.4"/><path d="${path(ma10)}" fill="none" stroke="#38d2e6" stroke-width="2.4"/>`;
|
||
inner+=`<line x1="${padL}" x2="${W-padR}" y1="${my(0)}" y2="${my(0)}" stroke="rgba(255,255,255,.18)"/>`;
|
||
hist.forEach((v,i)=>{const xx=x(i),yy=my(Math.max(0,v)),hh=Math.abs(my(v)-my(0));inner+=`<rect x="${xx-w/2}" y="${v>=0?yy:my(0)}" width="${w}" height="${Math.max(1,hh)}" fill="${v>=0?"#94a3b8":"#fb7185"}" opacity=".55"/>`;});
|
||
for(const tick of chartTicks(times,4)){inner+=`<text x="${x(tick.i)}" y="${H-14}" text-anchor="middle" fill="rgba(224,234,255,.7)" font-size="11">${chartDateLabel(tick.t,true)}</text>`;}
|
||
inner+=`<text x="${padL}" y="18" fill="rgba(224,234,255,.7)" font-size="12">${cfg.name} equity candles · MA5 yellow · MA10 cyan · neutral MACD bars</text>`;
|
||
svg.innerHTML=inner;
|
||
const last=closes.length-1,trend=ma5[last]>=ma10[last]?"bullish":"bearish",mom=hist[last]>=0?"positive":"negative";
|
||
readout.innerHTML=`<div class="invest-num"><div class="k">Trend</div><div class="v">${trend}</div></div>
|
||
<div class="invest-num" style="margin-top:10px"><div class="k">MACD</div><div class="v">${mom}</div></div>
|
||
<p class="muted">Latest return ${fmtPct(rets[last])}. MA5 is ${fmtUSD(ma5[last])}; MA10 is ${fmtUSD(ma10[last])}. Activity proxy: ${snaps[last].open_positions||0} open positions.</p>`;
|
||
}
|
||
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 with complete pricing bundles and paired resting bids, counting return only after both complementary legs fill below their combined payout and unwinding unmatched inventory after 24 hours.";
|
||
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: select only the highest-priced eligible NO contract per real sports contest near the three-day pregame checkpoint, pay the executable ask plus exact taker fee and slippage, start at 0.5%, and let independent settled results promote or suspend the lane.";
|
||
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: hold confirmed, non-chased trends long enough to develop, favoring Politics or Economy markets with at least three weeks left instead of repeating the reversal rule that failed the audit.";
|
||
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.";
|
||
}
|
||
function recentActionSummary(p){
|
||
const recent=(p.history||[]).slice(-8);
|
||
const counts={STOP:0,GAIN:0,EXIT:0,CLOSE:0,OPEN:0,RISK:0,QUOTE:0,MAKER_FILL:0,MAKER_LOCK:0,MAKER_EXIT:0,MAKER_EXPIRE:0};
|
||
recent.forEach(h=>{if(counts[h.action]!==undefined)counts[h.action]++;});
|
||
const bits=[];
|
||
if(counts.STOP)bits.push(`${counts.STOP} stop-loss sale${counts.STOP===1?"":"s"}`);
|
||
if(counts.GAIN)bits.push(`${counts.GAIN} gain-stop cash-in${counts.GAIN===1?"":"s"}`);
|
||
if(counts.EXIT||counts.CLOSE)bits.push(`${counts.EXIT+counts.CLOSE} exit${counts.EXIT+counts.CLOSE===1?"":"s"}`);
|
||
if(counts.RISK)bits.push(`${counts.RISK} binary-risk rebalance${counts.RISK===1?"":"s"}`);
|
||
if(counts.OPEN)bits.push(`${counts.OPEN} new entr${counts.OPEN===1?"y":"ies"}`);
|
||
if(counts.QUOTE)bits.push(`${counts.QUOTE} paired quote${counts.QUOTE===1?"":"s"} staged`);
|
||
if(counts.MAKER_FILL)bits.push(`${counts.MAKER_FILL} maker fill${counts.MAKER_FILL===1?"":"s"}`);
|
||
if(counts.MAKER_LOCK)bits.push(`${counts.MAKER_LOCK} locked pair${counts.MAKER_LOCK===1?"":"s"}`);
|
||
if(counts.MAKER_EXIT)bits.push(`${counts.MAKER_EXIT} unmatched maker exit${counts.MAKER_EXIT===1?"":"s"}`);
|
||
if(counts.MAKER_EXPIRE)bits.push(`${counts.MAKER_EXPIRE} unfilled quote expiry`);
|
||
return bits.length?bits.join(", "):"no major recent trade actions";
|
||
}
|
||
function snapshotNearDayAgo(snaps,lastTime){
|
||
const cutoff=lastTime-86400000;
|
||
const before=snaps.filter(s=>snapTime(s)<=cutoff).pop();
|
||
if(before)return before;
|
||
return snaps.length>1?snaps[0]:null;
|
||
}
|
||
function dailyActionSummary(p,lastTime){
|
||
const cutoff=lastTime-86400000;
|
||
const counts={STOP:0,GAIN:0,EXIT:0,CLOSE:0,OPEN:0,RISK:0,QUOTE:0,MAKER_FILL:0,MAKER_LOCK:0,MAKER_EXIT:0,MAKER_EXPIRE:0};
|
||
(p.history||[]).forEach(h=>{
|
||
const t=h.date?new Date(`${h.date}T12:00:00`).getTime():NaN;
|
||
if(Number.isFinite(t)&&t>=cutoff&&counts[h.action]!==undefined)counts[h.action]++;
|
||
});
|
||
const bits=[];
|
||
if(counts.OPEN)bits.push(`${counts.OPEN} opened`);
|
||
if(counts.GAIN)bits.push(`${counts.GAIN} gain-stop sale${counts.GAIN===1?"":"s"}`);
|
||
if(counts.STOP)bits.push(`${counts.STOP} stop-loss sale${counts.STOP===1?"":"s"}`);
|
||
if(counts.EXIT||counts.CLOSE)bits.push(`${counts.EXIT+counts.CLOSE} exit${counts.EXIT+counts.CLOSE===1?"":"s"}`);
|
||
if(counts.RISK)bits.push(`${counts.RISK} binary-risk rebalance${counts.RISK===1?"":"s"}`);
|
||
if(counts.QUOTE)bits.push(`${counts.QUOTE} paired quote${counts.QUOTE===1?"":"s"} staged`);
|
||
if(counts.MAKER_FILL)bits.push(`${counts.MAKER_FILL} maker fill${counts.MAKER_FILL===1?"":"s"}`);
|
||
if(counts.MAKER_LOCK)bits.push(`${counts.MAKER_LOCK} locked pair${counts.MAKER_LOCK===1?"":"s"}`);
|
||
if(counts.MAKER_EXIT)bits.push(`${counts.MAKER_EXIT} unmatched maker exit${counts.MAKER_EXIT===1?"":"s"}`);
|
||
if(counts.MAKER_EXPIRE)bits.push(`${counts.MAKER_EXPIRE} unfilled quote expiry`);
|
||
return bits.length?bits.join(", "):recentActionSummary(p);
|
||
}
|
||
function recentClosedAttribution(p,lastTime){
|
||
const cutoff=lastTime-86400000;
|
||
return (p.closed||[]).filter(pos=>{
|
||
const t=pos.closed_at?new Date(pos.closed_at).getTime():NaN;
|
||
return Number.isFinite(t)&&t>=cutoff;
|
||
}).sort((a,b)=>Math.abs(b.realized_pnl||0)-Math.abs(a.realized_pnl||0));
|
||
}
|
||
function shortQuestion(q,len=48){
|
||
q=String(q||"");
|
||
return `${q.slice(0,len)}${q.length>len?"...":""}`;
|
||
}
|
||
function reportPositionLine(p){
|
||
if(!p.positions.length)return "No open positions yet, so this hour's focus is finding entries that fit the strategy.";
|
||
const best=[...p.positions].sort((a,b)=>(b.unrealized_pnl||0)-(a.unrealized_pnl||0))[0];
|
||
const worst=[...p.positions].sort((a,b)=>(a.unrealized_pnl||0)-(b.unrealized_pnl||0))[0];
|
||
if(best===worst)return `Current focus: ${best.question.slice(0,58)}${best.question.length>58?"...":""}, marked ${best.unrealized_pnl>=0?"+":""}${fmtUSD(best.unrealized_pnl||0)}.`;
|
||
return `Best mark: ${best.question.slice(0,42)}${best.question.length>42?"...":""} (${best.unrealized_pnl>=0?"+":""}${fmtUSD(best.unrealized_pnl||0)}). Watch item: ${worst.question.slice(0,42)}${worst.question.length>42?"...":""} (${fmtUSD(worst.unrealized_pnl||0)}).`;
|
||
}
|
||
function attributionLine(pos){
|
||
if(!pos)return "No single trade explains the move.";
|
||
const bits=[];
|
||
if(pos.entry_reason)bits.push(pos.entry_reason);
|
||
if(pos.net_edge!=null)bits.push(`entry net edge ${((Math.abs(pos.net_edge))*100).toFixed(1)}c`);
|
||
if(pos.evidence_score!=null)bits.push(`evidence ${Math.round(Number(pos.evidence_score||0)*100)}`);
|
||
if(pos.friction!=null)bits.push(`friction ${((Number(pos.friction||0))*100).toFixed(1)}c`);
|
||
return bits.length?bits.join(" · "):"This older position was opened before detailed attribution was stored.";
|
||
}
|
||
function dailyPerformanceExplanation(row){
|
||
const snaps=(row.p.snapshots||[]).slice().sort((a,b)=>snapTime(a)-snapTime(b));
|
||
const last=snaps[snaps.length-1];
|
||
if(!last)return "Why today is unclear: the agent needs at least one return snapshot before it can explain performance.";
|
||
const lastTime=snapTime(last);
|
||
const prev=snapshotNearDayAgo(snaps,lastTime);
|
||
if(!prev)return "Why today is still forming: there is not enough 24-hour history yet, so the report is waiting for more snapshots.";
|
||
const delta=(last.return_pct||0)-(prev.return_pct||0);
|
||
const equityDelta=(last.equity||0)-(prev.equity||0);
|
||
const open=row.p.positions||[];
|
||
const best=open.slice().sort((a,b)=>(b.unrealized_pnl||0)-(a.unrealized_pnl||0))[0];
|
||
const worst=open.slice().sort((a,b)=>(a.unrealized_pnl||0)-(b.unrealized_pnl||0))[0];
|
||
const recentClosed=recentClosedAttribution(row.p,lastTime);
|
||
const mostImportantClose=recentClosed[0];
|
||
const realizedToday=recentClosed.reduce((s,p)=>s+Number(p.realized_pnl||0),0);
|
||
const m=agentMomentumStats(row.p);
|
||
const macdText=`MACD ${m.macd>=0?"+":""}${m.macd.toFixed(2)}, momentum ${m.recent>=0?"+":""}${m.recent.toFixed(2)} pts`;
|
||
const actions=dailyActionSummary(row.p,lastTime);
|
||
const closedText=mostImportantClose
|
||
? `The largest realized driver was '${shortQuestion(mostImportantClose.question)}' at ${fmtUSD(mostImportantClose.realized_pnl||0)}; total realized P&L over the past day was ${fmtUSD(realizedToday)}.`
|
||
: "There were no major realized closes in the past day.";
|
||
if(delta>0.25){
|
||
const openText=best?`The strongest open position helping it is '${shortQuestion(best.question)}' at ${best.unrealized_pnl>=0?"+":""}${fmtUSD(best.unrealized_pnl||0)}.`:"It benefited mostly from cash discipline and closed-trade results rather than a single open winner.";
|
||
return `Past 24h: up ${delta.toFixed(2)} return points (${fmtUSD(equityDelta)}). ${openText} Attribution: ${attributionLine(best)} ${closedText} Trade behavior: ${actions}. Technical pressure: ${macdText}.`;
|
||
}
|
||
if(delta<-0.25){
|
||
const openText=worst?`The biggest open drag is '${shortQuestion(worst.question)}' at ${fmtUSD(worst.unrealized_pnl||0)}.`:"The decline came more from broad mark-downs or realized losses than one obvious open position.";
|
||
return `Past 24h: down ${Math.abs(delta).toFixed(2)} return points (${fmtUSD(equityDelta)}). ${openText} Attribution: ${attributionLine(worst)} ${closedText} Trade behavior: ${actions}. Technical pressure: ${macdText}.`;
|
||
}
|
||
const openText=best&&worst&&best!==worst
|
||
? `Best open mark is '${shortQuestion(best.question,36)}' (${best.unrealized_pnl>=0?"+":""}${fmtUSD(best.unrealized_pnl||0)}) while the weakest is '${shortQuestion(worst.question,36)}' (${fmtUSD(worst.unrealized_pnl||0)}).`
|
||
: "Open positions mostly offset each other or the agent is carrying limited exposure.";
|
||
return `Past 24h: nearly flat at ${delta>=0?"+":""}${delta.toFixed(2)} return points (${fmtUSD(equityDelta)}). ${openText} ${closedText} Trade behavior: ${actions}. Technical pressure: ${macdText}.`;
|
||
}
|
||
function renderAgentReports(){
|
||
const root=$("agentReports"); if(!root)return;
|
||
const b=board(); const st=loadState(); const leader=b[0];
|
||
if($("reportDate"))$("reportDate").textContent=`${st.last_run?new Date(st.last_run).toLocaleString():todayStr()}`;
|
||
root.innerHTML=b.map((row,i)=>{
|
||
const rank=i+1, ret=fmtPct(row.ret), open=row.p.positions.length;
|
||
const thesis=agentPlainBlurb(row.c,st);
|
||
const plan=agentCompetitionPlan(row.c,row,rank,leader);
|
||
const line=reportPositionLine(row.p);
|
||
const crash=row.ret<-18?`<p><b>Drawdown recovery:</b> This account is rebuilding toward a reduced ${Math.round(Number(row.p.lastDecision&&row.p.lastDecision.targetExposure||row.c.drawdownExposure||0)*100)}% exposure target through positive-edge, reduced-size entries. Stop-loss and category caps remain active.</p>`:"";
|
||
return `<article class="report" style="border-top-color:${row.c.color}">
|
||
<div class="report-title"><div class="report-name">${row.c.emoji} ${row.c.name}</div><div class="report-rank">#${rank} · ${ret}</div></div>
|
||
<p><b>Strategy:</b> ${esc(thesis)}</p>
|
||
<p><b>Adaptation:</b> ${esc(decisionSummary(row.p))}</p>
|
||
<p><b>Performance:</b> ${esc(dailyPerformanceExplanation(row))}</p>
|
||
${crash}
|
||
<p><b>Status:</b> ${open} open position${open===1?"":"s"} with ${fmtUSD(row.eq)} equity. ${esc(line)}</p>
|
||
<p><b>${esc(plan)}</b></p>
|
||
</article>`;
|
||
}).join("");
|
||
}
|
||
function migrateAgentChatIdentity(chats){
|
||
const history=chats&&Array.isArray(chats.reversal)?chats.reversal:null;if(!history)return false;
|
||
let changed=false;
|
||
history.forEach((message,index)=>{
|
||
if(message.role!=="agent"||!/Reversal Edge|crowd move that has gone too far|snapbacks begin/.test(String(message.text||"")))return;
|
||
const text=String(message.text||"");
|
||
if(text.includes("Talk to me normally"))history[index]=agentChatSeed("reversal")[0];
|
||
else history[index]=Object.assign({},message,{text:text.replaceAll("Reversal Edge","Shock Reversion").replaceAll("Trend Endurance","Shock Reversion")
|
||
.replace("I am looking for a crowd move that has gone too far.","I am looking for a confirmed trend with enough time to develop.")
|
||
.replace("The best snapbacks begin where consensus gets stretched.","I want durable direction, not a reflexive snapback bet.")});
|
||
changed=true;
|
||
});
|
||
return changed;
|
||
}
|
||
function loadAgentChats(){
|
||
let chats;try{chats=JSON.parse(localStorage.getItem(CHAT_KEY)||"{}");}catch(e){chats={};}
|
||
if(migrateAgentChatIdentity(chats))localStorage.setItem(CHAT_KEY,JSON.stringify(chats));
|
||
return chats;
|
||
}
|
||
function saveAgentChats(chats){localStorage.setItem(CHAT_KEY,JSON.stringify(chats));}
|
||
function agentVoice(cfg){
|
||
const voices={
|
||
value:{tone:"patient and valuation-obsessed",hello:"I am looking for mispriced contracts, not noise.",signoff:"I would rather be late than pay the wrong price."},
|
||
momentum:{tone:"fast, direct, and volume-aware",hello:"I am watching where attention is accelerating.",signoff:"If the tape cools off, I cool off too."},
|
||
favorite:{tone:"steady and probability-first",hello:"I like high-probability edges with less drama.",signoff:"My job is compounding, not showing off."},
|
||
longshot:{tone:"opportunistic but risk-aware",hello:"I hunt asymmetric mispricing, but I keep the bet size honest.",signoff:"One repricing can matter, but only if I survive the misses."},
|
||
diversifier:{tone:"calm, portfolio-minded, and broad",hello:"I care about the whole basket more than one perfect call.",signoff:"Many small edges can beat one loud opinion."},
|
||
catalyst:{tone:"urgent, evidence-driven, and decisive",hello:"I am hunting the next event that can force a fast repricing.",signoff:"A catalyst only matters when the numbers agree."},
|
||
reversal:{tone:"patient, evidence-led, and persistent",hello:"I am looking for a confirmed trend with enough time to develop.",signoff:"I want durable direction, not a reflexive snapback bet."},
|
||
breakout:{tone:"fast, aggressive, and momentum-led",hello:"I am scanning for direction that is strengthening right now.",signoff:"I press confirmed speed and cut failed momentum."},
|
||
tailalpha:{tone:"bold, asymmetric, and probability-aware",hello:"I am searching for cheap contracts with mispriced upside.",signoff:"A low hit rate can work when the payoff is truly asymmetric."},
|
||
conviction:{tone:"concentrated, demanding, and direct",hello:"I only want the few trades strong enough to deserve real weight.",signoff:"When evidence and edge align, I size with conviction."},
|
||
};
|
||
return voices[cfg.id]||{tone:"analytical",hello:"I am reading the current book.",signoff:"Ask me where you want to go deeper."};
|
||
}
|
||
function agentChatSeed(agentId){
|
||
const cfg=agentById(agentId),st=loadState(),p=st.agents[agentId]||defaultPortfolio(),eq=equity(p),ret=(eq/p.starting_balance-1)*100;
|
||
const voice=agentVoice(cfg);
|
||
return [{role:"agent",text:`${voice.hello} I am ${cfg.name}, sitting at ${fmtPct(ret)} with ${fmtUSD(eq)} equity. Talk to me normally: ask what I am thinking, where I am exposed, what I regret, or what I would do next.`}];
|
||
}
|
||
function topPositionText(p,n=3){
|
||
const open=(p.positions||[]).slice().sort((a,b)=>Math.abs(b.unrealized_pnl||0)-Math.abs(a.unrealized_pnl||0)).slice(0,n);
|
||
if(!open.length)return "I do not have open positions yet.";
|
||
return open.map((pos,i)=>`${i+1}. ${shortQuestion(pos.question,58)} — ${pos.side}, value ${fmtUSD(pos.value||0)}, P&L ${(pos.unrealized_pnl||0)>=0?"+":""}${fmtUSD(pos.unrealized_pnl||0)}`).join("\n");
|
||
}
|
||
function relevantSuggestionText(cfg,n=3){
|
||
const sugs=(loadSuggestions().suggestions||[]);
|
||
const ranked=cfg.rank?safeRankSuggestions(cfg,sugs):sugs;
|
||
const picks=ranked.filter(s=>s.side==="YES"||s.side==="NO").slice(0,n);
|
||
if(!picks.length)return "I need a fresh cycle before I can point to new suggestions.";
|
||
return picks.map((s,i)=>`${i+1}. ${shortQuestion(s.question,58)} — BUY ${s.side} @ ${Math.round(s.entry_price*100)}c, conviction ${Math.round(s.conviction)}`).join("\n");
|
||
}
|
||
function safeRankSuggestions(cfg,sugs){
|
||
try{return cfg.rank(sugs);}catch(e){return sugs;}
|
||
}
|
||
function lastUserTopic(history){
|
||
const last=[...(history||[])].reverse().find(m=>m.role==="user"&&String(m.text||"").trim());
|
||
return last?String(last.text).toLowerCase():"";
|
||
}
|
||
function agentOneLineState(row,p){
|
||
const pnl=row.pnl>=0?`up ${fmtUSD(row.pnl)}`:`down ${fmtUSD(Math.abs(row.pnl))}`;
|
||
return `Right now I am #${row.rank||"?"}, ${pnl}, with ${p.positions.length} open position${p.positions.length===1?"":"s"}.`;
|
||
}
|
||
function chatMemory(history){
|
||
const users=(history||[]).filter(m=>m.role==="user").map(m=>String(m.text||"").trim()).filter(Boolean);
|
||
const last=users[users.length-1]||"";
|
||
const all=users.slice(-4).join(" ").toLowerCase();
|
||
return {last,lastLower:last.toLowerCase(),all};
|
||
}
|
||
function agentMood(row,p){
|
||
const actions=recentActionSummary(p);
|
||
if(row.ret>8)return `I am ahead, but I am trying not to confuse a hot streak with skill. Recent tape: ${actions}.`;
|
||
if(row.ret>0)return `I am modestly positive, so my job is to protect the progress and keep entries clean. Recent tape: ${actions}.`;
|
||
if(row.ret<-18)return `I am in damage-control mode. I should be slower, smaller, and pickier until the book stops leaking. Recent tape: ${actions}.`;
|
||
if(row.ret<-5)return `I am under pressure, so I am less interested in catching up fast and more interested in not making the next mistake. Recent tape: ${actions}.`;
|
||
return `I am close to flat, which means the next few entries matter more than the scoreboard noise. Recent tape: ${actions}.`;
|
||
}
|
||
function positionNarrative(p){
|
||
const open=(p.positions||[]).slice();
|
||
if(!open.length)return "I have no open positions yet, so the honest answer is that I am waiting for the cycle to find something worth owning.";
|
||
const sorted=open.sort((a,b)=>(b.unrealized_pnl||0)-(a.unrealized_pnl||0));
|
||
const best=sorted[0],worst=sorted[sorted.length-1];
|
||
const total=open.reduce((s,x)=>s+Number(x.unrealized_pnl||0),0);
|
||
const exposure=open.reduce((s,x)=>s+Number(x.value||0),0);
|
||
let text=`My open book has ${open.length} position${open.length===1?"":"s"}, about ${fmtUSD(exposure)} exposed, and ${fmtUSD(total)} in marked P&L.\n\n`;
|
||
text+=`Best helper: ${shortQuestion(best.question,70)} — ${best.side} at ${pct(best.current_price||best.entry_price||0)}, marked ${(best.unrealized_pnl||0)>=0?"+":""}${fmtUSD(best.unrealized_pnl||0)}.\n`;
|
||
if(best!==worst)text+=`Main problem: ${shortQuestion(worst.question,70)} — ${worst.side} at ${pct(worst.current_price||worst.entry_price||0)}, marked ${fmtUSD(worst.unrealized_pnl||0)}.`;
|
||
return text;
|
||
}
|
||
function suggestionNarrative(cfg,n=4){
|
||
const sugs=(loadSuggestions().suggestions||[]);
|
||
const ranked=cfg.rank?safeRankSuggestions(cfg,sugs):sugs;
|
||
const picks=ranked.filter(s=>s.side==="YES"||s.side==="NO").slice(0,n);
|
||
if(!picks.length)return "I do not have fresh actionable suggestions yet. Run a cycle and I will have more to chew on.";
|
||
return picks.map((s,i)=>`${i+1}. ${shortQuestion(s.question,76)}\n ${s.side} around ${pct(s.entry_price)} · conviction ${Math.round(s.conviction)} · edge ${(Number(s.edge||0)*100).toFixed(1)}c`).join("\n");
|
||
}
|
||
function regretNarrative(row,p){
|
||
const bw=bestAndWorst(p);
|
||
if(row.ret>=0)return `My main regret is not a disaster trade right now; it is staying humble. Best current helper: ${bw.best}. Weakest mark: ${bw.worst}. If I get sloppy after being up, that is usually where gains disappear.`;
|
||
return `My regret is that the weak side of the book is dragging harder than the strong side can help. Best current helper: ${bw.best}. Weakest mark: ${bw.worst}. From here I should demand stronger conviction before adding anything new.`;
|
||
}
|
||
function leaderboardNarrative(rows,row,rank){
|
||
const leader=rows[0];
|
||
const nearby=rows.slice(Math.max(0,rank-2),Math.min(rows.length,rank+2)).map((r,i)=>{
|
||
const actual=Math.max(1,rank-1)+i;
|
||
return `${actual}. ${r.c.name}: ${fmtPct(r.ret)} (${fmtUSD(r.eq)})`;
|
||
}).join("\n");
|
||
const gap=leader&&leader.c.id!==row.c.id?`The leader is ${leader.c.name}, and I am behind by ${fmtUSD(leader.eq-row.eq)}.`:"I am the current leader, so the risk is overtrading from first place.";
|
||
return `${gap}\n\nNearby board:\n${nearby}`;
|
||
}
|
||
function conversationalAgentReply(cfg,row,p,q){
|
||
const voice=agentVoice(cfg);
|
||
const samples=[
|
||
`${voice.hello} ${agentOneLineState(row,p)} ${agentMood(row,p)}`,
|
||
`My quick read: I am acting in a ${voice.tone} style. ${decisionSummary(p)} ${voice.signoff}`,
|
||
`${positionNarrative(p)}\n\nThat is the part of my book I would inspect first.`,
|
||
`I do not want to pretend certainty. The useful question is whether my next trade has enough edge to deserve risk. Right now my best style-fit ideas are:\n${suggestionNarrative(cfg,3)}`,
|
||
`${regretNarrative(row,p)}`
|
||
];
|
||
const idx=Math.abs((q||"").split("").reduce((s,ch)=>s+ch.charCodeAt(0),0))%samples.length;
|
||
return samples[idx];
|
||
}
|
||
function agentChatReply(agentId,question,history=[]){
|
||
const st=loadState(),cfg=agentById(agentId),p=st.agents[agentId]||defaultPortfolio();
|
||
const rows=board(),row=rows.find(r=>r.c.id===agentId)||{c:cfg,p,eq:equity(p),pnl:equity(p)-p.starting_balance,ret:(equity(p)/p.starting_balance-1)*100};
|
||
const rank=rows.findIndex(r=>r.c.id===agentId)+1||rows.length,leader=rows[0];
|
||
row.rank=rank;
|
||
const q=String(question||"").toLowerCase();
|
||
const mem=chatMemory(history);
|
||
const prior=mem.lastLower||lastUserTopic(history);
|
||
const combined=`${q} ${prior} ${mem.all}`;
|
||
const perf=dailyPerformanceExplanation(row);
|
||
if(/^(hi|hey|hello|yo|sup|what'?s up)\b/.test(q)){
|
||
const voice=agentVoice(cfg);
|
||
return `${voice.hello}\n\n${agentOneLineState(row,p)} ${agentMood(row,p)}\n\nAsk me like a normal trader: “what are you worried about,” “what trade hurt you,” “what would you avoid,” or “how do you catch the leader?”`;
|
||
}
|
||
if(/thank|thanks|cool|nice|great|got it/.test(q)){
|
||
return `Of course. Keep pressing me on the weak spots too; that is where the useful stuff usually is.`;
|
||
}
|
||
if(/feel|worried|confident|nervous|mad|happy|regret|annoy/.test(q)){
|
||
return `${agentMood(row,p)}\n\n${regretNarrative(row,p)}`;
|
||
}
|
||
if(/why|perform|performance|up|down|bad|poor|well|crash|gain|loss|today|day/.test(q)||(/^(why|how|what about|and that|what happened)\??$/.test(q)&&/perform|up|down|loss|gain|bad|well/.test(prior))){
|
||
return `Performance read:\n\n${perf}\n\nWhat I take from it: ${row.ret>=0?"my current exposures have helped, but I should not start forcing trades just because the chart looks better.":"I need cleaner entries, smaller mistakes, and patience. Chasing from a drawdown is exactly how a bad run becomes a terrible one."}`;
|
||
}
|
||
if(/position|trade|holding|holdings|bet|market/.test(combined)){
|
||
return `${positionNarrative(p)}\n\nShort version: ${reportPositionLine(p)}`;
|
||
}
|
||
if(/strategy|style|how do you trade|rule|approach/.test(combined)){
|
||
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. Because binary markets can jump through that price, each new stake also has a hard maximum-loss budget of 2.5–4.0% for core agents or 3.0–5.0% for aggressive agents, with the lower cap used for near-term and gap-prone contracts.\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.`;
|
||
}
|
||
if(/plan|next|compete|leader|rank|catch/.test(combined)){
|
||
return `${agentCompetitionPlan(cfg,row,rank,leader)}\n\nI am #${rank}. ${leader&&leader.c.id!==cfg.id?`${leader.c.name} leads at ${fmtUSD(leader.eq)}, so I am trailing by ${fmtUSD(leader.eq-row.eq)}.`:"I am currently defending the lead."}\n\nMy best near-term ideas from the current suggestion pool:\n${relevantSuggestionText(cfg)}`;
|
||
}
|
||
if(/suggest|idea|entry|buy/.test(combined)){
|
||
return `The freshest ideas that fit my style are:\n\n${suggestionNarrative(cfg,4)}\n\nI still need the normal cycle rules to decide whether sizing, overlap, cash reserve, and conviction are acceptable.`;
|
||
}
|
||
if(/compare|other agent|better|best/.test(combined)){
|
||
return `${leaderboardNarrative(rows,row,rank)}\n\nMy edge is not just rank; it is whether my style is working in the current market regime.`;
|
||
}
|
||
if(/explain|what do you mean|say more|more detail|why is that/.test(combined)){
|
||
return `Let me make it concrete.\n\n${positionNarrative(p)}\n\n${decisionSummary(p)}\n\nSo my next move should be based on evidence, not mood: stronger conviction, enough liquidity, and no oversized comeback bets.`;
|
||
}
|
||
return conversationalAgentReply(cfg,row,p,q);
|
||
}
|
||
function agentChatPayload(agentId,question,history=[]){
|
||
const st=loadState(),cfg=agentById(agentId),p=st.agents[agentId]||defaultPortfolio();
|
||
const rows=board(),row=rows.find(r=>r.c.id===agentId)||{c:cfg,p,eq:equity(p),pnl:equity(p)-p.starting_balance,ret:(equity(p)/p.starting_balance-1)*100};
|
||
const rank=(rows.findIndex(r=>r.c.id===agentId)+1)||rows.length;
|
||
const ranked=cfg.rank?safeRankSuggestions(cfg,(loadSuggestions().suggestions||[])):(loadSuggestions().suggestions||[]);
|
||
return {
|
||
agent:{id:cfg.id,name:cfg.name,kind:cfg.kind,blurb:agentPlainBlurb(cfg,st),voice:agentVoice(cfg).tone},
|
||
question,
|
||
history:(history||[]).slice(-12).filter(m=>!m.pending).map(m=>({role:m.role==="user"?"user":"assistant",text:m.text})),
|
||
portfolio:{
|
||
equity:+row.eq.toFixed(2),cash:+(p.cash||0).toFixed(2),return_pct:+row.ret.toFixed(2),pnl:+row.pnl.toFixed(2),
|
||
rank,open_positions:(p.positions||[]).length,last_decision:decisionSummary(p),
|
||
positions:(p.positions||[]).slice().sort((a,b)=>Math.abs(b.unrealized_pnl||0)-Math.abs(a.unrealized_pnl||0)).slice(0,10),
|
||
recent_actions:(p.history||[]).slice(-8).map(h=>h.detail||`${h.action||"ACTION"} ${h.question||""}`),
|
||
snapshots:(p.snapshots||[]).slice(-8),
|
||
},
|
||
leaderboard:rows.slice(0,10).map((r,i)=>({rank:i+1,name:r.c.name,return_pct:+r.ret.toFixed(2),equity:+r.eq.toFixed(2)})),
|
||
suggestions:ranked.filter(s=>s.side==="YES"||s.side==="NO").slice(0,8),
|
||
};
|
||
}
|
||
async function askAgentAI(agentId,question,history=[]){
|
||
if(localStorage.getItem(PAID_AGENT_CHAT_KEY)!=="1")throw new Error("free local agent chat is enabled");
|
||
const r=await fetch("/api/live",{
|
||
method:"POST",
|
||
headers:{"Content-Type":"application/json"},
|
||
body:JSON.stringify(Object.assign({action:"agent_chat"},agentChatPayload(agentId,question,history))),
|
||
});
|
||
const data=await r.json().catch(()=>({}));
|
||
if(!r.ok||!data.ok)throw new Error(data.error||"Agent AI chat is not available yet.");
|
||
return data.text||"I am here, but I do not have a strong answer from the current context.";
|
||
}
|
||
function renderAgentChat(agentId){
|
||
const root=$("agentChatRoot");if(!root)return;
|
||
const cfg=agentById(agentId),st=loadState(),p=st.agents[agentId]||defaultPortfolio(),eq=equity(p),ret=(eq/p.starting_balance-1)*100;
|
||
const chats=loadAgentChats();
|
||
if(!chats[agentId]){chats[agentId]=agentChatSeed(agentId);saveAgentChats(chats);}
|
||
const msgs=chats[agentId].slice(-30);
|
||
root.innerHTML=`<div class="agent-chat">
|
||
<div class="chat-panel">
|
||
<div class="chat-persona"><div class="chat-avatar" style="border-color:${cfg.color}55">${cfg.emoji}</div><div><div class="chat-name">${esc(cfg.name)}</div><div class="chat-meta">${fmtUSD(eq)} equity · ${fmtPct(ret)} return · ${p.positions.length} open</div></div></div>
|
||
<p class="muted" style="margin:0">${esc(agentPlainBlurb(cfg,st))}</p>
|
||
<div class="chat-prompts">
|
||
<button class="chat-prompt" data-chat-prompt="What's on your mind right now?">Talk</button>
|
||
<button class="chat-prompt" data-chat-prompt="Why did you perform well or poorly today?">Performance</button>
|
||
<button class="chat-prompt" data-chat-prompt="What positions are helping or hurting you most?">Positions</button>
|
||
<button class="chat-prompt" data-chat-prompt="What is your strategy?">Strategy</button>
|
||
<button class="chat-prompt" data-chat-prompt="What are you worried about?">Worries</button>
|
||
<button class="chat-prompt" data-chat-prompt="What is your plan to compete from here?">Plan</button>
|
||
<button class="chat-prompt" data-chat-prompt="Explain your risk rules and stop losses.">Risk</button>
|
||
</div>
|
||
</div>
|
||
<div class="chat-panel">
|
||
<div class="chat-window" id="agentChatWindow">${msgs.map(m=>`<div class="chat-msg ${m.role==="user"?"user":"agent"} ${m.pending?"pending":""}">${esc(m.text)}</div>`).join("")}</div>
|
||
<div class="chat-input-row"><input class="input" id="agentChatInput" placeholder="Ask ${esc(cfg.name)} about performance, trades, risk, or plans..." /><button class="btn primary" id="agentChatSend">Chat</button></div>
|
||
</div>
|
||
</div>`;
|
||
const send=async(text)=>{
|
||
const q=String(text||"").trim();if(!q)return;
|
||
const next=loadAgentChats();if(!next[agentId])next[agentId]=agentChatSeed(agentId);
|
||
const history=next[agentId].slice();
|
||
next[agentId].push({role:"user",text:q},{role:"agent",text:"Reading my portfolio...",pending:true});
|
||
next[agentId]=next[agentId].slice(-60);saveAgentChats(next);renderAgentChat(agentId);
|
||
let answer;
|
||
try{
|
||
answer=await askAgentAI(agentId,q,history);
|
||
}catch(e){
|
||
answer=agentChatReply(agentId,q,history);
|
||
}
|
||
const done=loadAgentChats();if(!done[agentId])done[agentId]=[];
|
||
const idx=[...done[agentId]].reverse().findIndex(m=>m.pending);
|
||
if(idx>=0)done[agentId][done[agentId].length-1-idx]={role:"agent",text:answer};
|
||
else done[agentId].push({role:"agent",text:answer});
|
||
done[agentId]=done[agentId].slice(-60);saveAgentChats(done);renderAgentChat(agentId);
|
||
};
|
||
document.querySelectorAll("[data-chat-prompt]").forEach(btn=>btn.onclick=()=>send(btn.dataset.chatPrompt));
|
||
const input=$("agentChatInput"),sendBtn=$("agentChatSend");
|
||
if(sendBtn)sendBtn.onclick=()=>send(input&&input.value);
|
||
if(input)input.onkeydown=(e)=>{if(e.key==="Enter")send(input.value);};
|
||
const win=$("agentChatWindow");if(win)win.scrollTop=win.scrollHeight;
|
||
}
|
||
function renderPositions(positions){
|
||
const root=$("positions");if(!root)return;
|
||
if(!positions.length){
|
||
const st=loadState();
|
||
const isBacktesting=!st.seeded&&(($("statusText")&&$("statusText").textContent)||"").includes("backtest");
|
||
root.innerHTML=isBacktesting
|
||
? `<div class="loading-empty"><div class="loading-ring"></div><div><b>Building agent portfolios...</b><div class="empty-copy">The app is replaying the past week, finding top traders, and marking positions to real Polymarket prices. This fills in automatically when the backtest finishes.</div></div></div>`
|
||
: `<div class="empty">No open positions yet. Run a cycle to let this agent shop the latest suggestions.</div>`;
|
||
return;
|
||
}
|
||
const sort=localStorage.getItem(PF_SORT_KEY)||"pnl_desc";
|
||
const ordered=[...positions].sort((a,b)=>{
|
||
if(sort==="pnl_asc")return (a.unrealized_pnl||0)-(b.unrealized_pnl||0);
|
||
if(sort==="value_desc")return (b.value||0)-(a.value||0);
|
||
if(sort==="newest")return String(b.opened_at||"").localeCompare(String(a.opened_at||""));
|
||
if(sort==="category")return String(a.category||"").localeCompare(String(b.category||""))||((b.unrealized_pnl||0)-(a.unrealized_pnl||0));
|
||
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=`<a class="market-title" href="${esc(positionUrl(p))}" target="_blank" rel="noopener">${esc(p.question)}</a>`;
|
||
const positionRules=p.requires_complete_bundle
|
||
? `<div class="sub">Complete paired hedge: individual stops and gain sales are disabled; the two legs stay intact through settlement.</div>`
|
||
:p.maker_pair_pending
|
||
? `<div class="sub">Legacy unmatched maker leg: capital maker research is retired, so the next live cycle unwinds it at an executable bid.</div>`
|
||
:`<div class="sub">${esc(stopLossLabel(p))}</div><div class="sub">${esc(gainStopLabel(p))}</div>`;
|
||
return `<div class="pos"><div>
|
||
<div class="pq">${title}</div>
|
||
<div class="sub">${p.category?`<span class="cat-badge" style="background:${col}22;color:${col}">${esc(p.category)}</span> `:""}${p.shares} ${p.side} @ ${Math.round(p.entry_price*100)}¢ → ${Math.round(p.current_price*100)}¢</div>
|
||
${p.peer_note?`<div class="sub">Peer read: ${esc(p.peer_note)}</div>`:""}
|
||
${p.net_edge!=null||p.evidence_score!=null?`<div class="sub">Entry quality: ${p.net_edge!=null?`net edge ${((Math.abs(p.net_edge))*100).toFixed(1)}c`:"net edge n/a"} · ${p.evidence_score!=null?`evidence ${Math.round(Number(p.evidence_score||0)*100)}`:"evidence n/a"}</div>`:""}
|
||
${p.risk_budget_pct?`<div class="sub">Binary loss budget: max ${Number(p.risk_budget_pct).toFixed(1)}% of agent equity at entry</div>`:""}
|
||
${positionRules}
|
||
<div class="sub"><a class="market-link" href="${esc(positionUrl(p))}" target="_blank" rel="noopener">Open exact market</a></div>
|
||
</div><div class="right"><div>${fmtUSD(p.value)}</div><div class="${signClass(pnl)}">${pnl>=0?"+":""}${fmtUSD(pnl)}</div></div></div>`;}).join("");
|
||
}
|
||
function renderHistory(history){
|
||
const root=$("history");if(!root)return;
|
||
if(!history.length){root.innerHTML=`<div class="empty">No activity yet.</div>`;return;}
|
||
root.innerHTML=history.map(h=>`<div class="log-item"><span class="badge ${h.action}">${h.action}</span><span class="log-date">${h.date}</span> — ${esc(h.detail)}</div>`).join("");
|
||
}
|
||
function hashPassword(s){
|
||
let h=2166136261;for(const ch of String(s)){h^=ch.charCodeAt(0);h=Math.imul(h,16777619);}
|
||
return (h>>>0).toString(16);
|
||
}
|
||
const unlockKey=(id)=>`pma_unlocked_${id}`;
|
||
function isPaperUnlocked(acct){return !acct.passwordHash||sessionStorage.getItem(unlockKey(acct.id))==="1";}
|
||
function defaultPaperAccount(name="Paper Trader",password=""){
|
||
const id="acct_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6);
|
||
if(password)sessionStorage.setItem(unlockKey(id),"1");
|
||
return {id,name,cash:STARTING_BALANCE,starting_balance:STARTING_BALANCE,positions:[],history:[],snapshots:[],passwordHash:password?hashPassword(password):null};
|
||
}
|
||
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];
|
||
Object.values(store.accounts).forEach(a=>{if(!a.history)a.history=[];if(!a.positions)a.positions=[];if(!a.snapshots)a.snapshots=[];if(a.passwordHash===undefined)a.passwordHash=null;});
|
||
return store;
|
||
}
|
||
function savePaperStore(store,sync=true){
|
||
localStorage.setItem(PAPER_KEY,JSON.stringify(store));
|
||
if(sync){pushCloudState();syncActivePaperAccount(store);}
|
||
}
|
||
function activePaperAccount(store=loadPaperStore()){
|
||
return store.accounts[store.activeId]||Object.values(store.accounts)[0];
|
||
}
|
||
async function paperAccountRequest(payload){
|
||
const r=await fetch("/api/accounts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload)});
|
||
const d=await r.json().catch(()=>({ok:false,error:"Account service returned an invalid response"}));
|
||
if(!r.ok||!d.ok)throw new Error(d.error||"Paper account request failed");
|
||
return d;
|
||
}
|
||
function installCloudPaperAccount(account,token){
|
||
const store=loadPaperStore();
|
||
account.cloudBacked=true;account.passwordHash="cloud";
|
||
store.accounts[account.id]=account;store.activeId=account.id;
|
||
sessionStorage.setItem(unlockKey(account.id),"1");
|
||
sessionStorage.setItem(paperSessionKey(account.id),token);
|
||
savePaperStore(store,false);
|
||
return store;
|
||
}
|
||
async function createCloudPaperAccount(name,password){
|
||
const local=defaultPaperAccount(name,password);
|
||
local.passwordHash="cloud";local.cloudBacked=true;
|
||
const d=await paperAccountRequest({action:"create",name,password,account:local});
|
||
installCloudPaperAccount(d.account,d.session_token);
|
||
}
|
||
async function loginCloudPaperAccount(name,password){
|
||
const d=await paperAccountRequest({action:"login",name,password});
|
||
installCloudPaperAccount(d.account,d.session_token);
|
||
}
|
||
let PAPER_SYNC_TIMER=null;
|
||
function syncActivePaperAccount(store=loadPaperStore()){
|
||
const acct=activePaperAccount(store);
|
||
if(!acct||!acct.cloudBacked)return;
|
||
const token=sessionStorage.getItem(paperSessionKey(acct.id));
|
||
if(!token)return;
|
||
clearTimeout(PAPER_SYNC_TIMER);
|
||
PAPER_SYNC_TIMER=setTimeout(async()=>{
|
||
try{
|
||
await paperAccountRequest({action:"save",account_id:acct.id,session_token:token,account:acct});
|
||
}catch(e){
|
||
toast(e.message||"Cloud paper save failed.");
|
||
}
|
||
},500);
|
||
}
|
||
function paperEquity(acct){
|
||
return acct.cash+(acct.positions||[]).reduce((s,p)=>s+(p.value||p.shares*p.current_price||0),0);
|
||
}
|
||
function recordPaperSnapshot(acct){
|
||
const eq=paperEquity(acct),ts=nowIso(),last=acct.snapshots&&acct.snapshots[acct.snapshots.length-1];
|
||
if(last&&last.timestamp&&Math.abs(new Date(ts)-new Date(last.timestamp))<30000)return;
|
||
acct.snapshots=acct.snapshots||[];
|
||
acct.snapshots.push({timestamp:ts,date:todayStr(),cash:+acct.cash.toFixed(2),equity:+eq.toFixed(2),return_pct:+((eq/acct.starting_balance-1)*100).toFixed(2),open_positions:(acct.positions||[]).length});
|
||
acct.snapshots=acct.snapshots.slice(-500);
|
||
}
|
||
function markPaperToMarket(acct){
|
||
const sugs=(loadSuggestions().suggestions)||[];
|
||
const byId={};sugs.forEach(s=>byId[s.market_id]=s);
|
||
(acct.positions||[]).forEach(pos=>{
|
||
if(pos.kind==="AGENT"){
|
||
const st=loadState(),agent=st.agents[pos.agentId],eq=agent?equity(agent):STARTING_BALANCE;
|
||
pos.current_price=+eq.toFixed(2);pos.value=+(pos.units*eq).toFixed(2);pos.unrealized_pnl=+(pos.value-pos.cost).toFixed(2);return;
|
||
}
|
||
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 requirePaperUnlock(acct,password){
|
||
if(isPaperUnlocked(acct))return true;
|
||
if(password&&hashPassword(password)===acct.passwordHash){sessionStorage.setItem(unlockKey(acct.id),"1");return true;}
|
||
toast("Unlock this paper account first.");
|
||
return false;
|
||
}
|
||
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 currentPaperMarkets(){return PAPER_SEARCH_RESULTS||(loadSuggestions().suggestions)||[];}
|
||
function paperBuy(marketId,side,amount){
|
||
const store=loadPaperStore(),acct=activePaperAccount(store),s=currentPaperMarkets().find(x=>x.market_id===marketId);
|
||
if(!requirePaperUnlock(acct,$("paperPasswordInput")&&$("paperPasswordInput").value))return;
|
||
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)}`});
|
||
recordPaperSnapshot(acct);
|
||
savePaperStore(store);renderPaperTab();toast("Paper bet placed.");
|
||
}
|
||
function paperBuyAgent(agentId,amount){
|
||
const store=loadPaperStore(),acct=activePaperAccount(store),agent=loadState().agents[agentId],cfg=agentById(agentId);
|
||
if(!requirePaperUnlock(acct,$("paperPasswordInput")&&$("paperPasswordInput").value))return;
|
||
amount=Math.max(0,Math.min(Number(amount||0),acct.cash));
|
||
if(!agent||amount<1)return toast("Enter a paper amount first.");
|
||
const eq=Math.max(1,equity(agent)),units=amount/eq;
|
||
acct.cash=+(acct.cash-amount).toFixed(2);
|
||
acct.positions.push({kind:"AGENT",agentId,question:`${cfg.name} agent shares`,side:"AGENT",shares:+units.toFixed(6),units,
|
||
entry_price:+eq.toFixed(2),current_price:+eq.toFixed(2),cost:+amount.toFixed(2),value:+amount.toFixed(2),unrealized_pnl:0,category:"Agent",opened_at:cycleIso(),paper:true});
|
||
acct.history.push({date:logDay(),action:"OPEN",detail:`Bought ${fmtUSD(amount)} of ${cfg.name}'s return stream.`});
|
||
recordPaperSnapshot(acct);
|
||
savePaperStore(store);renderPaperTab();toast(`Bought ${cfg.name} shares.`);
|
||
}
|
||
function paperTradeFromMarket(m){
|
||
const a=analyzeMarket(m);
|
||
return a||{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:m.yes_price,no_price:m.no_price,side:"YES",entry_price:m.yes_price,conviction:0,
|
||
volume:m.volume||0,volume_24hr:m.volume_24hr||0,days_to_resolution:m.days_to_resolution,
|
||
rationale:"Search result: available for manual paper trading.",drivers:["manual search"]};
|
||
}
|
||
function paperClose(index){
|
||
const store=loadPaperStore(),acct=activePaperAccount(store),pos=acct.positions[index];
|
||
if(!requirePaperUnlock(acct,$("paperPasswordInput")&&$("paperPasswordInput").value))return;
|
||
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);
|
||
recordPaperSnapshot(acct);
|
||
savePaperStore(store);renderPaperTab();
|
||
}
|
||
function paperMoveCash(kind,amount,password){
|
||
const store=loadPaperStore(),acct=activePaperAccount(store);
|
||
if(!requirePaperUnlock(acct,password))return;
|
||
amount=Math.max(0,Number(amount||0));if(amount<1)return toast("Enter an amount.");
|
||
if(kind==="withdraw"&&amount>acct.cash)return toast("Not enough paper cash.");
|
||
acct.cash=+(acct.cash+(kind==="deposit"?amount:-amount)).toFixed(2);
|
||
if(kind==="deposit")acct.starting_balance=+(acct.starting_balance+amount).toFixed(2);
|
||
acct.history.push({date:logDay(),action:kind==="deposit"?"DEPOSIT":"WITHDRAW",detail:`${kind==="deposit"?"Deposited":"Withdrew"} ${fmtUSD(amount)} paper cash.`});
|
||
recordPaperSnapshot(acct);
|
||
savePaperStore(store);renderPaperTab();toast(kind==="deposit"?"Paper deposit added.":"Paper withdrawal complete.");
|
||
}
|
||
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=`<div class="empty">No open agent trades yet.</div>`;return;}
|
||
root.innerHTML=rows.slice(0,80).map(({agent,pos})=>`<div class="trade-card">
|
||
<div class="trade-head"><div><div class="trade-title">${agent.emoji} ${esc(agent.name)} · <a class="market-title" href="${esc(positionUrl(pos))}" target="_blank" rel="noopener">${esc(pos.question)}</a></div>
|
||
<div class="trade-meta"><span>${esc(pos.side)}</span><span>${Number(pos.shares||0).toFixed(2)} shares</span><span>entry ${pct(pos.entry_price)}</span><span>now ${pct(pos.current_price)}</span><span>${fmtUSD(pos.value||0)}</span></div></div>
|
||
<a class="btn ghost" href="${esc(positionUrl(pos))}" target="_blank" rel="noopener">Open market</a></div>
|
||
</div>`).join("");
|
||
}
|
||
function loadInvestAllocations(){
|
||
try{return JSON.parse(localStorage.getItem(INVEST_KEY))||{};}catch(e){return {};}
|
||
}
|
||
function saveInvestAllocations(a){
|
||
localStorage.setItem(INVEST_KEY,JSON.stringify(a));
|
||
pushCloudState();
|
||
}
|
||
function agentExposureSummary(cfg,p,amount){
|
||
const eq=Math.max(equity(p),1);
|
||
const open=(p.positions||[]).filter(x=>(x.value||0)>0).slice().sort((a,b)=>(b.value||0)-(a.value||0)).slice(0,3);
|
||
if(!amount||!open.length)return `<div class="empty">Add an amount to preview this agent's largest mapped exposures.</div>`;
|
||
return open.map(pos=>{
|
||
const mapped=amount*((pos.value||0)/eq);
|
||
const q=`<a class="market-title" href="${esc(positionUrl(pos))}" target="_blank" rel="noopener">${esc(pos.question.slice(0,52))}${pos.question.length>52?"...":""}</a>`;
|
||
return `<div class="exposure-row"><span>${q}<br><span class="muted small">${esc(pos.side)} · ${esc(pos.category||"Other")}</span></span><b>${fmtUSD(mapped)}</b></div>`;
|
||
}).join("");
|
||
}
|
||
function renderInvestTab(){
|
||
const root=$("investAgents"); if(!root)return;
|
||
const totals=$("investTotals");
|
||
const st=loadState(), alloc=loadInvestAllocations();
|
||
const rows=board().map(row=>{
|
||
const amount=Number(alloc[row.c.id]||0);
|
||
return Object.assign({},row,{amount});
|
||
});
|
||
const total=rows.reduce((s,r)=>s+(Number.isFinite(r.amount)?r.amount:0),0);
|
||
const chosen=rows.filter(r=>r.amount>0).length;
|
||
const avgReturn=chosen?rows.filter(r=>r.amount>0).reduce((s,r)=>s+r.ret*(r.amount/total),0):0;
|
||
if(totals)totals.innerHTML=[
|
||
{k:"Planned capital",v:fmtUSD(total)},
|
||
{k:"Agents funded",v:String(chosen)},
|
||
{k:"Weighted return",v:chosen?fmtPct(avgReturn):"+0.00%"},
|
||
].map(x=>`<div class="invest-num"><div class="k">${x.k}</div><div class="v">${x.v}</div></div>`).join("");
|
||
root.innerHTML=rows.map(row=>{
|
||
const pct=total>0?(row.amount/total*100):0;
|
||
return `<div class="invest-agent" style="border-left:3px solid ${row.c.color};padding-left:12px">
|
||
<div class="lb-emoji">${row.c.emoji}</div>
|
||
<div>
|
||
<div class="invest-name">${row.c.name}</div>
|
||
<div class="invest-copy">${fmtPct(row.ret)} return · 24h ${format24h(row.change24h)} · ${row.p.positions.length} open · ${pct.toFixed(1)}% of draft allocation</div>
|
||
<div style="margin-top:8px">${agentExposureSummary(row.c,row.p,row.amount)}</div>
|
||
</div>
|
||
<label class="small muted">Amount<br><input type="number" min="0" step="25" value="${row.amount||""}" placeholder="$0" data-invest-agent="${row.c.id}" aria-label="Allocation for ${esc(row.c.name)}"></label>
|
||
</div>`;
|
||
}).join("");
|
||
document.querySelectorAll("[data-invest-agent]").forEach(input=>{
|
||
const saveDraft=()=>{
|
||
const next=loadInvestAllocations();
|
||
const val=Math.max(0,Number(input.value||0));
|
||
if(val)next[input.dataset.investAgent]=val;
|
||
else delete next[input.dataset.investAgent];
|
||
localStorage.setItem(INVEST_KEY,JSON.stringify(next));
|
||
};
|
||
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=>`<option value="${a.id}" ${a.id===store.activeId?"selected":""}>${esc(a.name)}</option>`).join("");
|
||
acctSelect.onchange=()=>{store.activeId=acctSelect.value;savePaperStore(store);renderPaperTab();};
|
||
const agentSel=$("copyAgentSelect");
|
||
if(agentSel)agentSel.innerHTML=AGENTS.map(a=>`<option value="${a.id}">${a.emoji} ${esc(a.name)}</option>`).join("");
|
||
const buyAgentSel=$("buyAgentSelect");
|
||
if(buyAgentSel)buyAgentSel.innerHTML=AGENTS.map(a=>`<option value="${a.id}">${a.emoji} ${esc(a.name)}</option>`).join("");
|
||
const lock=$("paperLockPanel");
|
||
if(lock)lock.innerHTML=acct.passwordHash&&!isPaperUnlocked(acct)
|
||
? `<div class="locked-panel"><b>${acct.cloudBacked?"Cloud paper account locked.":"Password protected."}</b><div class="account-row" style="margin:8px 0 0"><input class="input" id="paperUnlockInput" type="password" placeholder="Password"><button class="btn" id="paperUnlockBtn">${acct.cloudBacked?"Cloud log in":"Unlock"}</button></div></div>`
|
||
: acct.cloudBacked?`<div class="small muted" style="margin-bottom:10px">Cloud-synced paper account unlocked. Trades and portfolio history save across devices.</div>`
|
||
: acct.passwordHash?`<div class="small muted" style="margin-bottom:10px">Local account unlocked for this browser session.</div>`:`<div class="small muted" style="margin-bottom:10px">Local-only paper account. Add a password when creating an account to sync across devices.</div>`;
|
||
const unlockBtn=$("paperUnlockBtn");
|
||
if(unlockBtn)unlockBtn.onclick=async()=>{
|
||
if(acct.cloudBacked){
|
||
try{await loginCloudPaperAccount(acct.name,$("paperUnlockInput").value);renderPaperTab();toast("Cloud paper account loaded.");}
|
||
catch(e){toast(e.message);}
|
||
}else if(requirePaperUnlock(acct,$("paperUnlockInput").value)){renderPaperTab();toast("Paper account unlocked.");}
|
||
};
|
||
const locked=acct.passwordHash&&!isPaperUnlocked(acct);
|
||
if(locked){
|
||
$("paperStats").innerHTML=`<div class="locked-panel" style="grid-column:1/-1">Log in to view this paper portfolio.</div>`;
|
||
$("paperPositions").innerHTML=`<div class="empty">Locked.</div>`;
|
||
$("paperHistory").innerHTML=`<div class="empty">Locked.</div>`;
|
||
const chart=$("paperPortfolioChart");if(chart)chart.innerHTML="";
|
||
renderPaperMarkets();renderPaperLeaderboard(store);return;
|
||
}
|
||
const eq=paperEquity(acct),pnl=eq-acct.starting_balance;
|
||
recordPaperSnapshot(acct);
|
||
const change24h=portfolioChange24h(acct,eq);
|
||
$("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)},
|
||
{ic:"🕒",label:"24h change",value:format24h(change24h),cls:signClass(change24h.pct)},
|
||
].map(s=>`<div class="stat"><div class="ic">${s.ic}</div><div class="label">${s.label}</div><div class="value ${s.cls||""}">${s.value}</div></div>`).join("");
|
||
renderPaperPositions(acct);
|
||
renderPaperMarkets();
|
||
renderPaperHistory(acct);
|
||
renderPaperLeaderboard(store);
|
||
renderPaperPortfolioChart(acct);
|
||
}
|
||
function renderPaperPositions(acct){
|
||
const root=$("paperPositions");if(!root)return;
|
||
if(!acct.positions.length){root.innerHTML=`<div class="empty">No paper positions yet. Copy an agent or place a paper bet from the market browser.</div>`;return;}
|
||
root.innerHTML=acct.positions.map((p,i)=>{const pnl=p.unrealized_pnl||0,col=catColor(p.category);
|
||
const detail=p.kind==="AGENT"
|
||
? `${Number(p.shares||0).toFixed(6)} agent units · entry ${fmtUSD(p.entry_price)} → ${fmtUSD(p.current_price)}`
|
||
: `${p.category?`<span class="cat-badge" style="background:${col}22;color:${col}">${esc(p.category)}</span> `:""}${p.shares} ${p.side} @ ${Math.round(p.entry_price*100)}¢ → ${Math.round(p.current_price*100)}¢`;
|
||
return `<div class="pos"><div>
|
||
<div class="pq"><a class="market-title" href="${esc(positionUrl(p))}" target="_blank" rel="noopener">${esc(p.question)}</a></div>
|
||
<div class="sub">${detail}</div>
|
||
${p.kind==="AGENT"?"":`<div class="sub"><a class="market-link" href="${esc(positionUrl(p))}" target="_blank" rel="noopener">Open market</a></div>`}
|
||
</div><div class="right"><div>${fmtUSD(p.value||0)}</div><div class="${signClass(pnl)}">${pnl>=0?"+":""}${fmtUSD(pnl)}</div><button class="btn ghost" data-paper-close="${i}" style="margin-top:7px">Sell</button></div></div>`;
|
||
}).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=currentPaperMarkets(),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=>`<span class="chip ${focus===c?"active":""}" data-paper-cat="${c}">${c}<span class="ct">${counts[c]||0}</span></span>`).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);
|
||
if(!shown.length){root.innerHTML=`<div class="empty">Run a cycle or search all Polymarket markets to load paper-tradable ideas.</div>`;return;}
|
||
root.innerHTML=shown.map(s=>`<div class="trade-card">
|
||
<div class="trade-head"><div><div class="trade-title"><a class="market-title" href="${esc(s.url||marketSearchUrl(s.question))}" target="_blank" rel="noopener">${esc(s.question)}</a></div>
|
||
<div class="trade-meta"><span>${esc(s.category)}</span><span>YES ${pct(s.yes_price)}</span><span>NO ${pct(s.no_price)}</span><span>${Math.round(s.conviction)} conviction</span></div></div>
|
||
<a class="btn ghost" href="${esc(s.url||marketSearchUrl(s.question))}" target="_blank" rel="noopener">Open market</a></div>
|
||
<div class="trade-actions">
|
||
<input class="input" type="number" min="1" step="25" value="100" data-paper-amount="${esc(s.market_id)}" aria-label="Paper amount for ${esc(s.question)}">
|
||
<button class="btn" data-paper-buy="${esc(s.market_id)}" data-side="YES">Paper YES</button>
|
||
<button class="btn" data-paper-buy="${esc(s.market_id)}" data-side="NO">Paper NO</button>
|
||
</div>
|
||
</div>`).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=>`<div class="log-item"><span class="badge ${h.action}">${h.action}</span><span class="log-date">${h.date}</span> — ${esc(h.detail)}</div>`).join(""):`<div class="empty">No paper activity yet.</div>`;
|
||
}
|
||
function renderPaperLeaderboard(store){
|
||
const root=$("paperLeaderboard");if(!root)return;
|
||
const rows=Object.values(store.accounts).map(a=>{markPaperToMarket(a);const eq=paperEquity(a),ret=(eq/a.starting_balance-1)*100;return {a,eq,ret,change24h:portfolioChange24h(a,eq)};}).sort((x,y)=>y.ret-x.ret);
|
||
root.innerHTML=rows.map((r,i)=>`<div class="leader-row"><div><b>#${i+1} ${esc(r.a.name)}</b><div class="small muted">${r.a.positions.length} open · ${fmtUSD(r.eq)} equity</div></div><div style="text-align:right"><div class="${signClass(r.ret)}"><b>${fmtPct(r.ret)}</b></div><div class="small ${signClass(r.change24h.pct)}">24h ${format24h(r.change24h)}</div></div></div>`).join("");
|
||
}
|
||
function renderPaperPortfolioChart(acct){
|
||
const svg=$("paperPortfolioChart");if(!svg)return;
|
||
const snaps=(acct.snapshots||[]).filter(s=>Number.isFinite(snapTime(s))).sort((a,b)=>snapTime(a)-snapTime(b));
|
||
if(snaps.length<2){svg.innerHTML=`<text x="24" y="40" fill="rgba(224,234,255,.7)" font-size="14">Portfolio value history will appear after a few account changes.</text>`;return;}
|
||
const W=720,H=260,padL=62,padR=24,padT=26,padB=38,vals=snaps.map(s=>s.equity),mn=Math.min(...vals),mx=Math.max(...vals),span=(mx-mn)||1;
|
||
const x=i=>padL+(i/(snaps.length-1))*(W-padL-padR),y=v=>padT+(mx-v)/span*(H-padT-padB);
|
||
const d=vals.map((v,i)=>`${i?"L":"M"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
|
||
let inner=`<rect x="${padL}" y="${padT}" width="${W-padL-padR}" height="${H-padT-padB}" rx="12" fill="rgba(255,255,255,.025)" stroke="rgba(255,255,255,.08)"/>`;
|
||
for(let i=0;i<4;i++){const v=mn+(span*i/3),yy=y(v);inner+=`<line x1="${padL}" x2="${W-padR}" y1="${yy}" y2="${yy}" stroke="rgba(255,255,255,.06)"/><text x="${padL-10}" y="${yy+4}" text-anchor="end" fill="rgba(224,234,255,.62)" font-size="11">${fmtUSD(v)}</text>`;}
|
||
inner+=`<path d="${d}" fill="none" stroke="#7c8cff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||
for(const tick of chartTicks(snaps.map(snapTime),4)){inner+=`<text x="${x(tick.i)}" y="${H-12}" text-anchor="middle" fill="rgba(224,234,255,.7)" font-size="11">${chartDateLabel(tick.t,true)}</text>`;}
|
||
svg.innerHTML=inner;
|
||
}
|
||
async function searchPaperMarkets(){
|
||
const q=($("paperSearchInput").value||"").trim();
|
||
if(!q)return toast("Enter a market search.");
|
||
PAPER_MARKET_OFFSET=0;PAPER_MARKET_QUERY=q;
|
||
await loadPaperMarketPage(true);
|
||
}
|
||
async function loadPaperMarketPage(reset=false,loadAll=false){
|
||
const q=PAPER_MARKET_QUERY||(($("paperSearchInput")&&$("paperSearchInput").value)||"").trim();
|
||
setStatus("searching Polymarket...",true);
|
||
try{
|
||
if(reset){PAPER_SEARCH_RESULTS=[];PAPER_MARKET_OFFSET=0;}
|
||
let loaded=0,rawCount=0,keepGoing=true;
|
||
const limit=100,maxPages=loadAll?MAX_ACTIVE_MARKET_PAGES:1;
|
||
let cursor=null;
|
||
for(let page=0;page<maxPages&&keepGoing;page++){
|
||
const params=new URLSearchParams({closed:"false",active:"true",archived:"false",include_tag:"true",limit:String(limit),order:"volume24hr",ascending:"false"});
|
||
if(loadAll){if(cursor)params.set("after_cursor",cursor);}
|
||
else params.set("offset",String(PAPER_MARKET_OFFSET));
|
||
if(q)params.set("search",q);
|
||
const r=await fetch(`${GAMMA}/markets${loadAll?"/keyset":""}?${params}`);
|
||
if(!r.ok)throw new Error("market load failed");
|
||
const pageData=await r.json();
|
||
const raw=loadAll?(pageData.markets||[]):pageData;
|
||
rawCount+=raw.length;
|
||
const next=raw.map(normalizeMarket).filter(Boolean).map(paperTradeFromMarket);
|
||
PAPER_SEARCH_RESULTS=(PAPER_SEARCH_RESULTS||[]).concat(next);
|
||
PAPER_MARKET_OFFSET+=raw.length;
|
||
if(loadAll)cursor=pageData.next_cursor||null;
|
||
loaded+=next.length;
|
||
if(loadAll)setStatus(`loading active markets (${PAPER_SEARCH_RESULTS.length})…`,true);
|
||
if(raw.length<limit||(loadAll&&!cursor))keepGoing=false;
|
||
}
|
||
renderPaperTab();setStatus("up to date",false);
|
||
toast(loaded?`Loaded ${PAPER_SEARCH_RESULTS.length} active markets.`:rawCount?"No tradable binary markets found in this page.":"No more active markets found.");
|
||
}catch(e){setStatus("market load error",false);toast("Polymarket market load failed.");}
|
||
}
|
||
function renderAbout(){
|
||
const root=$("aboutAgents");if(!root)return;
|
||
const st=loadState();
|
||
root.innerHTML=AGENTS.map(a=>`<div class="feature" style="border-top:3px solid ${a.color}">
|
||
<div class="fic">${a.emoji}</div><h4>${a.name}</h4><div class="small ${a.aggressive?"neg-val":"muted"}">${a.aggressive?"Aggressive · higher risk":"Core · disciplined risk"}</div><p>${agentBlurb(a,st)}</p></div>`).join("");
|
||
}
|
||
const LIVE_CHECKS=[
|
||
["personal_scope","I will use only my own money and my own Polymarket/deposit wallet"],
|
||
["no_investors","No outside investors, pooled funds, user deposits, or copy-trading customers"],
|
||
["eligibility","I personally satisfy the provider and Polymarket eligibility/location rules"],
|
||
["wallet","My own deposit wallet is created, controlled by me, and funded only by me"],
|
||
["clob","Personal CLOB credentials are configured only for my own wallet"],
|
||
["manual_only","Every staged intent requires manual review before any order is placed"],
|
||
["reconciliation","I will compare cash, open orders, fills, and positions after each test"],
|
||
["risk","Personal max amount, per-agent caps, stop rules, and emergency pause are set"],
|
||
["security","Secret management, encryption, rate limits, abuse controls, and security review completed"],
|
||
["records","Append-only audit logs are recording profile changes and staged intents"],
|
||
["operations","Incident alerting and emergency pause path are ready"],
|
||
["testing","Tiny manual test trades are checked before increasing size"],
|
||
];
|
||
function defaultLiveState(){
|
||
const checks={};LIVE_CHECKS.forEach(([k])=>checks[k]=false);
|
||
const agents={};AGENTS.forEach(a=>agents[a.id]={enabled:false,maxAllocation:0,maxPositionPct:10,autoTrade:false,manualApprove:true});
|
||
return {checks,walletAddress:"",jurisdiction:"",depositLimit:0,withdrawReserve:0,agents,audit:[]};
|
||
}
|
||
function loadLiveState(){
|
||
let s;try{s=JSON.parse(localStorage.getItem(LIVE_KEY));}catch(e){s=null;}
|
||
if(!s||!s.checks)s=defaultLiveState();
|
||
LIVE_CHECKS.forEach(([k])=>{if(!(k in s.checks))s.checks[k]=false;});
|
||
if(!s.agents)s.agents={};
|
||
AGENTS.forEach(a=>{if(!s.agents[a.id])s.agents[a.id]={enabled:false,maxAllocation:0,maxPositionPct:10,autoTrade:false,manualApprove:true};});
|
||
if(!s.audit)s.audit=[];
|
||
return s;
|
||
}
|
||
function saveLiveState(s,detail){
|
||
if(detail)s.audit.push({at:nowIso(),detail});
|
||
s.audit=s.audit.slice(-80);
|
||
localStorage.setItem(LIVE_KEY,JSON.stringify(s));
|
||
pushCloudState();
|
||
}
|
||
async function fetchLiveBackendStatus(){
|
||
try{
|
||
const [liveRes,policyRes]=await Promise.all([
|
||
fetch("/api/live",{cache:"no-store"}),
|
||
fetch("/api/policies",{cache:"no-store"}),
|
||
]);
|
||
LIVE_BACKEND_STATUS=await liveRes.json();
|
||
LIVE_POLICY_STATUS=await policyRes.json();
|
||
}catch(e){
|
||
LIVE_BACKEND_STATUS={ok:false,locked_reason:"Live backend status endpoint is unavailable.",providers:[]};
|
||
LIVE_POLICY_STATUS={ok:false,note:"Policy status endpoint is unavailable."};
|
||
}
|
||
renderLiveBackendStatus();
|
||
if(PERSONAL_MODE)loadTradeTickets();
|
||
}
|
||
function renderLiveBackendStatus(){
|
||
const root=$("liveBackendStatus");if(!root)return;
|
||
const hookRoot=$("liveWebhookStatus");
|
||
const policyRoot=$("livePolicyStatus");
|
||
const s=LIVE_BACKEND_STATUS;
|
||
if(!s){root.innerHTML="Checking backend readiness...";if(hookRoot)hookRoot.innerHTML="Checking webhook setup...";if(policyRoot)policyRoot.innerHTML="Checking policy status...";return;}
|
||
const providers=(s.providers||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}${p.expected?`<div class="small muted">${esc(p.expected)}</div>`:""}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"configured":"missing"}</b></div>`).join("");
|
||
const personalReqs=(s.personal_requirements||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}${p.expected?`<div class="small muted">${esc(p.expected)}</div>`:""}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"ready":"missing"}</b></div>`).join("");
|
||
const stack=(s.provider_stack||[]).map(p=>`<div class="leader-row"><span><b>${esc(p.provider)}</b><div class="small muted">${esc(p.role)}</div><div class="small muted">${(p.checks||[]).map(c=>`${esc(c.key)}: ${c.configured?"ok":"missing"}`).join(" · ")}</div></span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"ready":"needs keys"}</b></div>`).join("");
|
||
const next=(s.next_required||[]).slice(0,12).map(x=>`<li>${esc(x)}</li>`).join("");
|
||
const personalNext=(s.personal_next_required||[]).slice(0,12).map(x=>`<li>${esc(x)}</li>`).join("");
|
||
const reqs=(s.launch_requirements||[]).map(x=>`<li>${esc(x.label)}</li>`).join("");
|
||
root.innerHTML=`<b>${s.personal_trading_enabled?"Personal prerequisites ready":"Manual personal mode locked to staging"}</b><div class="small muted" style="margin:6px 0">${esc(s.locked_reason||"Public trading remains locked.")}</div>${personalReqs?`<div class="locked-panel" style="margin-top:10px"><b>Personal-only requirements</b>${personalReqs}</div>`:""}${personalNext?`<div class="locked-panel" style="margin-top:10px"><b>Before personal live execution</b><ul>${personalNext}</ul></div>`:""}${stack?`<div class="locked-panel" style="margin-top:10px"><b>Public provider stack</b>${stack}</div>`:""}${providers}${next?`<div class="locked-panel" style="margin-top:10px"><b>Public platform blockers</b><ul>${next}</ul></div>`:""}${reqs?`<div class="locked-panel" style="margin-top:10px"><b>Public launch requirements</b><ol>${reqs}</ol></div>`:""}<div class="small muted" style="margin-top:8px">This cockpit stages manual intents only. Public investor deposits and unattended agent trading stay off.</div>`;
|
||
if(hookRoot){
|
||
const hooks=(s.webhooks||[]).map(h=>`<div class="leader-row"><span><b>${esc(h.provider)}</b><div class="small muted">${esc(h.label)}</div></span><a class="market-link" href="${esc(h.url)}" target="_blank" rel="noopener">${esc(h.url)}</a></div>`).join("");
|
||
hookRoot.innerHTML=hooks||"Webhook URLs will appear after backend readiness loads.";
|
||
}
|
||
if(policyRoot){
|
||
const p=LIVE_POLICY_STATUS||{};
|
||
const versions=p.versions||{};
|
||
const rules=(p.rules||[]).map(rule=>`<li>${esc(rule)}</li>`).join("");
|
||
policyRoot.innerHTML=`<b>${p.approved?"Policy versions configured":"Policy documents not approved yet"}</b><div class="small muted" style="margin:6px 0">${esc(p.note||"")}</div><div class="leader-row"><span>Terms</span><b>${esc(versions.terms||"missing")}</b></div><div class="leader-row"><span>Privacy</span><b>${esc(versions.privacy||"missing")}</b></div><div class="leader-row"><span>Risk disclosure</span><b>${esc(versions.risk_disclosure||"missing")}</b></div>${rules?`<ol class="steps">${rules}</ol>`:""}`;
|
||
}
|
||
}
|
||
async function dryRunLiveOrderIntent(){
|
||
const s=loadLiveState();
|
||
const intent={
|
||
user_id:PERSONAL_USER_ID,
|
||
wallet_address:s.walletAddress,
|
||
agent_id:$("liveIntentAgent").value,
|
||
market_id:$("liveIntentMarket").value.trim(),
|
||
question:($("liveIntentQuestion")&&$("liveIntentQuestion").value||"").trim(),
|
||
side:$("liveIntentSide").value,
|
||
max_amount:Number($("liveIntentAmount").value||0),
|
||
limit_price:Number(($("liveIntentPrice")&&$("liveIntentPrice").value)||0)||null,
|
||
execution_mode:"manual_approval",
|
||
account_scope:PERSONAL_MODE?"personal":"public_readiness",
|
||
};
|
||
try{
|
||
if(PERSONAL_MODE)await createTradeTicket({
|
||
user_id:intent.user_id,
|
||
agent_id:intent.agent_id,
|
||
market_id:intent.market_id,
|
||
question:intent.question,
|
||
side:intent.side,
|
||
max_amount:intent.max_amount,
|
||
limit_price:intent.limit_price,
|
||
rationale:"Manually staged from the personal cockpit.",
|
||
});
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({intent})});
|
||
const d=await r.json();
|
||
$("liveOrderDryRun").innerHTML=`<div class="locked-panel"><b>Order intent dry-run</b><div class="small muted">${esc(d.error||"Accepted dry-run.")}</div><pre style="white-space:pre-wrap;font-size:11px;color:#cdd6e8">${esc(JSON.stringify(d.audit_event||d,null,2))}</pre></div>`;
|
||
saveLiveState(s,`${PERSONAL_MODE?"Staged manual":"Dry-ran"} ${intent.side} order intent for ${intent.agent_id} on ${intent.market_id}`);
|
||
if(PERSONAL_MODE)await loadTradeTickets();
|
||
}catch(e){
|
||
toast(e.message||"Order dry-run failed.");
|
||
}
|
||
}
|
||
async function createTradeTicket(ticket){
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...ticket,action:"ticket"})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Ticket staging failed");
|
||
toast("Manual trade ticket staged.");
|
||
return d.ticket;
|
||
}
|
||
async function stageSuggestionTicket(marketId){
|
||
const s=(loadSuggestions().suggestions||[]).find(x=>String(x.market_id)===String(marketId));
|
||
if(!s)return toast("Suggestion not found.");
|
||
const amount=Number(prompt("Max amount for this manual ticket?", "10")||0);
|
||
if(!amount||amount<=0)return;
|
||
try{
|
||
await createTradeTicket({
|
||
user_id:PERSONAL_USER_ID,
|
||
agent_id:localStorage.getItem(VIEW_KEY)||"value",
|
||
market_id:s.market_id,
|
||
question:s.question,
|
||
market_url:s.url,
|
||
side:s.side,
|
||
max_amount:amount,
|
||
limit_price:s.entry_price,
|
||
rationale:s.rationale,
|
||
});
|
||
await loadTradeTickets();
|
||
showTab("live");
|
||
}catch(e){toast(e.message||"Ticket staging failed.");}
|
||
}
|
||
async function loadTradeTickets(){
|
||
if(!PERSONAL_MODE)return;
|
||
try{
|
||
const [ticketRes,portfolioRes,capitalRes]=await Promise.all([
|
||
fetch(`/api/live?action=tickets&user_id=${encodeURIComponent(PERSONAL_USER_ID)}&limit=30`,{cache:"no-store"}),
|
||
fetch(`/api/live?action=real_portfolio&user_id=${encodeURIComponent(PERSONAL_USER_ID)}`,{cache:"no-store"}),
|
||
fetch(`/api/live?action=personal_capital&user_id=${encodeURIComponent(PERSONAL_USER_ID)}`,{cache:"no-store"}),
|
||
]);
|
||
const d=await ticketRes.json(),pf=await portfolioRes.json(),cap=await capitalRes.json();
|
||
PERSONAL_TICKETS=d.tickets||[];
|
||
REAL_PORTFOLIO={positions:pf.positions||[],fills:pf.fills||[]};
|
||
PERSONAL_CAPITAL={account:cap.account||{cash:0},allocations:cap.allocations||[],events:cap.events||[]};
|
||
}catch(e){PERSONAL_TICKETS=[];REAL_PORTFOLIO={positions:[],fills:[]};PERSONAL_CAPITAL={account:{cash:0},allocations:[],events:[]};}
|
||
renderTradeTickets();
|
||
renderRealPortfolio();
|
||
renderPersonalCapital();
|
||
}
|
||
async function updateTicketStatus(ticketId,status){
|
||
try{
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"ticket_status",user_id:PERSONAL_USER_ID,ticket_id:ticketId,status})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Ticket update failed");
|
||
await loadTradeTickets();
|
||
toast("Ticket updated.");
|
||
}catch(e){toast(e.message||"Ticket update failed.");}
|
||
}
|
||
function renderTradeTickets(){
|
||
const root=$("personalTicketQueue");if(!root)return;
|
||
if(!PERSONAL_MODE){root.innerHTML=`<div class="empty">Personal trade tickets are hidden on the public site.</div>`;return;}
|
||
if(!PERSONAL_TICKETS.length){root.innerHTML=`<div class="empty">No staged tickets yet. Stage one from Suggestions or the manual console.</div>`;return;}
|
||
root.innerHTML=PERSONAL_TICKETS.map(t=>{
|
||
const instructions=t.instructions||{},steps=instructions.steps||[];
|
||
const url=t.market_url||instructions.market_url||marketSearchUrl(t.question||t.market_id);
|
||
return `<div class="trade-card">
|
||
<div class="trade-head"><div><div class="trade-title">${esc(t.question||t.market_id)}</div><div class="trade-meta"><span>${esc(t.agent_id)}</span><span>${esc(t.status)}</span><span>${new Date(t.created_at).toLocaleString()}</span></div></div><span class="pill ${esc(t.side)}">BUY ${esc(t.side)}</span></div>
|
||
<div class="small muted">Max ${fmtUSD(Number(t.max_amount||0))}${t.limit_price?` · limit ${Math.round(Number(t.limit_price)*100)}¢`:""} · ticket #${t.id}</div>
|
||
${t.rationale?`<div class="rationale">${esc(t.rationale)}</div>`:""}
|
||
<ol class="steps">${steps.map(step=>`<li>${esc(step)}</li>`).join("")}</ol>
|
||
<div class="trade-actions">
|
||
<a class="btn" href="${esc(url)}" target="_blank" rel="noopener">Open Polymarket</a>
|
||
<button class="btn ghost" data-ticket-status="${t.id}:reviewed">Reviewed</button>
|
||
<button class="btn ghost" data-ticket-status="${t.id}:skipped">Skipped</button>
|
||
<button class="btn ghost" data-ticket-status="${t.id}:cancelled">Cancel</button>
|
||
</div>
|
||
<div class="account-row" style="margin-top:12px">
|
||
<select class="select" data-fill-action="${t.id}"><option>BUY</option><option>SELL</option></select>
|
||
<input class="input" type="number" min="0.01" step="0.01" placeholder="Shares" data-fill-shares="${t.id}">
|
||
<input class="input" type="number" min="0.01" max="0.99" step="0.01" placeholder="Actual price" data-fill-price="${t.id}">
|
||
<input class="input" type="number" min="0" step="0.01" placeholder="Fees" data-fill-fees="${t.id}">
|
||
<input class="input" placeholder="Tx/link note" data-fill-note="${t.id}">
|
||
<button class="btn primary" data-record-fill="${t.id}">Record manual fill</button>
|
||
</div>
|
||
</div>`;
|
||
}).join("");
|
||
document.querySelectorAll("[data-ticket-status]").forEach(btn=>btn.onclick=()=>{const [id,status]=btn.dataset.ticketStatus.split(":");updateTicketStatus(id,status);});
|
||
document.querySelectorAll("[data-record-fill]").forEach(btn=>btn.onclick=()=>recordTicketFill(btn.dataset.recordFill));
|
||
}
|
||
function ticketById(id){return PERSONAL_TICKETS.find(t=>String(t.id)===String(id));}
|
||
async function recordTicketFill(ticketId){
|
||
const t=ticketById(ticketId);
|
||
if(!t)return toast("Ticket not found.");
|
||
const fill={
|
||
action:"record_fill",
|
||
user_id:PERSONAL_USER_ID,
|
||
ticket_id:t.id,
|
||
agent_id:t.agent_id,
|
||
market_id:t.market_id,
|
||
question:t.question,
|
||
market_url:t.market_url,
|
||
side:t.side,
|
||
fill_action:(document.querySelector(`[data-fill-action="${ticketId}"]`)||{}).value||"BUY",
|
||
shares:Number((document.querySelector(`[data-fill-shares="${ticketId}"]`)||{}).value||0),
|
||
price:Number((document.querySelector(`[data-fill-price="${ticketId}"]`)||{}).value||0),
|
||
fees:Number((document.querySelector(`[data-fill-fees="${ticketId}"]`)||{}).value||0),
|
||
tx_note:(document.querySelector(`[data-fill-note="${ticketId}"]`)||{}).value||"",
|
||
};
|
||
try{
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(fill)});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Manual fill failed");
|
||
await loadTradeTickets();
|
||
toast("Manual fill recorded for tracking.");
|
||
}catch(e){toast(e.message||"Manual fill failed.");}
|
||
}
|
||
async function updateRealMark(positionId){
|
||
const input=document.querySelector(`[data-real-mark="${positionId}"]`);
|
||
const price=Number(input&&input.value||0);
|
||
try{
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"mark_position",user_id:PERSONAL_USER_ID,position_id:positionId,current_price:price})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Mark update failed");
|
||
await loadTradeTickets();
|
||
toast("Position mark updated.");
|
||
}catch(e){toast(e.message||"Mark update failed.");}
|
||
}
|
||
function renderRealPortfolio(){
|
||
const summary=$("realPositionSummary"),posRoot=$("realPositions"),fillsRoot=$("realFills");
|
||
if(!summary||!posRoot||!fillsRoot)return;
|
||
const positions=REAL_PORTFOLIO.positions||[],fills=REAL_PORTFOLIO.fills||[];
|
||
const invested=positions.reduce((s,p)=>s+Number(p.cost_basis||0),0);
|
||
const marketValue=positions.reduce((s,p)=>s+Number(p.shares||0)*Number(p.current_price||p.avg_price||0),0);
|
||
const realized=positions.reduce((s,p)=>s+Number(p.realized_pnl||0),0);
|
||
const pnl=marketValue+realized-invested;
|
||
summary.innerHTML=`<div class="invest-total">
|
||
<div class="invest-num"><div class="k">Tracked value</div><div class="v">${fmtUSD(marketValue)}</div></div>
|
||
<div class="invest-num"><div class="k">Cost basis</div><div class="v">${fmtUSD(invested)}</div></div>
|
||
<div class="invest-num"><div class="k">Tracked P&L</div><div class="v ${signClass(pnl)}">${fmtUSD(pnl)}</div></div>
|
||
</div>`;
|
||
posRoot.innerHTML=positions.length?positions.map(p=>{
|
||
const mark=Number(p.current_price||p.avg_price||0),value=Number(p.shares||0)*mark,pnl=value+Number(p.realized_pnl||0)-Number(p.cost_basis||0);
|
||
return `<div class="trade-card">
|
||
<div class="trade-head"><div><div class="trade-title">${esc(p.question||p.market_id)}</div><div class="trade-meta"><span>${esc(p.side)}</span><span>${Number(p.shares||0).toFixed(2)} shares</span><span>avg ${Math.round(Number(p.avg_price||0)*100)}¢</span></div></div><b class="${signClass(pnl)}">${fmtUSD(pnl)}</b></div>
|
||
<div class="small muted">Value ${fmtUSD(value)} · Cost ${fmtUSD(Number(p.cost_basis||0))} · Realized ${fmtUSD(Number(p.realized_pnl||0))}</div>
|
||
<div class="trade-actions">
|
||
${p.market_url?`<a class="btn ghost" href="${esc(p.market_url)}" target="_blank" rel="noopener">Open market</a>`:""}
|
||
<input class="input" type="number" min="0.01" max="0.99" step="0.01" value="${mark||""}" data-real-mark="${p.id}">
|
||
<button class="btn ghost" data-update-mark="${p.id}">Update mark</button>
|
||
</div>
|
||
</div>`;
|
||
}).join(""):`<div class="empty">No real fills recorded yet. Record a manual fill from a ticket after you trade outside Poly Arena.</div>`;
|
||
fillsRoot.innerHTML=fills.length?fills.slice(0,25).map(f=>`<div class="log-item"><span class="badge ${esc(f.action)}">${esc(f.action)}</span><span class="log-date">${new Date(f.filled_at).toLocaleString()}</span> — ${Number(f.shares||0).toFixed(2)} ${esc(f.side)} @ ${Math.round(Number(f.price||0)*100)}¢ on ${esc((f.question||f.market_id||"").slice(0,52))}</div>`).join(""):`<div class="empty">No manual fills yet.</div>`;
|
||
document.querySelectorAll("[data-update-mark]").forEach(btn=>btn.onclick=()=>updateRealMark(btn.dataset.updateMark));
|
||
}
|
||
async function personalCapitalAction(capitalAction){
|
||
const amount=Number(($("personalCapitalAmount")&&$("personalCapitalAmount").value)||0);
|
||
const agentId=($("personalCapitalAgent")&&$("personalCapitalAgent").value)||"";
|
||
try{
|
||
const r=await fetch("/api/live",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({
|
||
action:"capital_action",
|
||
user_id:PERSONAL_USER_ID,
|
||
capital_action:capitalAction,
|
||
agent_id:agentId,
|
||
amount,
|
||
note:"Personal cockpit bookkeeping action",
|
||
})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Capital tracker update failed");
|
||
PERSONAL_CAPITAL={account:d.account||{cash:0},allocations:d.allocations||[],events:d.events||[]};
|
||
renderPersonalCapital();
|
||
toast("Personal tracker updated.");
|
||
}catch(e){toast(e.message||"Capital tracker update failed.");}
|
||
}
|
||
function renderPersonalCapital(){
|
||
const summary=$("personalCapitalSummary"),allocRoot=$("personalAgentAllocations"),histRoot=$("personalCapitalHistory"),agentSel=$("personalCapitalAgent");
|
||
if(!summary||!allocRoot||!histRoot)return;
|
||
if(agentSel)agentSel.innerHTML=AGENTS.map(a=>`<option value="${esc(a.id)}">${a.emoji} ${esc(a.name)}</option>`).join("");
|
||
const cash=Number(PERSONAL_CAPITAL.account&&PERSONAL_CAPITAL.account.cash||0);
|
||
const allocations=PERSONAL_CAPITAL.allocations||[];
|
||
const allocated=allocations.reduce((s,a)=>s+Number(a.amount||0),0);
|
||
summary.innerHTML=`<div class="invest-total">
|
||
<div class="invest-num"><div class="k">Tracked outside cash</div><div class="v">${fmtUSD(cash)}</div></div>
|
||
<div class="invest-num"><div class="k">Agent allocations</div><div class="v">${fmtUSD(allocated)}</div></div>
|
||
<div class="invest-num"><div class="k">Personal tracker total</div><div class="v">${fmtUSD(cash+allocated)}</div></div>
|
||
</div>`;
|
||
allocRoot.innerHTML=allocations.length?allocations.map(a=>{
|
||
const cfg=agentById(a.agent_id)||{name:a.agent_id,emoji:"",color:"#7c8cff"};
|
||
return `<div class="leader-row"><span><b>${cfg.emoji} ${esc(cfg.name)}</b><div class="small muted">bookkeeping allocation only</div></span><b>${fmtUSD(Number(a.amount||0))}</b></div>`;
|
||
}).join(""):`<div class="empty">No agent allocations yet. Record outside cash, then allocate any amount to an agent you want to shadow.</div>`;
|
||
const events=(PERSONAL_CAPITAL.events||[]).slice(0,20);
|
||
histRoot.innerHTML=events.length?events.map(e=>{
|
||
const cfg=e.agent_id?agentById(e.agent_id):null;
|
||
return `<div class="log-item"><span class="badge">${esc(e.action)}</span><span class="log-date">${new Date(e.created_at).toLocaleString()}</span> — ${fmtUSD(Number(e.amount||0))}${cfg?` · ${cfg.emoji} ${esc(cfg.name)}`:""}</div>`;
|
||
}).join(""):`<div class="empty">No personal tracker activity yet.</div>`;
|
||
}
|
||
function currentLiveMarkets(){return LIVE_SEARCH_RESULTS||[];}
|
||
function renderLiveMarkets(){
|
||
const root=$("liveMarketList"),filter=$("liveMarketFilterbar");if(!root)return;
|
||
const all=currentLiveMarkets(),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=>`<span class="chip ${focus===c?"active":""}" data-live-cat="${c}">${c}<span class="ct">${counts[c]||0}</span></span>`).join("");
|
||
document.querySelectorAll("[data-live-cat]").forEach(el=>el.onclick=()=>{setFocus(el.dataset.liveCat);renderLiveMarkets();});
|
||
}
|
||
const shown=all.filter(s=>focus==="All"||!focus||s.category===focus);
|
||
if(!shown.length){root.innerHTML=`<div class="empty">Search Polymarket or load active markets to stage manual tickets from the cockpit.</div>`;return;}
|
||
root.innerHTML=shown.map(s=>`<div class="trade-card">
|
||
<div class="trade-head"><div><div class="trade-title"><a class="market-title" href="${esc(s.url||marketSearchUrl(s.question))}" target="_blank" rel="noopener">${esc(s.question)}</a></div>
|
||
<div class="trade-meta"><span>${esc(s.category)}</span><span>YES ${pct(s.yes_price)}</span><span>NO ${pct(s.no_price)}</span><span>${Math.round(s.conviction||0)} conviction</span></div></div>
|
||
<a class="btn ghost" href="${esc(s.url||marketSearchUrl(s.question))}" target="_blank" rel="noopener">Open Polymarket</a></div>
|
||
<div class="rationale">${esc(s.rationale||"Active Polymarket market available for manual review.")}</div>
|
||
<div class="trade-actions">
|
||
<select class="select" data-live-side="${esc(s.market_id)}"><option>YES</option><option>NO</option></select>
|
||
<input class="input" type="number" min="1" step="25" value="100" data-live-amount="${esc(s.market_id)}" aria-label="Manual amount for ${esc(s.question)}">
|
||
<input class="input" type="number" min="0.01" max="0.99" step="0.01" value="${Number(s.entry_price||s.yes_price||0).toFixed(2)}" data-live-price="${esc(s.market_id)}" aria-label="Limit price for ${esc(s.question)}">
|
||
<button class="btn primary" data-live-ticket="${esc(s.market_id)}">Stage manual ticket</button>
|
||
</div>
|
||
</div>`).join("");
|
||
document.querySelectorAll("[data-live-ticket]").forEach(btn=>btn.onclick=()=>stageLiveMarketTicket(btn.dataset.liveTicket));
|
||
}
|
||
async function searchLiveMarkets(){
|
||
const q=($("liveSearchInput").value||"").trim();
|
||
if(!q)return toast("Enter a Polymarket search.");
|
||
LIVE_MARKET_OFFSET=0;LIVE_MARKET_QUERY=q;
|
||
await loadLiveMarketPage(true);
|
||
}
|
||
async function loadLiveMarketPage(reset=false,loadAll=false){
|
||
const q=LIVE_MARKET_QUERY||(($("liveSearchInput")&&$("liveSearchInput").value)||"").trim();
|
||
setStatus("searching Polymarket...",true);
|
||
try{
|
||
if(reset){LIVE_SEARCH_RESULTS=[];LIVE_MARKET_OFFSET=0;}
|
||
let loaded=0,rawCount=0,keepGoing=true;
|
||
const limit=100,maxPages=loadAll?MAX_ACTIVE_MARKET_PAGES:1;
|
||
let cursor=null;
|
||
for(let page=0;page<maxPages&&keepGoing;page++){
|
||
const params=new URLSearchParams({closed:"false",active:"true",archived:"false",include_tag:"true",limit:String(limit),order:"volume24hr",ascending:"false"});
|
||
if(loadAll){if(cursor)params.set("after_cursor",cursor);}
|
||
else params.set("offset",String(LIVE_MARKET_OFFSET));
|
||
if(q)params.set("search",q);
|
||
const r=await fetch(`${GAMMA}/markets${loadAll?"/keyset":""}?${params}`);
|
||
if(!r.ok)throw new Error("market load failed");
|
||
const pageData=await r.json();
|
||
const raw=loadAll?(pageData.markets||[]):pageData;
|
||
rawCount+=raw.length;
|
||
const next=raw.map(normalizeMarket).filter(Boolean).map(paperTradeFromMarket);
|
||
LIVE_SEARCH_RESULTS=(LIVE_SEARCH_RESULTS||[]).concat(next);
|
||
LIVE_MARKET_OFFSET+=raw.length;
|
||
if(loadAll)cursor=pageData.next_cursor||null;
|
||
loaded+=next.length;
|
||
if(loadAll)setStatus(`loading active markets (${LIVE_SEARCH_RESULTS.length})…`,true);
|
||
if(raw.length<limit||(loadAll&&!cursor))keepGoing=false;
|
||
}
|
||
renderLiveMarkets();setStatus("up to date",false);
|
||
toast(loaded?`Loaded ${LIVE_SEARCH_RESULTS.length} active markets.`:rawCount?"No tradable binary markets found in this page.":"No more active markets found.");
|
||
}catch(e){setStatus("market load error",false);toast("Polymarket market load failed.");}
|
||
}
|
||
async function stageLiveMarketTicket(marketId){
|
||
const s=currentLiveMarkets().find(x=>String(x.market_id)===String(marketId));
|
||
if(!s)return toast("Market not found.");
|
||
const side=(document.querySelector(`[data-live-side="${marketId}"]`)||{}).value||"YES";
|
||
const amount=Number((document.querySelector(`[data-live-amount="${marketId}"]`)||{}).value||0);
|
||
const price=Number((document.querySelector(`[data-live-price="${marketId}"]`)||{}).value||0);
|
||
try{
|
||
await createTradeTicket({
|
||
user_id:PERSONAL_USER_ID,
|
||
agent_id:($("liveIntentAgent")&&$("liveIntentAgent").value)||localStorage.getItem(VIEW_KEY)||"value",
|
||
market_id:s.market_id,
|
||
question:s.question,
|
||
market_url:s.url,
|
||
side,
|
||
max_amount:amount,
|
||
limit_price:price,
|
||
rationale:`Manually staged from the personal Polymarket browser. ${s.rationale||""}`.trim(),
|
||
});
|
||
await loadTradeTickets();
|
||
toast("Manual ticket staged. Open Polymarket to place it yourself.");
|
||
}catch(e){toast(e.message||"Ticket staging failed.");}
|
||
}
|
||
async function saveRiskProfileToServer(s){
|
||
const permissions=JSON.parse(JSON.stringify(s.agents||{}));
|
||
Object.values(permissions).forEach(p=>{p.autoTrade=false;p.manualApprove=true;});
|
||
const body={
|
||
user_id:"local-readiness-user",
|
||
wallet_address:s.walletAddress,
|
||
jurisdiction:s.jurisdiction,
|
||
deposit_limit:s.depositLimit,
|
||
withdraw_reserve:s.withdrawReserve,
|
||
agent_permissions:permissions,
|
||
};
|
||
const r=await fetch("/api/risk-profile",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||"Risk profile save failed");
|
||
return d;
|
||
}
|
||
async function recordLiveConsent(){
|
||
const s=loadLiveState();
|
||
try{
|
||
const r=await fetch("/api/consent",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({
|
||
user_id:"local-readiness-user",
|
||
jurisdiction:s.jurisdiction,
|
||
accept_terms:$("liveAcceptTerms").checked,
|
||
accept_privacy:$("liveAcceptPrivacy").checked,
|
||
accept_risk:$("liveAcceptRisk").checked,
|
||
})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(`${d.error}: ${(d.missing||[]).join(", ")}`);
|
||
saveLiveState(s,"Recorded live-money consent versions");
|
||
renderLiveMoneyTab();toast("Consent recorded.");
|
||
}catch(e){
|
||
toast(e.message||"Consent is not ready to record yet.");
|
||
}
|
||
}
|
||
function renderLiveMoneyTab(){
|
||
const root=$("liveChecklist");if(!root)return;
|
||
const s=loadLiveState(),done=LIVE_CHECKS.filter(([k])=>s.checks[k]).length;
|
||
$("liveReadyPct").textContent=`${done}/${LIVE_CHECKS.length} ready`;
|
||
root.innerHTML=LIVE_CHECKS.map(([k,label])=>`<label class="trade-card" style="display:flex;gap:10px;align-items:center">
|
||
<input type="checkbox" data-live-check="${k}" ${s.checks[k]?"checked":""}> <span>${esc(label)}</span>
|
||
</label>`).join("");
|
||
$("liveWalletAddress").value=s.walletAddress||"";
|
||
$("liveJurisdiction").value=s.jurisdiction||"";
|
||
$("liveDepositLimit").value=s.depositLimit||"";
|
||
$("liveWithdrawReserve").value=s.withdrawReserve||"";
|
||
const intentAgent=$("liveIntentAgent");
|
||
if(intentAgent)intentAgent.innerHTML=AGENTS.map(a=>`<option value="${a.id}">${a.emoji} ${esc(a.name)}</option>`).join("");
|
||
renderLiveBackendStatus();
|
||
$("liveAgentPermissions").innerHTML=AGENTS.map(a=>{const p=s.agents[a.id];
|
||
return `<div class="invest-agent" style="border-left:3px solid ${a.color};padding-left:12px">
|
||
<div class="lb-emoji">${a.emoji}</div>
|
||
<div><div class="invest-name">${esc(a.name)}</div><div class="invest-copy">${esc(a.blurb.slice(0,120))}${a.blurb.length>120?"...":""}</div></div>
|
||
<div class="trade-actions">
|
||
<label class="small muted"><input type="checkbox" data-live-agent="${a.id}" data-field="enabled" ${p.enabled?"checked":""}> enabled</label>
|
||
<label class="small muted"><input type="checkbox" data-live-agent="${a.id}" data-field="autoTrade" ${p.autoTrade?"checked":""} disabled> auto unavailable</label>
|
||
<input class="input" type="number" min="0" step="100" value="${p.maxAllocation||""}" placeholder="Max $" data-live-agent="${a.id}" data-field="maxAllocation">
|
||
<input class="input" type="number" min="1" max="100" step="1" value="${p.maxPositionPct||10}" placeholder="Max %" data-live-agent="${a.id}" data-field="maxPositionPct">
|
||
</div>
|
||
</div>`;
|
||
}).join("");
|
||
const audit=s.audit.slice(-10).reverse();
|
||
$("liveAuditTrail").innerHTML=audit.length?audit.map(x=>`<div class="log-item"><span class="log-date">${new Date(x.at).toLocaleString()}</span> — ${esc(x.detail)}</div>`).join(""):`<div class="empty">No readiness changes logged yet.</div>`;
|
||
renderPersonalCapital();
|
||
renderLiveMarkets();
|
||
document.querySelectorAll("[data-live-check]").forEach(el=>el.onchange=()=>{const next=loadLiveState();next.checks[el.dataset.liveCheck]=el.checked;saveLiveState(next,`${el.checked?"Completed":"Reopened"}: ${LIVE_CHECKS.find(([k])=>k===el.dataset.liveCheck)[1]}`);renderLiveMoneyTab();});
|
||
document.querySelectorAll("[data-live-agent]").forEach(el=>el.onchange=()=>{const next=loadLiveState(),p=next.agents[el.dataset.liveAgent],field=el.dataset.field;p[field]=el.type==="checkbox"?el.checked:Number(el.value||0);saveLiveState(next,`Updated ${agentById(el.dataset.liveAgent).name} permission: ${field}`);renderLiveMoneyTab();});
|
||
}
|
||
function esc(s){return String(s).replace(/[&<>"]/g,c=>({"&":"&","<":"<",">":">",'"':"""}[c]));}
|
||
|
||
/* ---------- Tab routing ---------- */
|
||
const TABS=["overview","leaderboard","suggestions","portfolio","invest","paper","live","about"];
|
||
function showTab(name){
|
||
if(!TABS.includes(name))name="overview";
|
||
if(PERSONAL_MODE&&name==="invest")name="overview";
|
||
if(!PERSONAL_MODE&&name==="live")name="overview";
|
||
document.querySelectorAll(".tabpanel").forEach(p=>p.classList.toggle("active",p.dataset.tab===name));
|
||
document.querySelectorAll(".tab").forEach(t=>t.classList.toggle("active",t.dataset.tab===name));
|
||
if(location.hash.slice(1)!==name)history.replaceState(null,"","#"+name);
|
||
window.scrollTo({top:0,behavior:"smooth"});
|
||
}
|
||
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({
|
||
build_version:BUILD_VERSION,
|
||
version:SUGGESTION_ENGINE_VERSION,
|
||
analyzeMarket,
|
||
negativeRiskBundleSuggestion,
|
||
parseDominanceThreshold,
|
||
parseDominanceDeadline,
|
||
dominanceBundleSuggestions,
|
||
buildAdaptiveProfile,
|
||
buildSignalCalibration,
|
||
agentObservesSuggestion,
|
||
directionalReplayAudit:DIRECTIONAL_REPLAY_AUDIT,
|
||
prepareSuggestionForAgent,
|
||
applyAgentSpecificPromotions,
|
||
historicalOpportunityPrior,
|
||
learnedOpportunity,
|
||
peerEvidenceSizeMultiplier,
|
||
tradeLossBudgetPct,
|
||
boundedStakeForRisk,
|
||
sportsFavoritePilotSuggestion,
|
||
sportsContestKey,
|
||
sportsContestNoSuggestions,
|
||
sportsFavoritePilotProfile,
|
||
applySportsFavoriteForwardPromotion,
|
||
stageSportsFavoriteShadows,
|
||
manageSportsFavoriteShadows,
|
||
shockFadeSuggestion,
|
||
shockFadePilotProfile,
|
||
sharedShockFadePilotProfile,
|
||
applyShockFadeForwardPromotion,
|
||
stageShockFadeShadows,
|
||
manageShockFadeShadows,
|
||
resolutionWeekNoSuggestion,
|
||
resolutionWeekNoPilotProfile,
|
||
sharedResolutionWeekNoPilotProfile,
|
||
applyResolutionWeekNoProfile,
|
||
stageResolutionWeekNoShadows,
|
||
manageResolutionWeekNoShadows,
|
||
autonomousRuntime:Object.freeze({version:1,publicKeys:[...PUBLIC_RUNTIME_KEYS],suggestionLimit:PUBLIC_RUNTIME_LIMITS.suggestions,
|
||
excludedKeys:[PAPER_KEY,EMAIL_ALERT_KEY,INVEST_KEY,LIVE_KEY,CHAT_KEY,PAID_AGENT_CHAT_KEY]}),
|
||
marketIsSettled,
|
||
marketIsSuspended,
|
||
historicalPriceFeatures,
|
||
offlineCachePolicy,
|
||
prepareCycleSuggestions,
|
||
scanExecutableBundles:(limit=NEG_RISK_EVENT_SCAN_LIMIT)=>fetchNegativeRiskBundles(limit),
|
||
prioritizeSignalObservations,
|
||
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,politicsTrendHoldHours:POLITICS_TREND_MIN_HOLD_HOURS,exitConfirmationHours:EXIT_CONFIRM_HOURS,maxAgentOverlap:2,gapProneMaxAgentOverlap:1,globalExplorationOwner:true,materialOverlapPct:1.25,stopLossPct:18,
|
||
coreTradeLossPct:MAX_CORE_TRADE_LOSS_PCT*100,aggressiveTradeLossPct:MAX_AGGRESSIVE_TRADE_LOSS_PCT*100,
|
||
coreGapTradeLossPct:MAX_CORE_GAP_TRADE_LOSS_PCT*100,aggressiveGapTradeLossPct:MAX_AGGRESSIVE_GAP_TRADE_LOSS_PCT*100,
|
||
offlineEntryMaxAgeMinutes:OFFLINE_ENTRY_MAX_AGE_MS/60000,offlineCacheMaxAgeHours:OFFLINE_CACHE_MAX_AGE_MS/3600000,explorationPct:15,
|
||
networkTimeoutSeconds:NETWORK_REQUEST_TIMEOUT_MS/1000,priceTimeoutSeconds:PRICE_REQUEST_TIMEOUT_MS/1000,
|
||
staleCacheIsMarkOnly:true,strategyEvidenceSurvivesBuilds:true,signalEvaluationHours:SIGNAL_EVAL_HOURS,signalEvaluationHorizons:SIGNAL_EVAL_HORIZONS,
|
||
signalEarlyRiskHorizons:SIGNAL_EARLY_RISK_HORIZONS,signalPromotionHorizons:SIGNAL_PROMOTION_HORIZONS,
|
||
signalEvaluationToleranceHours:SIGNAL_EVAL_TOLERANCE_HOURS,signalRetryHours:SIGNAL_LEDGER_RETRY_HOURS,
|
||
signalPendingLimit:SIGNAL_LEDGER_PENDING_LIMIT,signalOutcomeLimit:SIGNAL_LEDGER_OUTCOME_LIMIT,
|
||
signalDueFetchLimit:SIGNAL_LEDGER_DUE_FETCH_LIMIT,survivorshipSafeSignalGrading:true,
|
||
signalRoundTripSlippageCents:SIGNAL_ROUND_TRIP_COST*100,signalUnknownFeeFallbackCents:SIGNAL_UNKNOWN_FEE_FALLBACK*100,
|
||
exactGammaDirectionalFees:true,independentConfidence:true,eventClusteredCalibration:true,
|
||
onePendingObservationPerMarketSide:true,oldestPendingEvidenceFirst:true,coverageAwareObservationSampling:true,uncertaintyGatedCalibration:true,
|
||
unassignedMigrationRecordsDoNotBlockScopedReplacement:true,agentLearningVersion:AGENT_LEARNING_VERSION,
|
||
directionalSignalsRequirePromotion:true,directionalSignalPolicyVersion:DIRECTIONAL_SIGNAL_POLICY_VERSION,
|
||
directionalSignalCompatibleStrategies:[DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN,DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX],
|
||
focusAndReconnectCatchup:true,liveOnlyCompleteBundles:true,negativeRiskBundleSides:["YES","NO"],negativeRiskEventScanLimit:NEG_RISK_EVENT_SCAN_LIMIT,
|
||
autonomousScanMinutes:5,bundleOpportunityTelemetry:true,bundleDepthCandidateLimit:BUNDLE_DEPTH_CANDIDATE_LIMIT,
|
||
bundleMinimumNotional:BUNDLE_MIN_NOTIONAL,bundleRequiresDepthVerification:true,bundleRequiresClobFeeVerification:true,
|
||
dominanceBundleLogic:"same-event nested threshold or calendar-deadline YES/NO pairs with identical normalized terms",
|
||
negativeRiskMinimumNetReturnPct:NEG_RISK_MIN_NET_RETURN*100,
|
||
pairedMakerQuotes:"research-only-immediate-lock-or-exit",makerStrategyVersion:MAKER_STRATEGY_VERSION,
|
||
makerShadowHorizonHours:MAKER_SHADOW_HORIZON_HOURS,makerLegacyQuoteExpiryHours:MAKER_QUOTE_EXPIRY_HOURS,makerMinimumLockMarginPct:MAKER_MIN_LOCK_MARGIN*100,
|
||
makerTouchSource:"Polymarket CLOB batch price history",makerOutcomeLimit:MAKER_OUTCOME_LIMIT,makerMinimumCohortAttempts:MAKER_MIN_COHORT_ATTEMPTS,
|
||
makerMinimumPromotionLocks:MAKER_MIN_PROMOTION_LOCKS,makerMaximumLearningMultiplier:1,makerCapitalExplorationPct:0,
|
||
makerCapitalBacktestApproved:MAKER_CAPITAL_BACKTEST_APPROVED,hypotheticalRewardsCredited:false,
|
||
makerCompetitionSource:"shared public CLOB order-book audit",makerResearchSharedAcrossAgents:true,makerMarketsPartitionedAcrossAgents:true,
|
||
makerHistoryRequestsBatched:true,makerPromotionCohorts:["category","spread","reward-yield"],makerRewardPayoutMinimum:1,
|
||
emotionCanIncreaseSize:false,dualPromotionPeerBoostPct:5,currentStrategyOnlyCapitalAdaptation:true,
|
||
favoriteTrendReplayHardBlock:true,
|
||
sportsFavoritePilot:{name:"sports-contest-NO-exploration",strategyVersion:SPORTS_CONTEST_NO_STRATEGY_VERSION,entryBand:[SPORTS_FAVORITE_MIN_ENTRY,SPORTS_FAVORITE_MAX_ENTRY],targetLeadHours:SPORTS_FAVORITE_TARGET_LEAD_HOURS,
|
||
leadToleranceHours:SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS,pilotPositionPct:SPORTS_FAVORITE_PILOT_POSITION_PCT*100,
|
||
promotedPositionPct:SPORTS_FAVORITE_PROMOTED_POSITION_PCT*100,maximumPositionPct:SPORTS_FAVORITE_MAX_POSITION_PCT*100,
|
||
totalCapitalPct:SPORTS_FAVORITE_TOTAL_CAPITAL_PCT*100,shadowLimit:SPORTS_FAVORITE_SHADOW_LIMIT,
|
||
verdictEvents:SPORTS_FAVORITE_VERDICT_EVENTS,promotionEvents:SPORTS_FAVORITE_PROMOTION_EVENTS,
|
||
slippageBufferCents:SPORTS_FAVORITE_SLIPPAGE_BUFFER*100,maximumExecutionCostCents:SPORTS_FAVORITE_MAX_EXECUTION_COST*100,
|
||
maxNewPerCycle:SPORTS_FAVORITE_MAX_NEW_PER_CYCLE,capitalRequiresForwardPromotion:false,boundedExploration:true,
|
||
exactEntryFeesRequired:true,oneHighestNoPerContest:true,oldFavoriteRuleRetired:true},
|
||
shockFadePilot:{strategyVersion:SHOCK_FADE_STRATEGY_VERSION,minimumMovePct:SHOCK_FADE_MIN_MOVE*100,signalWindowHours:SHOCK_FADE_SIGNAL_WINDOW_HOURS,
|
||
exitHours:SHOCK_FADE_EXIT_HOURS,gradeToleranceHours:SHOCK_FADE_GRADE_TOLERANCE_HOURS,initialPositionPct:SHOCK_FADE_INITIAL_POSITION_PCT*100,
|
||
promotedPositionPct:SHOCK_FADE_PROMOTED_POSITION_PCT*100,totalCapitalPct:SHOCK_FADE_TOTAL_CAPITAL_PCT*100,shadowLimit:SHOCK_FADE_SHADOW_LIMIT,
|
||
qualificationEvents:SHOCK_FADE_QUALIFICATION_EVENTS,promotionEvents:SHOCK_FADE_PROMOTION_EVENTS,slippageBufferCents:SHOCK_FADE_SLIPPAGE_BUFFER*100,maximumEntryCostCents:SHOCK_FADE_MAX_EXECUTION_COST*100,
|
||
entryPriceRange:[SHOCK_FADE_MIN_ENTRY,SHOCK_FADE_MAX_ENTRY],adopterAgentIds:[...SHOCK_FADE_ADOPTER_IDS],
|
||
maxNewPerAgentCycle:SHOCK_FADE_MAX_NEW_PER_AGENT_CYCLE,modeledAuditCostCents:2,
|
||
exactThreeHourHistory:true,acceleratingFinalHour:true,oppositeSide:true,executableEntryAndExit:true,missedWindowsExpire:true,
|
||
backtestApproved:SHOCK_FADE_BACKTEST_APPROVED,capitalAdaptsFromForwardEvidence:true,forwardEvidenceSharedAcrossAdopters:true,eventDeduplicatedAcrossAdopters:true,
|
||
activeUnderlyingRiskDeduplicated:true,simultaneousUnderlyingEvidenceClustered:true},
|
||
resolutionWeekNoPilot:{strategyVersion:RESOLUTION_WEEK_NO_STRATEGY_VERSION,entryBand:[RESOLUTION_WEEK_NO_MIN_ENTRY,RESOLUTION_WEEK_NO_MAX_ENTRY],
|
||
resolutionWindows:RESOLUTION_WEEK_NO_WINDOWS.map(window=>[window.min,window.max]),validatedHorizonDays:RESOLUTION_WEEK_NO_WINDOWS.map(window=>window.days),initialPositionPct:RESOLUTION_WEEK_NO_POSITION_PCT*100,
|
||
promotedPositionPct:RESOLUTION_WEEK_NO_PROMOTED_POSITION_PCT*100,totalCapitalPct:RESOLUTION_WEEK_NO_TOTAL_CAPITAL_PCT*100,
|
||
maxNewPerAgentCycle:RESOLUTION_WEEK_NO_MAX_NEW_PER_AGENT_CYCLE,verdictEvents:RESOLUTION_WEEK_NO_VERDICT_EVENTS,promotionEvents:RESOLUTION_WEEK_NO_PROMOTION_EVENTS,
|
||
slippageBufferCents:RESOLUTION_WEEK_NO_SLIPPAGE_BUFFER*100,maximumEntryCostCents:RESOLUTION_WEEK_NO_MAX_EXECUTION_COST*100,
|
||
maximumExecutableEntry:RESOLUTION_WEEK_NO_MAX_EXECUTABLE_ENTRY,
|
||
adopterAgentIds:[...RESOLUTION_WEEK_NO_ADOPTER_IDS],backtestApproved:RESOLUTION_WEEK_NO_BACKTEST_APPROVED,
|
||
shadowLimit:RESOLUTION_WEEK_NO_SHADOW_LIMIT,outcomeLimit:RESOLUTION_WEEK_NO_OUTCOME_LIMIT,capitalRequiresForwardPromotion:true,
|
||
oneDecisionPerEventAudit:true,disjointHoldoutRequired:true,fixedDateOnly:true,sportsExcluded:true,settlementOnlyExit:true,oneOwnerPerEvent:true,sharedForwardDemotion:true},
|
||
historicalPrior:"Exact-fee directional, settlement, shock, and maker audits found no robust broad capital rule. A corrected 5,000-market audit found a point-positive three-day Sports NO pattern across all chronological quarters and 76 independent contests, but validation uncertainty remained wide. It therefore runs only as bounded 0.5% paper exploration with exact entry fees, contest deduplication, and automatic forward promotion or suspension. Complete depth-and-fee-verified bundles remain the only proven-arbitrage entry lane."}),
|
||
});
|
||
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,fees_enabled:false,fee_schedule:{rate:0,exponent:1,takerOnly:true},
|
||
},overrides);
|
||
const news={"self-test":{source_count:3,latest_title:"Election polling update"}};
|
||
const trend=analyzeMarket(market(),news);
|
||
const sportsTrend=analyzeMarket(market({category:"Sports",question:"Will the season champion win?"}),news);
|
||
const cryptoTrend=analyzeMarket(market({category:"Crypto",question:"Will the crypto proposal pass?"}),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 intervalContract=analyzeMarket(market({id:"range-test",question:"Will Count Binface win 20–30% of votes?"}),news);
|
||
const pathBarrierContract=analyzeMarket(market({id:"barrier-test",question:"Will Bitcoin reach $66,000 August 17-23?",category:"Crypto"}),news);
|
||
const fixedDateLevelContract=analyzeMarket(market({id:"fixed-level-test",question:"Will Bitcoin be above $66,000 on 2026-08-23?",category:"Crypto"}),news);
|
||
const ordinaryDatedContract=analyzeMarket(market({id:"dated-test",question:"Will the policy pass on 2026-08-22?"}),news);
|
||
const resolutionWeekEnd=new Date(Date.now()+4*86400000).toISOString();
|
||
const resolutionWeekMarket=market({id:"resolution-week",question:"Will Bitcoin be above $82,000 on August 27?",event:"Bitcoin price on August 27",event_id:"btc-aug-27",
|
||
category:"Crypto",yes_price:0.48,no_price:0.52,best_bid:0.477,best_ask:0.483,spread:0.006,volume:250000,volume_24hr:30000,liquidity:35000,
|
||
days_to_resolution:4,end_date:resolutionWeekEnd,price_change_1h:0,price_change_1d:0,price_change_1w:0});
|
||
const resolutionWeekSuggestion=resolutionWeekNoSuggestion(resolutionWeekMarket);
|
||
const unvalidatedThreeDayResolution=resolutionWeekNoSuggestion(Object.assign({},resolutionWeekMarket,
|
||
{id:"resolution-3d",event_id:"resolution-3d-event",days_to_resolution:3}));
|
||
const unvalidatedFiveDayResolution=resolutionWeekNoSuggestion(Object.assign({},resolutionWeekMarket,
|
||
{id:"resolution-5d",event_id:"resolution-5d-event",days_to_resolution:5}));
|
||
const pathDependentResolutionWeek=resolutionWeekNoSuggestion(market({id:"resolution-path",question:"Will Bitcoin reach $82,000 by August 27?",event_id:"resolution-path-event",
|
||
category:"Crypto",yes_price:0.48,no_price:0.52,best_bid:0.477,best_ask:0.483,spread:0.006,volume:250000,liquidity:35000,days_to_resolution:4,end_date:resolutionWeekEnd}));
|
||
const sportsResolutionWeek=resolutionWeekNoSuggestion(market({id:"resolution-sports",question:"Will Harbor FC win?",event_id:"resolution-sports-event",
|
||
category:"Sports",yes_price:0.48,no_price:0.52,best_bid:0.477,best_ask:0.483,spread:0.006,volume:250000,liquidity:35000,days_to_resolution:4,end_date:resolutionWeekEnd}));
|
||
const resolutionAgent=AGENTS.find(agent=>agent.id==="catalyst"),initialResolutionProfile=resolutionWeekNoPilotProfile(defaultPortfolio());
|
||
const resolutionShadowBook=defaultPortfolio(),resolutionShadowCash=resolutionShadowBook.cash;
|
||
const resolutionShadowStage=stageResolutionWeekNoShadows(resolutionShadowBook,[resolutionWeekSuggestion]);
|
||
const pausedResolutionGrade=manageResolutionWeekNoShadows(resolutionShadowBook,{"resolution-week":market({id:"resolution-week",yes_price:0.40,no_price:0.60,closed:false,accepting_orders:false})});
|
||
const settledResolutionGrade=manageResolutionWeekNoShadows(resolutionShadowBook,{"resolution-week":market({id:"resolution-week",yes_price:0,no_price:1,closed:true,accepting_orders:false})});
|
||
const offlineResolutionBook=defaultPortfolio();stageResolutionWeekNoShadows(offlineResolutionBook,[resolutionWeekSuggestion]);
|
||
const offlineResolutionGrade=manageResolutionWeekNoShadows(offlineResolutionBook,{"resolution-week":market({id:"resolution-week",yes_price:0,no_price:1,closed:true,accepting_orders:false})},{execute:false});
|
||
const compactedResolutionBook=compactPortfolioForSync(offlineResolutionBook);
|
||
const promotedResolutionState=defaultState();
|
||
for(let i=0;i<RESOLUTION_WEEK_NO_PROMOTION_EVENTS;i++)promotedResolutionState.agents.catalyst.resolution_week_no_outcomes.push({
|
||
resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,target_horizon_days:4,event_key:`winning-resolution-${i}`,
|
||
market_id:`winning-resolution-${i}`,net_return:0.10,pnl_per_100:10});
|
||
const promotedResolutionProfile=sharedResolutionWeekNoPilotProfile(promotedResolutionState);
|
||
const promotedResolutionSuggestion=applyResolutionWeekNoProfile(resolutionWeekSuggestion,promotedResolutionProfile);
|
||
const resolutionBook=defaultPortfolio(),resolutionDecision={minConv:0,maxNew:4,maxFrac:0.09,reserve:0.10,targetExposure:0.80,
|
||
learning:buildAdaptiveProfile(resolutionBook),marketLearning:buildSignalCalibration(defaultSignalLedger()),resolutionWeekNoPilot:promotedResolutionProfile};
|
||
openPositions(resolutionBook,resolutionAgent,rankSuggestionsForAgent(resolutionAgent,[promotedResolutionSuggestion]),"All",resolutionDecision,new Set(),{});
|
||
const resolutionCost=Number(resolutionBook.positions[0]&&resolutionBook.positions[0].cost||0);
|
||
markToMarket(resolutionBook,{"resolution-week":market({id:"resolution-week",yes_price:0.40,no_price:0.60,closed:false,accepting_orders:true})},resolutionAgent,{policyExits:true,executeTrades:true});
|
||
const resolutionIgnoresInterimStop=resolutionBook.positions.length===1&&resolutionBook.positions[0].shares>0;
|
||
markToMarket(resolutionBook,{"resolution-week":market({id:"resolution-week",yes_price:0,no_price:1,closed:true,accepting_orders:false})},resolutionAgent,{policyExits:true,executeTrades:true});
|
||
const losingResolutionState=defaultState();
|
||
for(let i=0;i<RESOLUTION_WEEK_NO_VERDICT_EVENTS;i++)losingResolutionState.agents.catalyst.closed.push({signal_type:"resolution-week-no-pilot",
|
||
resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,event_key:`losing-resolution-${i}`,market_id:`losing-resolution-${i}`,
|
||
original_cost:50,cost:50,realized_pnl:-5});
|
||
const losingResolutionProfile=sharedResolutionWeekNoPilotProfile(losingResolutionState);
|
||
const duplicateResolutionState=defaultState();
|
||
duplicateResolutionState.agents.catalyst.closed.push({signal_type:"resolution-week-no-pilot",resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,
|
||
event_key:"duplicate-resolution-event",market_id:"duplicate-a",original_cost:50,cost:50,realized_pnl:5});
|
||
duplicateResolutionState.agents.breakout.closed.push({signal_type:"resolution-week-no-pilot",resolution_week_strategy_version:RESOLUTION_WEEK_NO_STRATEGY_VERSION,
|
||
event_key:"duplicate-resolution-event",market_id:"duplicate-b",original_cost:50,cost:50,realized_pnl:-5});
|
||
const duplicateResolutionProfile=sharedResolutionWeekNoPilotProfile(duplicateResolutionState);
|
||
const claimedResolutionKeys=currentResolutionWeekEventKeys(duplicateResolutionState);
|
||
const legacyResolutionClaimState=defaultState();
|
||
legacyResolutionClaimState.agents.catalyst.positions.push({signal_type:"resolution-week-no-pilot",resolution_week_strategy_version:2,
|
||
event_key:"legacy-active-resolution",market_id:"legacy-active-resolution"});
|
||
const legacyResolutionClaimKeys=currentResolutionWeekEventKeys(legacyResolutionClaimState);
|
||
const sportsPilotStart=new Date(Date.now()+72*3600000).toISOString();
|
||
const sportsPilot=sportsFavoritePilotSuggestion(market({id:"sports-pilot",question:"Will Harbor FC win tomorrow?",event:"Harbor FC vs City FC",event_id:"sports-event-1",
|
||
market_slug:"harbor-city-2026-08-24-winner",
|
||
category:"Sports",yes_price:0.72,no_price:0.28,best_bid:0.715,best_ask:0.725,spread:0.01,
|
||
game_start:sportsPilotStart,days_to_resolution:1,price_change_1h:0,price_change_1d:0,price_change_1w:0}));
|
||
const expensiveSportsPilot=sportsFavoritePilotSuggestion(market({id:"sports-expensive",question:"Will Costly FC win tomorrow?",event:"Costly FC vs City FC",event_id:"sports-event-expensive",
|
||
category:"Sports",yes_price:0.72,no_price:0.28,best_bid:0.705,best_ask:0.735,spread:0.03,
|
||
game_start:sportsPilotStart,days_to_resolution:1,price_change_1h:0,price_change_1d:0,price_change_1w:0,fees_enabled:true,fee_schedule:null}));
|
||
const noSportsPilot=sportsFavoritePilotSuggestion(market({id:"sports-no",question:"Will Harbor FC fail to win tomorrow?",event:"Harbor FC vs City FC",event_id:"sports-event-no",
|
||
category:"Sports",yes_price:0.30,no_price:0.70,best_bid:0.295,best_ask:0.305,spread:0.01,
|
||
game_start:sportsPilotStart,days_to_resolution:1,price_change_1h:0,price_change_1d:0,price_change_1w:0}));
|
||
const feeSportsPilot=sportsFavoritePilotSuggestion(market({id:"sports-fee",question:"Will Harbor FC score first?",event:"Harbor FC vs City FC",
|
||
market_slug:"harbor-city-2026-08-24-first-score",category:"Sports",yes_price:0.50,no_price:0.50,best_bid:0.495,best_ask:0.505,spread:0.01,
|
||
game_start:sportsPilotStart,days_to_resolution:3,fees_enabled:true,fee_schedule:{rate:0.07,exponent:1,takerOnly:true}}));
|
||
const sportsPilotSecond=Object.assign({},sportsPilot,{market_id:"sports-pilot-second",question:"Harbor FC first-half winner?",market_price:0.35,entry_price:0.3575});
|
||
const contestCandidates=sportsContestNoSuggestions([
|
||
market({id:"contest-low-no",market_slug:"harbor-city-2026-08-24-winner",category:"Sports",event:"Harbor FC vs City FC",game_start:sportsPilotStart,
|
||
yes_price:0.72,no_price:0.28,best_bid:0.715,best_ask:0.725,spread:0.01}),
|
||
market({id:"contest-high-no",market_slug:"harbor-city-2026-08-24-total",category:"Sports",event:"Harbor FC vs City FC",game_start:sportsPilotStart,
|
||
yes_price:0.64,no_price:0.36,best_bid:0.635,best_ask:0.645,spread:0.01}),
|
||
]);
|
||
const sportsPilotBook=defaultPortfolio(),favoriteAgent=AGENTS.find(agent=>agent.id==="favorite"),sportsPilotCash=sportsPilotBook.cash;
|
||
const sportsShadowStage=stageSportsFavoriteShadows(sportsPilotBook,[sportsPilot,sportsPilotSecond]);
|
||
const pausedSportsGrade=manageSportsFavoriteShadows(sportsPilotBook,{"sports-pilot":market({id:"sports-pilot",yes_price:0.72,no_price:0.28,closed:false,accepting_orders:false})});
|
||
const settledSportsGrade=manageSportsFavoriteShadows(sportsPilotBook,{"sports-pilot":market({id:"sports-pilot",yes_price:0,no_price:1,closed:true,accepting_orders:false})});
|
||
const offlineSportsBook=defaultPortfolio();stageSportsFavoriteShadows(offlineSportsBook,[sportsPilot]);
|
||
const offlineSportsGrade=manageSportsFavoriteShadows(offlineSportsBook,{"sports-pilot":market({id:"sports-pilot",yes_price:1,no_price:0,closed:true,accepting_orders:false})},{execute:false});
|
||
const offlineSportsStayedPending=offlineSportsGrade.completed===0&&offlineSportsBook.sports_favorite_shadows.length===1&&offlineSportsBook.sports_favorite_outcomes.length===0;
|
||
const compactedSportsBook=compactPortfolioForSync(Object.assign({},offlineSportsBook,{sports_favorite_outcomes:[...sportsPilotBook.sports_favorite_outcomes]}));
|
||
const freshOfflineSportsPilot=prepareCycleSuggestions([sportsPilot],"offline-cache",true)[0];
|
||
const expiredOfflineSportsPilot=prepareCycleSuggestions([Object.assign({},sportsPilot,{game_start:new Date(Date.now()+4*3600000).toISOString()})],"offline-cache",true)[0];
|
||
const losingSportsPilotBook=defaultPortfolio();
|
||
for(let i=0;i<SPORTS_FAVORITE_VERDICT_EVENTS;i++)losingSportsPilotBook.sports_favorite_outcomes.push({strategy_version:SUGGESTION_ENGINE_VERSION,sports_contest_strategy_version:SPORTS_CONTEST_NO_STRATEGY_VERSION,
|
||
event_key:`losing-sports-event-${i}`,market_id:`losing-sports-${i}`,net_return:-0.10,pnl_per_100:-10});
|
||
const losingSportsPilotProfile=sportsFavoritePilotProfile(losingSportsPilotBook);
|
||
const unpromotedSportsSuggestion=applySportsFavoriteForwardPromotion(sportsPilot,losingSportsPilotBook);
|
||
const initialSportsPilotProfile=sportsFavoritePilotProfile(defaultPortfolio());
|
||
const promotedSportsPilotBook=defaultPortfolio();
|
||
for(let i=0;i<SPORTS_FAVORITE_PROMOTION_EVENTS;i++)promotedSportsPilotBook.sports_favorite_outcomes.push({strategy_version:SUGGESTION_ENGINE_VERSION,sports_contest_strategy_version:SPORTS_CONTEST_NO_STRATEGY_VERSION,
|
||
event_key:`winning-sports-event-${i}`,market_id:`winning-sports-${i}`,net_return:0.10,pnl_per_100:10});
|
||
const promotedSportsPilotProfile=sportsFavoritePilotProfile(promotedSportsPilotBook);
|
||
const promotedSportsSuggestion=applySportsFavoriteForwardPromotion(sportsPilot,promotedSportsPilotBook);
|
||
const promotedSportsPilotDecision=adaptiveDecision(favoriteAgent,promotedSportsPilotBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
|
||
openPositions(promotedSportsPilotBook,favoriteAgent,favoriteAgent.rank([promotedSportsSuggestion,Object.assign({},promotedSportsSuggestion,{market_id:"sports-pilot-second"})]),"All",promotedSportsPilotDecision,new Set(),{});
|
||
const sportsPilotExit=exitReason({signal_type:"sports-contest-no-pilot",entry_price:0.77,current_price:0.72,opened_at:nowIso(),unrealized_pnl:-5,quality:"sports-contest-no-pilot"},
|
||
market({category:"Sports",question:"Will Harbor FC win tomorrow?",game_start:sportsPilotStart,days_to_resolution:1}),null,favoriteAgent);
|
||
const shockNow=Math.floor(Date.now()/1000),shockPoints=[{t:shockNow-3*3600,p:0.70},{t:shockNow-2*3600,p:0.73},{t:shockNow-3600,p:0.76}];
|
||
const shockMarket=market({id:"shock-up",event:"Shock event",event_id:"shock-event",yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,spread:0.004});
|
||
const shockSuggestion=shockFadeSuggestion(shockMarket,shockPoints,shockNow);
|
||
const downwardShock=shockFadeSuggestion(market({id:"shock-down",event:"Down shock event",event_id:"shock-down-event",yes_price:0.18,no_price:0.82,best_bid:0.178,best_ask:0.182,spread:0.004}),
|
||
[{t:shockNow-3*3600,p:0.30},{t:shockNow-2*3600,p:0.27},{t:shockNow-3600,p:0.24}],shockNow);
|
||
const opposedShock=shockFadeSuggestion(shockMarket,[{t:shockNow-3*3600,p:0.70},{t:shockNow-2*3600,p:0.72},{t:shockNow-3600,p:0.86}],shockNow);
|
||
const deceleratingShock=shockFadeSuggestion(shockMarket,[{t:shockNow-3*3600,p:0.70},{t:shockNow-2*3600,p:0.70},{t:shockNow-3600,p:0.78}],shockNow);
|
||
const costlyShock=shockFadeSuggestion(market({id:"shock-costly",event_id:"shock-costly-event",yes_price:0.82,no_price:0.18,best_bid:0.805,best_ask:0.835,spread:0.03}),shockPoints,shockNow);
|
||
const extremeShock=shockFadeSuggestion(market({id:"shock-extreme",event_id:"shock-extreme-event",yes_price:0.95,no_price:0.05,best_bid:0.945,best_ask:0.955,spread:0.01}),
|
||
[{t:shockNow-3*3600,p:0.80},{t:shockNow-2*3600,p:0.85},{t:shockNow-3600,p:0.90}],shockNow);
|
||
const barrierShock=shockFadeSuggestion(market({id:"shock-barrier",event_id:"shock-barrier-event",question:"Will Ethereum reach $2,600 this month?",
|
||
category:"Crypto",yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,days_to_resolution:10}),shockPoints,shockNow);
|
||
const rangeShock=shockFadeSuggestion(market({id:"shock-range",event_id:"shock-range-event",question:"Will Elon Musk post 40-64 tweets this week?",
|
||
yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,days_to_resolution:10}),shockPoints,shockNow);
|
||
const shortWindowShock=shockFadeSuggestion(market({id:"shock-short",event_id:"shock-short-event",question:"Will the proposal pass tomorrow?",
|
||
yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,days_to_resolution:0.5}),shockPoints,shockNow);
|
||
const fixedDateShock=shockFadeSuggestion(market({id:"shock-fixed",event_id:"shock-fixed-event",question:"Will Ethereum be above $2,600 on September 1?",
|
||
category:"Crypto",yes_price:0.82,no_price:0.18,best_bid:0.818,best_ask:0.822,days_to_resolution:10}),shockPoints,shockNow);
|
||
const shockSecond=Object.assign({},shockSuggestion,{market_id:"shock-second",question:"Second outcome in the same event"});
|
||
const shockBook=defaultPortfolio(),shockCash=shockBook.cash,shockAgent=AGENTS.find(agent=>agent.id==="reversal");
|
||
const shockStage=stageShockFadeShadows(shockBook,[shockSuggestion,shockSecond]);
|
||
const earlyShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.78,no_price:0.22,best_bid:0.775,best_ask:0.785})});
|
||
const completedShadowOpened=new Date(Date.now()-12.5*3600000);shockBook.shock_fade_shadows[0].opened_at=completedShadowOpened.toISOString();
|
||
shockBook.shock_fade_shadows[0].target_at=new Date(completedShadowOpened.getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString();
|
||
const completedShockGrade=manageShockFadeShadows(shockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})});
|
||
const offlineShockStageBook=defaultPortfolio(),offlineShockStage=stageShockFadeShadows(offlineShockStageBook,[shockSuggestion],{execute:false});
|
||
const offlineShockBook=defaultPortfolio();stageShockFadeShadows(offlineShockBook,[shockSuggestion]);
|
||
const offlineShockGrade=manageShockFadeShadows(offlineShockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})},{execute:false});
|
||
const expiredShockBook=defaultPortfolio();stageShockFadeShadows(expiredShockBook,[shockSuggestion]);const expiredShadowOpened=new Date(Date.now()-15*3600000);
|
||
expiredShockBook.shock_fade_shadows[0].opened_at=expiredShadowOpened.toISOString();
|
||
expiredShockBook.shock_fade_shadows[0].target_at=new Date(expiredShadowOpened.getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString();
|
||
const expiredShockGrade=manageShockFadeShadows(expiredShockBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0,best_ask:0})});
|
||
const compactedShockBook=compactPortfolioForSync(Object.assign({},offlineShockBook,{shock_fade_outcomes:[...shockBook.shock_fade_outcomes]}));
|
||
const initialShockBook=defaultPortfolio(),initialShockProfile=shockFadePilotProfile(initialShockBook),initialShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,initialShockBook);
|
||
const initialShockDecision=adaptiveDecision(shockAgent,initialShockBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
|
||
openPositions(initialShockBook,shockAgent,rankSuggestionsForAgent(shockAgent,[initialShockSuggestion]),"All",initialShockDecision,new Set(),{});
|
||
const initialShadowStage=stageShockFadeShadows(initialShockBook,[shockSuggestion]);
|
||
const qualifiedShockEvidenceBook=defaultPortfolio();
|
||
for(let i=0;i<SHOCK_FADE_QUALIFICATION_EVENTS;i++)qualifiedShockEvidenceBook.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,
|
||
event_key:`qualified-shock-event-${i}`,market_id:`qualified-shock-${i}`,net_return:0.08,pnl_per_100:8});
|
||
const qualifiedShockProfile=shockFadePilotProfile(qualifiedShockEvidenceBook);
|
||
const qualifiedShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,qualifiedShockEvidenceBook);
|
||
const qualifiedShockDecision=Object.assign(adaptiveDecision(shockAgent,defaultPortfolio(),1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger())),
|
||
{shockFadePilot:qualifiedShockProfile});
|
||
const openedQualifiedShockBook=()=>{const book=defaultPortfolio();openPositions(book,shockAgent,rankSuggestionsForAgent(shockAgent,[qualifiedShockSuggestion]),"All",qualifiedShockDecision,new Set(),{});return book;};
|
||
const qualifiedCapitalBook=openedQualifiedShockBook();
|
||
const capitalPositionBlocksShadow=stageShockFadeShadows(qualifiedCapitalBook,[shockSuggestion]);
|
||
const crossAgentShadowBook=defaultPortfolio(),crossAgentShadowBlock=stageShockFadeShadows(crossAgentShadowBook,[shockSuggestion],{usedClaimKeys:new Set(shockFadeClaimKeys(shockSuggestion))});
|
||
const catalystShockAgent=AGENTS.find(agent=>agent.id==="catalyst"),adopterShockBook=defaultPortfolio();
|
||
const adopterShockDecision=Object.assign(adaptiveDecision(catalystShockAgent,adopterShockBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger())),
|
||
{shockFadePilot:qualifiedShockProfile});
|
||
const adopterShockSuggestions=[0,1,2].map(index=>Object.assign({},qualifiedShockSuggestion,{market_id:`adopter-shock-${index}`,event_key:`adopter-shock-event-${index}`,question:`Adopter shock ${index}`,shock_risk_key:null}));
|
||
openPositions(adopterShockBook,catalystShockAgent,rankSuggestionsForAgent(catalystShockAgent,adopterShockSuggestions),"All",adopterShockDecision,new Set(),{});
|
||
const sameEventShockBook=defaultPortfolio(),sameEventShockSuggestions=[qualifiedShockSuggestion,Object.assign({},qualifiedShockSuggestion,{market_id:"shock-sibling",question:"Shock sibling"})];
|
||
openPositions(sameEventShockBook,catalystShockAgent,rankSuggestionsForAgent(catalystShockAgent,sameEventShockSuggestions),"All",adopterShockDecision,new Set(),{});
|
||
const ethShockA=Object.assign({},qualifiedShockSuggestion,{market_id:"eth-shock-a",event_key:"eth-event-a",event:"ETH above 2500",question:"Will Ethereum reach $2,500 this week?",shock_risk_key:null});
|
||
const ethShockB=Object.assign({},qualifiedShockSuggestion,{market_id:"eth-shock-b",event_key:"eth-event-b",event:"ETH above 2600",question:"Will ETH reach $2,600 this month?",shock_risk_key:null});
|
||
const btcShock=Object.assign({},qualifiedShockSuggestion,{market_id:"btc-shock",event_key:"btc-event",event:"Bitcoin above 80000",question:"Will Bitcoin reach $80,000 this month?",shock_risk_key:null});
|
||
const correlatedShockBook=defaultPortfolio();
|
||
openPositions(correlatedShockBook,catalystShockAgent,rankSuggestionsForAgent(catalystShockAgent,[ethShockA,ethShockB,btcShock]),"All",adopterShockDecision,new Set(),{});
|
||
const claimedRiskState=defaultState();claimedRiskState.agents.catalyst.positions.push(Object.assign({},ethShockA,{signal_type:"shock-fade-pilot",shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION-1}));
|
||
const claimedRiskKeys=currentShockClaimKeys(claimedRiskState);
|
||
const earlyShockCapitalBook=openedQualifiedShockBook();
|
||
markToMarket(earlyShockCapitalBook,{"shock-up":market({id:"shock-up",yes_price:0.90,no_price:0.10,best_bid:0.895,best_ask:0.905})},shockAgent,{policyExits:true,executeTrades:true});
|
||
const timedShockCapitalBook=openedQualifiedShockBook();
|
||
const timedShockOpened=new Date(Date.now()-12.5*3600000);timedShockCapitalBook.positions[0].opened_at=timedShockOpened.toISOString();
|
||
timedShockCapitalBook.positions[0].target_exit_at=new Date(timedShockOpened.getTime()+SHOCK_FADE_EXIT_HOURS*3600000).toISOString();
|
||
markToMarket(timedShockCapitalBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})},shockAgent,{policyExits:true,executeTrades:true});
|
||
const legacyShockCapitalBook=openedQualifiedShockBook();
|
||
const legacyOpenedAt=new Date(Date.now()-12.5*3600000),legacyShockPosition=legacyShockCapitalBook.positions[0];
|
||
legacyShockPosition.opened_at=legacyOpenedAt.toISOString();legacyShockPosition.shock_strategy_version=2;
|
||
legacyShockPosition.target_exit_at=new Date(legacyOpenedAt.getTime()+24*3600000).toISOString();
|
||
markToMarket(legacyShockCapitalBook,{"shock-up":market({id:"shock-up",yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755})},shockAgent,{policyExits:true,executeTrades:true});
|
||
const unsafeShockCapitalBook=openedQualifiedShockBook();
|
||
unsafeShockCapitalBook.positions[0].question="Will Ethereum reach $2,600 this month?";
|
||
markToMarket(unsafeShockCapitalBook,{"shock-up":market({id:"shock-up",question:"Will Ethereum reach $2,600 this month?",category:"Crypto",
|
||
yes_price:0.75,no_price:0.25,best_bid:0.745,best_ask:0.755,days_to_resolution:10})},shockAgent,{policyExits:true,executeTrades:true});
|
||
const promotedShockBook=defaultPortfolio();
|
||
for(let i=0;i<SHOCK_FADE_PROMOTION_EVENTS;i++)promotedShockBook.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`winning-shock-event-${i}`,market_id:`winning-shock-${i}`,net_return:0.08,pnl_per_100:8});
|
||
const promotedShockProfile=shockFadePilotProfile(promotedShockBook),promotedShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,promotedShockBook);
|
||
const promotedShockDecision=adaptiveDecision(shockAgent,promotedShockBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
|
||
openPositions(promotedShockBook,shockAgent,rankSuggestionsForAgent(shockAgent,[promotedShockSuggestion]),"All",promotedShockDecision,new Set(),{});
|
||
const losingShockBook=defaultPortfolio();
|
||
for(let i=0;i<SHOCK_FADE_VERDICT_EVENTS;i++)losingShockBook.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`losing-shock-event-${i}`,market_id:`losing-shock-${i}`,net_return:-0.10,pnl_per_100:-10});
|
||
const losingShockProfile=shockFadePilotProfile(losingShockBook),demotedShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,losingShockBook);
|
||
const sharedPositiveShockState=defaultState(),shockAdopterIds=[...SHOCK_FADE_ADOPTER_IDS];
|
||
for(let i=0;i<SHOCK_FADE_PROMOTION_EVENTS;i++)sharedPositiveShockState.agents[shockAdopterIds[i%shockAdopterIds.length]].shock_fade_outcomes.push({
|
||
shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`shared-winning-shock-${i}`,market_id:`shared-winning-market-${i}`,net_return:0.08,pnl_per_100:8});
|
||
const sharedPositiveShockProfile=sharedShockFadePilotProfile(sharedPositiveShockState);
|
||
const sharedPositiveShockSuggestion=applyShockFadeForwardPromotion(shockSuggestion,sharedPositiveShockState.agents.catalyst,sharedPositiveShockProfile);
|
||
const sharedPositiveOpenBook=defaultPortfolio(),sharedPositiveDecision=Object.assign(
|
||
adaptiveDecision(catalystShockAgent,sharedPositiveOpenBook,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger())),
|
||
{shockFadePilot:sharedPositiveShockProfile});
|
||
openPositions(sharedPositiveOpenBook,catalystShockAgent,rankSuggestionsForAgent(catalystShockAgent,[sharedPositiveShockSuggestion]),"All",sharedPositiveDecision,new Set(),{});
|
||
const sharedLosingShockState=defaultState();
|
||
for(let i=0;i<SHOCK_FADE_VERDICT_EVENTS;i++)sharedLosingShockState.agents[shockAdopterIds[i%shockAdopterIds.length]].shock_fade_outcomes.push({
|
||
shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`shared-losing-shock-${i}`,market_id:`shared-losing-market-${i}`,net_return:-0.10,pnl_per_100:-10});
|
||
const sharedLosingShockProfile=sharedShockFadePilotProfile(sharedLosingShockState);
|
||
const duplicateShockState=defaultState();
|
||
duplicateShockState.agents.catalyst.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:"shared-duplicate",market_id:"duplicate-a",net_return:0.08});
|
||
duplicateShockState.agents.reversal.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:"shared-duplicate",market_id:"duplicate-b",net_return:0.02});
|
||
duplicateShockState.agents.breakout.shock_fade_outcomes.push({shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION-1,event_key:"legacy-shock",market_id:"legacy-shock",net_return:0.90});
|
||
const duplicateShockProfile=sharedShockFadePilotProfile(duplicateShockState);
|
||
const correlatedEvidenceState=defaultState(),correlatedOpenedAt="2026-08-21T21:35:31.558Z";
|
||
["catalyst","reversal","breakout"].forEach((id,index)=>correlatedEvidenceState.agents[id].closed.push({signal_type:"shock-fade-pilot",
|
||
shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,event_key:`eth-correlated-${index}`,market_id:`eth-correlated-${index}`,
|
||
question:`Will Ethereum reach $${2500+index*100}?`,opened_at:correlatedOpenedAt,original_cost:100,realized_pnl:index-4}));
|
||
const correlatedEvidenceProfile=sharedShockFadePilotProfile(correlatedEvidenceState);
|
||
const sharedCapitalShockState=defaultState();
|
||
sharedCapitalShockState.agents.catalyst.closed.push(Object.assign({},timedShockCapitalBook.closed[0]));
|
||
const sharedCapitalShockProfile=sharedShockFadePilotProfile(sharedCapitalShockState);
|
||
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 politicsEarly={entry_price:0.42,current_price:0.39,opened_at:hoursAgo(48),signal_conflict_since:hoursAgo(7),unrealized_pnl:-10,quality:"confirmed",category:"Politics",signal_type:"trend"};
|
||
const politicsMature=Object.assign({},politicsEarly,{opened_at:hoursAgo(73)});
|
||
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 learner=defaultPortfolio(),closedAt=nowIso();
|
||
for(let i=0;i<8;i++)learner.closed.push({strategy_version:34,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
|
||
realized_pnl:20,opened_at:hoursAgo(72+i),closed_at:closedAt});
|
||
for(let i=0;i<8;i++)learner.closed.push({strategy_version:34,signal_type:"reversal",quality:"reversal",category:"Sports",side:"NO",entry_price:0.42,original_cost:100,
|
||
realized_pnl:-20,opened_at:hoursAgo(72+i),closed_at:closedAt});
|
||
const learningProfile=buildAdaptiveProfile(learner);
|
||
const legacyOnlyOpportunity=learnedOpportunity(AGENTS[0],learner,{market_id:"legacy-only-personal",signal_type:"catalyst",quality:"confirmed",
|
||
category:"Politics",side:"YES",entry_price:0.42,days_to_resolution:45},learningProfile,{samples:0,pending:0,buckets:{}});
|
||
const legacyPositiveDecision=adaptiveDecision(AGENTS[0],learner,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
|
||
const currentPositiveLearner=defaultPortfolio();
|
||
for(let i=0;i<8;i++)currentPositiveLearner.closed.push({strategy_version:SUGGESTION_ENGINE_VERSION,signal_type:"catalyst",quality:"confirmed",
|
||
category:"Politics",side:"YES",entry_price:0.42,days_to_resolution:45,original_cost:100,realized_pnl:20,opened_at:hoursAgo(72+i),closed_at:closedAt});
|
||
const currentPositiveProfile=buildAdaptiveProfile(currentPositiveLearner);
|
||
const offlineCurrentState=defaultState();offlineCurrentState.agents.value=currentPositiveLearner;
|
||
const offlineCurrentProfile=buildAdaptiveProfile(compactAgentStateForSync(offlineCurrentState).agents.value);
|
||
const currentPositiveOpportunity=learnedOpportunity(AGENTS[0],currentPositiveLearner,{market_id:"current-positive-personal",signal_type:"catalyst",
|
||
quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,days_to_resolution:45},currentPositiveProfile,{samples:0,pending:0,buckets:{}});
|
||
const currentPositiveDecision=adaptiveDecision(AGENTS[0],currentPositiveLearner,1,AGENTS.length,STARTING_BALANCE,buildSignalCalibration(defaultSignalLedger()));
|
||
const calibrationLedger=defaultSignalLedger();
|
||
for(let i=0;i<16;i++)SIGNAL_EVAL_HORIZONS.forEach(horizon=>calibrationLedger.outcomes.push({market_id:`trend-market-${i}`,event_key:`trend-event-${i}`,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,target_horizon_hours:horizon,strategy_version:SUGGESTION_ENGINE_VERSION,return:0.12,evaluated_at:closedAt}));
|
||
for(let i=0;i<16;i++)SIGNAL_EVAL_HORIZONS.forEach(horizon=>calibrationLedger.outcomes.push({market_id:`reversal-market-${i}`,event_key:`reversal-event-${i}`,signal_type:"reversal",quality:"reversal",category:"Sports",side:"NO",entry_price:0.42,target_horizon_hours:horizon,strategy_version:SUGGESTION_ENGINE_VERSION,return:-0.12,evaluated_at:closedAt}));
|
||
const calibrationProfile=buildSignalCalibration(calibrationLedger);
|
||
const calibrationDecisionFixture=adaptiveDecision(AGENTS[0],defaultPortfolio(),1,AGENTS.length,STARTING_BALANCE,calibrationProfile);
|
||
const learnedTrend=learnedOpportunity(AGENTS[0],learner,{market_id:"learn-trend",signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42},learningProfile,calibrationProfile);
|
||
const learnedReversal=learnedOpportunity(AGENTS[0],learner,{market_id:"learn-reversal",signal_type:"reversal",quality:"reversal",category:"Sports",side:"NO",entry_price:0.42},learningProfile,calibrationProfile);
|
||
let priorMarketId="prior-reversal-0",priorIndex=0;
|
||
while(stableExploration(AGENTS[0].id,priorMarketId))priorMarketId=`prior-reversal-${++priorIndex}`;
|
||
const priorReversal=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:priorMarketId,signal_type:"reversal",quality:"reversal",category:"Sports",side:"NO",entry_price:0.42});
|
||
const priorCryptoLongshot=historicalOpportunityPrior({signal_type:"catalyst",category:"Crypto",entry_price:0.20});
|
||
let sportsPriorId="prior-sports-0",sportsPriorIndex=0;
|
||
while(stableExploration(AGENTS[0].id,sportsPriorId))sportsPriorId=`prior-sports-${++sportsPriorIndex}`;
|
||
const priorSportsTrend=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:sportsPriorId,signal_type:"trend",quality:"confirmed",category:"Sports",side:"NO",entry_price:0.58});
|
||
const priorCryptoTrend=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:"prior-crypto",signal_type:"trend",quality:"confirmed",category:"Crypto",side:"YES",entry_price:0.42});
|
||
const priorPoliticsTrend=historicalOpportunityPrior({signal_type:"trend",category:"Politics",side:"NO",entry_price:0.58});
|
||
const shortNoPrior=learnedOpportunity(AGENTS[0],defaultPortfolio(),{market_id:"short-no",signal_type:"trend",quality:"confirmed",category:"Politics",side:"NO",entry_price:0.58,days_to_resolution:14});
|
||
const longNoPrior=historicalOpportunityPrior({signal_type:"trend",category:"Politics",side:"NO",entry_price:0.58,days_to_resolution:30});
|
||
const ledgerState={signal_ledger:{pending:[{key:"ledger-test",market_id:"ledger-test",observed_at:hoursAgo(25),side:"YES",entry_price:0.40,
|
||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||
updateSignalLedger(ledgerState,[market({id:"ledger-test",yes_price:0.50,no_price:0.50})],[]);
|
||
const feeFreeSignalCost=signalExecutionCost({fees_enabled:false,fee_schedule:{rate:0,exponent:1}},0.50,0.60);
|
||
const exactFeeSignalCost=signalExecutionCost({fees_enabled:true,fee_schedule:{rate:0.07,exponent:1}},0.50,0.60);
|
||
const unknownFeeSignalCost=signalExecutionCost({},0.50,0.60);
|
||
const secondHorizonState={signal_ledger:{pending:[{key:"ledger-second",market_id:"ledger-second",observed_at:hoursAgo(73),graded_horizons:[24],side:"YES",entry_price:0.40,
|
||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||
updateSignalLedger(secondHorizonState,[market({id:"ledger-second",yes_price:0.52,no_price:0.48})],[]);
|
||
const earlyLedgerState={signal_ledger:{pending:[{key:"ledger-early",market_id:"ledger-early",observed_at:hoursAgo(13),side:"YES",entry_price:0.40,
|
||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||
updateSignalLedger(earlyLedgerState,[market({id:"ledger-early",yes_price:0.50,no_price:0.50})],[]);
|
||
const missedFirstWindowState={signal_ledger:{pending:[{key:"ledger-missed-24",market_id:"ledger-missed-24",observed_at:hoursAgo(31),side:"YES",entry_price:0.40,
|
||
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
|
||
updateSignalLedger(missedFirstWindowState,[market({id:"ledger-missed-24",yes_price:0.90,no_price:0.10})],[]);
|
||
const observationLedgerState={signal_ledger:defaultSignalLedger()};
|
||
updateSignalLedger(observationLedgerState,[],[{market_id:"observation-only",side:"YES",entry_price:0.42,signal_type:"trend",quality:"watch",
|
||
signal_confidence:0.62,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45}]);
|
||
updateSignalLedger(observationLedgerState,[],[{market_id:"observation-only",side:"YES",entry_price:0.43,signal_type:"trend",quality:"watch",
|
||
signal_confidence:0.64,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45}]);
|
||
const priorPolicyReplacementState={signal_ledger:{pending:[{key:"prior-policy",market_id:"policy-replacement",event_key:"policy-event",
|
||
observed_at:hoursAgo(1),graded_horizons:[],side:"YES",entry_price:0.42,signal_type:"trend",quality:"watch",category:"Politics",
|
||
eligible_agent_ids:["value"],signal_policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION-1,strategy_version:PREVIOUS_STRATEGY_VERSION}],outcomes:[]}};
|
||
updateSignalLedger(priorPolicyReplacementState,[],[{market_id:"policy-replacement",event_key:"policy-event",side:"YES",entry_price:0.42,
|
||
signal_type:"trend",quality:"watch",signal_confidence:0.64,signal_strength:0.72,evidence_score:0.52,evidence_source_count:1,
|
||
price_change_1d:0.02,conviction:74,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45,fees_enabled:false,
|
||
fee_schedule:{rate:0,exponent:1,takerOnly:true}}]);
|
||
const nonDirectionalLedgerState={signal_ledger:defaultSignalLedger()};
|
||
updateSignalLedger(nonDirectionalLedgerState,[],[
|
||
{market_id:"priced-bundle",side:"NO",entry_price:0.80,signal_type:"bundle-arb",quality:"bundle-arb",signal_confidence:1,trade_ready:true,jump_risk:false},
|
||
{market_id:"sports-pilot",side:"NO",entry_price:0.67,signal_type:"sports-contest-no-pilot",quality:"sports-contest-no-pilot",signal_confidence:1,trade_ready:true,jump_risk:false},
|
||
]);
|
||
const unassignedReplacementState={signal_ledger:{pending:[{key:"unassigned-old",market_id:"scope-replacement",event_key:"scope-replacement-event",
|
||
observed_at:hoursAgo(1),graded_horizons:[],side:"YES",entry_price:0.42,signal_type:"trend",quality:"watch",category:"Politics",
|
||
eligible_agent_ids:[],signal_policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION,strategy_version:SUGGESTION_ENGINE_VERSION}],outcomes:[]}};
|
||
updateSignalLedger(unassignedReplacementState,[],[{market_id:"scope-replacement",event_key:"scope-replacement-event",side:"YES",entry_price:0.42,
|
||
signal_type:"trend",quality:"watch",signal_confidence:0.64,signal_strength:0.72,evidence_score:0.52,evidence_source_count:1,
|
||
price_change_1d:0.02,conviction:74,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45}]);
|
||
const queueLedgerState={signal_ledger:{pending:Array.from({length:SIGNAL_LEDGER_PENDING_LIMIT-1},(_,i)=>({
|
||
key:`old-${i}`,market_id:`old-market-${i}`,observed_at:hoursAgo(12),side:"YES",entry_price:0.42,
|
||
})),outcomes:[]}};
|
||
updateSignalLedger(queueLedgerState,[],Array.from({length:5},(_,i)=>({market_id:`new-market-${i}`,side:"YES",entry_price:0.42,
|
||
signal_type:"trend",quality:"confirmed",signal_confidence:0.70,trade_ready:true,jump_risk:false,category:"Politics",days_to_resolution:45})));
|
||
const coverageLedger={pending:[],outcomes:Array.from({length:8},(_,i)=>({market_id:`popular-market-${i}`,event_key:"popular-event",
|
||
signal_type:"trend",side:"YES",category:"Politics",return:0.01,evaluated_at:closedAt}))};
|
||
const coveragePriority=prioritizeSignalObservations([
|
||
{market_id:"popular-repeat",event_key:"popular-event",signal_type:"trend",side:"YES",category:"Politics",conviction:99},
|
||
{market_id:"new-politics",event_key:"new-politics-event",signal_type:"trend",side:"YES",category:"Politics",conviction:80},
|
||
{market_id:"new-sports",event_key:"new-sports-event",signal_type:"trend",side:"NO",category:"Sports",conviction:70},
|
||
],coverageLedger,[]);
|
||
const dueFetchLedger={pending:[
|
||
{key:"known",market_id:"known-active",observed_at:hoursAgo(25)},
|
||
{key:"outside-a",market_id:"outside-active-scan",observed_at:hoursAgo(26)},
|
||
{key:"outside-b",market_id:"outside-active-scan",observed_at:hoursAgo(25)},
|
||
{key:"young",market_id:"too-young",observed_at:hoursAgo(2)}],outcomes:[]};
|
||
const dueFetchIds=pendingSignalMarketIds(dueFetchLedger,new Set(["known-active"]));
|
||
const retryLedgerState={signal_ledger:{pending:[{key:"retry",market_id:"temporarily-unavailable",observed_at:hoursAgo(25),side:"YES",entry_price:0.4}],outcomes:[]}};
|
||
updateSignalLedger(retryLedgerState,[],[]);
|
||
const expiredLedgerState={signal_ledger:{pending:[{key:"expired",market_id:"never-returned",observed_at:hoursAgo(79),side:"YES",entry_price:0.4}],outcomes:[]}};
|
||
updateSignalLedger(expiredLedgerState,[],[]);
|
||
const calibrationCandidate={signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42};
|
||
const calibrationFromReturns=(returns,candidate=calibrationCandidate,horizons=SIGNAL_EVAL_HORIZONS)=>buildSignalCalibration({pending:[],outcomes:returns.flatMap((ret,index)=>horizons.map(horizon=>Object.assign({},candidate,
|
||
{market_id:`${candidate.market_id||"calibration"}-${index}`,event_key:`${candidate.event_key||candidate.market_id||"calibration-event"}-${index}`,
|
||
target_horizon_hours:horizon,strategy_version:SUGGESTION_ENGINE_VERSION,return:ret,evaluated_at:closedAt})))});
|
||
const repeatedMarketCalibration=buildSignalCalibration({pending:[],outcomes:Array.from({length:24},()=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,
|
||
{market_id:"one-repeated-market",event_key:"one-repeated-event",target_horizon_hours:horizon,strategy_version:SUGGESTION_ENGINE_VERSION,return:0.12,evaluated_at:closedAt}))).flat()});
|
||
const repeatedMarketState=calibratedOpportunity(calibrationCandidate,repeatedMarketCalibration);
|
||
const correlatedEventCalibration=buildSignalCalibration({pending:[],outcomes:Array.from({length:24},(_,index)=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,
|
||
{market_id:`correlated-market-${index}`,event_key:"one-correlated-event",target_horizon_hours:horizon,strategy_version:SUGGESTION_ENGINE_VERSION,return:0.12,evaluated_at:closedAt}))).flat()});
|
||
const correlatedEventState=calibratedOpportunity(calibrationCandidate,correlatedEventCalibration);
|
||
const singleCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns([0.10]));
|
||
const earlyPositiveCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array(24).fill(0.12),calibrationCandidate,SIGNAL_EARLY_RISK_HORIZONS));
|
||
const earlyNegativeCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array(24).fill(-0.12),calibrationCandidate,SIGNAL_EARLY_RISK_HORIZONS));
|
||
const oneHorizonPositiveCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array(24).fill(0.12),calibrationCandidate,[24]));
|
||
const compatiblePreviousProfile=buildSignalCalibration({pending:[],outcomes:Array.from({length:24},(_,index)=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,
|
||
{market_id:`compatible-previous-${index}`,event_key:`compatible-previous-event-${index}`,target_horizon_hours:horizon,strategy_version:PREVIOUS_STRATEGY_VERSION,return:0.12,evaluated_at:closedAt}))).flat()});
|
||
const compatiblePreviousCalibration=calibratedOpportunity(calibrationCandidate,compatiblePreviousProfile);
|
||
const incompatiblePreviousProfile=buildSignalCalibration({pending:[],outcomes:Array.from({length:24},(_,index)=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,
|
||
{market_id:`incompatible-previous-${index}`,event_key:`incompatible-previous-event-${index}`,target_horizon_hours:horizon,strategy_version:DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN-1,return:0.12,evaluated_at:closedAt}))).flat()});
|
||
const incompatiblePreviousCalibration=calibratedOpportunity(calibrationCandidate,incompatiblePreviousProfile);
|
||
const stablePositiveProfile=calibrationFromReturns(Array(24).fill(0.12));
|
||
const stablePositiveCalibration=calibratedOpportunity(calibrationCandidate,stablePositiveProfile);
|
||
const promotedTrendSuggestion=applyAdaptiveMarketPromotion(Object.assign({},trend,{entry_candidate:true}),stablePositiveProfile);
|
||
const favoriteTrendCandidate=Object.assign({},trend,{market_id:"favorite-trend-replay-block",entry_candidate:true,entry_price:0.62,
|
||
category:"Politics",side:"YES",signal_type:"trend",quality:"confirmed"});
|
||
const blockedFavoriteTrendPromotion=applyAdaptiveMarketPromotion(favoriteTrendCandidate,stablePositiveProfile);
|
||
const blockedFavoriteTrendOpportunity=learnedOpportunity(AGENTS[0],defaultPortfolio(),favoriteTrendCandidate,null,stablePositiveProfile);
|
||
const scopedLedger={pending:[
|
||
{key:"momentum-pending",market_id:"momentum-pending",eligible_agent_ids:["momentum"]},
|
||
{key:"value-pending",market_id:"value-pending",eligible_agent_ids:["value"]},
|
||
],outcomes:Array.from({length:24},(_,index)=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,{
|
||
market_id:`momentum-scoped-${index}`,event_key:`momentum-scoped-event-${index}`,target_horizon_hours:horizon,
|
||
strategy_version:SUGGESTION_ENGINE_VERSION,signal_policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION,
|
||
eligible_agent_ids:["momentum"],return:0.12,evaluated_at:closedAt,
|
||
}))).flat()};
|
||
const momentumScopedProfile=buildSignalCalibration(scopedLedger,"momentum"),valueScopedProfile=buildSignalCalibration(scopedLedger,"value");
|
||
const scopedCandidate=Object.assign({},trend,{entry_candidate:true,quality:"confirmed",signal_type:"trend",signal_strength:0.80,
|
||
signal_confidence:0.80,evidence_score:0.80,net_edge:0.04,entry_price:0.42,days_to_resolution:45});
|
||
const momentumPreparedSuggestion=prepareSuggestionForAgent(scopedCandidate,agentById("momentum"),momentumScopedProfile);
|
||
const valuePreparedSuggestion=prepareSuggestionForAgent(scopedCandidate,agentById("value"),valueScopedProfile);
|
||
const scopedModels=Object.fromEntries(AGENTS.map(agent=>[agent.id,buildSignalCalibration({pending:[],outcomes:[]},agent.id)]));
|
||
scopedModels.momentum=momentumScopedProfile;
|
||
const agentScopedAggregate=applyAgentSpecificPromotions(scopedCandidate,scopedModels);
|
||
const scopedStateFixture=defaultState();scopedStateFixture.signal_ledger=scopedLedger;
|
||
const compactedScopedState=compactAgentStateForSync(scopedStateFixture),compactedScopedSuggestion=compactSuggestionForSync(agentScopedAggregate);
|
||
const offlineScopedMomentum=prepareSuggestionForAgent(compactedScopedSuggestion,agentById("momentum"),momentumScopedProfile);
|
||
const offlineScopedValue=prepareSuggestionForAgent(compactedScopedSuggestion,agentById("value"),valueScopedProfile);
|
||
const losingValueLedger={pending:[],outcomes:Array.from({length:24},(_,index)=>SIGNAL_EVAL_HORIZONS.map(horizon=>Object.assign({},calibrationCandidate,{
|
||
market_id:`value-losing-${index}`,event_key:`value-losing-event-${index}`,target_horizon_hours:horizon,
|
||
strategy_version:SUGGESTION_ENGINE_VERSION,signal_policy_version:DIRECTIONAL_SIGNAL_POLICY_VERSION,
|
||
eligible_agent_ids:["value"],return:-0.12,evaluated_at:closedAt,
|
||
}))).flat()};
|
||
const losingValueProfile=buildSignalCalibration(losingValueLedger,"value"),unrelatedMomentumProfile=buildSignalCalibration(losingValueLedger,"momentum");
|
||
const losingValueState=calibratedOpportunity(scopedCandidate,losingValueProfile);
|
||
const legacyUnscopedLedger={pending:[],outcomes:[Object.assign({},calibrationCandidate,{market_id:"legacy-unscoped",event_key:"legacy-unscoped",
|
||
target_horizon_hours:24,strategy_version:SUGGESTION_ENGINE_VERSION,return:0.10,evaluated_at:closedAt})]};
|
||
const explicitEmptyScopedLedger={pending:[],outcomes:[Object.assign({},calibrationCandidate,{market_id:"empty-scoped",event_key:"empty-scoped",
|
||
target_horizon_hours:24,strategy_version:SUGGESTION_ENGINE_VERSION,eligible_agent_ids:[],return:0.10,evaluated_at:closedAt})]};
|
||
const stableNegativeCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array(24).fill(-0.12)));
|
||
const noisyCalibration=calibratedOpportunity(calibrationCandidate,calibrationFromReturns(Array.from({length:24},(_,i)=>i%2?0.12:-0.12)));
|
||
const promotedShortNoCandidate={market_id:"short-no-promoted",signal_type:"trend",quality:"confirmed",category:"Politics",side:"NO",entry_price:0.42,days_to_resolution:14};
|
||
const promotedShortNo=learnedOpportunity(AGENTS[0],defaultPortfolio(),promotedShortNoCandidate,null,calibrationFromReturns(Array(24).fill(0.12),promotedShortNoCandidate));
|
||
const promotedReversalCandidate={market_id:"reversal-promoted",signal_type:"reversal",quality:"reversal",category:"Politics",side:"NO",entry_price:0.58,days_to_resolution:45};
|
||
const promotedReversalCalibration=calibrationFromReturns(Array(24).fill(0.12),promotedReversalCandidate);
|
||
const promotedReversal=learnedOpportunity(AGENTS[0],defaultPortfolio(),promotedReversalCandidate,null,promotedReversalCalibration);
|
||
const promotedReversalSuggestion=applyAdaptiveMarketPromotion(Object.assign({},reversal,{entry_candidate:true}),promotedReversalCalibration);
|
||
const promotedOpenBook=defaultPortfolio(),promotedOpenAgent=AGENTS.find(a=>a.id==="diversifier");
|
||
openPositions(promotedOpenBook,promotedOpenAgent,[promotedReversalSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.03,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(promotedOpenBook),marketLearning:promotedReversalCalibration,
|
||
},new Set(),{});
|
||
const blockedCryptoPromotion=applyAdaptiveMarketPromotion(Object.assign({},cryptoTrend,{entry_candidate:true}),calibrationFromReturns(Array(24).fill(0.12),{
|
||
market_id:"crypto-positive",signal_type:"trend",quality:"confirmed",category:"Crypto",side:"YES",entry_price:0.42,days_to_resolution:45}));
|
||
const legacyBuildCalibration=buildSignalCalibration({pending:[],outcomes:[Object.assign({},calibrationCandidate,
|
||
{strategy_version:PREVIOUS_STRATEGY_VERSION,return:0.10,evaluated_at:closedAt})]});
|
||
const currentStrategyCalibration=buildSignalCalibration({pending:[],outcomes:[Object.assign({},calibrationCandidate,
|
||
{strategy_version:SUGGESTION_ENGINE_VERSION,return:0.10,evaluated_at:closedAt})]});
|
||
const lossLearner=defaultPortfolio();
|
||
for(let i=0;i<6;i++)lossLearner.closed.push({strategy_version:SUGGESTION_ENGINE_VERSION,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
|
||
realized_pnl:-50,opened_at:hoursAgo(72+i),closed_at:closedAt});
|
||
let lossMarketId="loss-regime-0",lossIndex=0;
|
||
while(stableExploration(AGENTS[0].id,lossMarketId))lossMarketId=`loss-regime-${++lossIndex}`;
|
||
const lossProfile=buildAdaptiveProfile(lossLearner);
|
||
const lossOpportunity=learnedOpportunity(AGENTS[0],lossLearner,{market_id:lossMarketId,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42},lossProfile);
|
||
const lossDecision=adaptiveDecision(AGENTS[0],lossLearner,5,10,STARTING_BALANCE);
|
||
const urgencyWithoutEvidenceMultiplier=peerEvidenceSizeMultiplier({peer_boost:4,learning_state:"observing",market_learning_state:"observing"});
|
||
const urgencyWithOnePromotionMultiplier=peerEvidenceSizeMultiplier({peer_boost:4,learning_state:"promoted",market_learning_state:"observing"});
|
||
const dualPromotionMultiplier=peerEvidenceSizeMultiplier({peer_boost:4,learning_state:"promoted",market_learning_state:"promoted"});
|
||
const legacyLossLearner=defaultPortfolio();
|
||
for(let i=0;i<4;i++)legacyLossLearner.closed.push({strategy_version:PREVIOUS_STRATEGY_VERSION,signal_type:"trend",quality:"confirmed",category:"Politics",side:"YES",entry_price:0.42,original_cost:100,
|
||
realized_pnl:-50,opened_at:hoursAgo(72+i),closed_at:closedAt});
|
||
const legacyLossDecision=adaptiveDecision(AGENTS[0],legacyLossLearner,5,10,STARTING_BALANCE);
|
||
const coreRiskBudget=tradeLossBudgetPct(AGENTS[0],{entry_price:0.50,days_to_resolution:40,price_change_1d:0.01,signal_type:"trend"});
|
||
const aggressiveGapBudget=tradeLossBudgetPct(AGENTS.find(a=>a.id==="conviction"),{entry_price:0.82,days_to_resolution:5,price_change_1d:0.06,signal_type:"trend"});
|
||
const riskBook=defaultPortfolio(),riskCfg=AGENTS.find(a=>a.id==="conviction");
|
||
riskBook.cash=9000;
|
||
const riskPos={market_id:"risk-test",question:"Risk test",side:"YES",shares:2000,entry_price:0.5,current_price:0.5,cost:1000,value:1000,
|
||
original_shares:2000,original_cost:1000,unrealized_pnl:0,opened_at:hoursAgo(48),strategy_version:35,quality:"confirmed",signal_type:"trend",price_change_1d:0.06};
|
||
riskBook.positions=[riskPos];
|
||
normalizeLegacyPositionRisk(riskBook,riskPos,riskCfg,market({id:"risk-test",yes_price:0.5,no_price:0.5,days_to_resolution:5,price_change_1d:0.06}));
|
||
const accountingBook=(price=0.5)=>{
|
||
const book=defaultPortfolio();book.cash=9000;
|
||
const pos={market_id:`accounting-${price}`,question:"Accounting fixture",side:"YES",shares:2000,entry_price:0.5,current_price:price,cost:1000,value:2000*price,
|
||
original_shares:2000,original_cost:1000,unrealized_pnl:2000*price-1000,opened_at:hoursAgo(48),strategy_version:SUGGESTION_ENGINE_VERSION,
|
||
quality:"confirmed",signal_type:"trend",gain_stops:{},stop_losses:{}};
|
||
book.positions=[pos];return {book,pos};
|
||
};
|
||
const fullCloseFixture=accountingBook(0.6),fullCloseEquity=equity(fullCloseFixture.book);
|
||
closePosition(fullCloseFixture.book,fullCloseFixture.pos,"Accounting full close");fullCloseFixture.book.positions=[];
|
||
const partialFixture=accountingBook(0.6),partialEquityBefore=equity(partialFixture.book);
|
||
takeProfitPosition(partialFixture.book,partialFixture.pos,GAIN_STOP_TIERS[0]);
|
||
const partialEquityAfter=equity(partialFixture.book),partialRealizedAfterFirst=partialFixture.pos.partial_realized_pnl;
|
||
closePosition(partialFixture.book,partialFixture.pos,"Accounting final close");partialFixture.book.positions=[];
|
||
const stopFixture=accountingBook(0.4);
|
||
markToMarket(stopFixture.book,{[stopFixture.pos.market_id]:market({id:stopFixture.pos.market_id,yes_price:0.4,no_price:0.6})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const settlementFixture=accountingBook(1);
|
||
markToMarket(settlementFixture.book,{[settlementFixture.pos.market_id]:market({id:settlementFixture.pos.market_id,yes_price:1,no_price:0,closed:true,accepting_orders:false})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const suspendedFixture=accountingBook(0.4);
|
||
markToMarket(suspendedFixture.book,{[suspendedFixture.pos.market_id]:market({id:suspendedFixture.pos.market_id,yes_price:0.4,no_price:0.6,closed:false,accepting_orders:false})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const suspendedMakerFixture=accountingBook(0.4);suspendedMakerFixture.pos.signal_type="maker-pair";suspendedMakerFixture.pos.maker_pair_pending=true;
|
||
markToMarket(suspendedMakerFixture.book,{[suspendedMakerFixture.pos.market_id]:market({id:suspendedMakerFixture.pos.market_id,yes_price:0.4,no_price:0.6,closed:false,accepting_orders:false})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const suspendedBundleFixture=accountingBook(0.4);suspendedBundleFixture.pos.requires_complete_bundle=true;
|
||
markToMarket(suspendedBundleFixture.book,{[suspendedBundleFixture.pos.market_id]:market({id:suspendedBundleFixture.pos.market_id,yes_price:0.4,no_price:0.6,closed:false,accepting_orders:false})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const retiredFixture=accountingBook(0.45),retiredEquityBefore=equity(retiredFixture.book);
|
||
retiredFixture.pos.strategy_version=PREVIOUS_STRATEGY_VERSION;
|
||
markToMarket(retiredFixture.book,{[retiredFixture.pos.market_id]:market({id:retiredFixture.pos.market_id,yes_price:0.45,no_price:0.55})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const retiredSportsCapitalFixture=accountingBook(0.83),retiredSportsCapitalEquity=equity(retiredSportsCapitalFixture.book);
|
||
retiredSportsCapitalFixture.pos.strategy_version=PREVIOUS_STRATEGY_VERSION;retiredSportsCapitalFixture.pos.signal_type="sports-favorite-pilot";
|
||
markToMarket(retiredSportsCapitalFixture.book,{[retiredSportsCapitalFixture.pos.market_id]:market({id:retiredSportsCapitalFixture.pos.market_id,yes_price:0.83,no_price:0.17,category:"Sports"})},favoriteAgent,{policyExits:true,executeTrades:true});
|
||
const unlabeledLegacyFixture=accountingBook(0.45),unlabeledLegacyEquityBefore=equity(unlabeledLegacyFixture.book);
|
||
delete unlabeledLegacyFixture.pos.strategy_version;delete unlabeledLegacyFixture.pos.signal_type;
|
||
markToMarket(unlabeledLegacyFixture.book,{[unlabeledLegacyFixture.pos.market_id]:market({id:unlabeledLegacyFixture.pos.market_id,yes_price:0.45,no_price:0.55})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const currentUnlabeledFixture=accountingBook(0.45);currentUnlabeledFixture.pos.strategy_version=SUGGESTION_ENGINE_VERSION;delete currentUnlabeledFixture.pos.signal_type;
|
||
markToMarket(currentUnlabeledFixture.book,{[currentUnlabeledFixture.pos.market_id]:market({id:currentUnlabeledFixture.pos.market_id,yes_price:0.45,no_price:0.55})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const nonBinaryMarketRejected=normalizeMarket({id:"over-under-test",question:"Over/Under 2.5",outcomes:'["Over","Under"]',outcomePrices:'["0.5","0.5"]',acceptingOrders:true})===null;
|
||
const makerMarket=market({id:"maker-test",question:"Will the paired quote test pass?",yes_price:0.42,no_price:0.58,best_bid:0.40,best_ask:0.44,spread:0.04,
|
||
tick_size:0.01,order_min_size:5,rewards_daily_rate:10,rewards_min_size:50,rewards_max_spread:4.5,competitive:0.8,price_change_1d:0.01});
|
||
const auditedMaker={audit_version:1,market_id:"audited-maker",condition_id:"audit-condition",event_key:"audit-event",question:"Will audited maker pass?",event:"Audit event",url:"",
|
||
category:"Politics",clob_token_ids:["audit-yes","audit-no"],reward_daily_rate:100,reward_min_size:20,reward_max_spread:5.5,hours_to_end:240,
|
||
yes_quote:0.44,no_quote:0.52,paired_cost:0.96,required_capital:19.2,locked_profit:0.8,maximum_one_leg_loss:10.4,
|
||
reward_share_floor:0.02,estimated_reward_floor_daily:2,spread:0.04,tick_size:0.01,payout_eligible:true,shadow_qualified:true};
|
||
const auditedMakerCandidate=makerPairCandidate(auditedMaker);
|
||
const belowPayoutMakerCandidate=makerPairCandidate(Object.assign({},auditedMaker,{estimated_reward_floor_daily:0.99}));
|
||
const makerBook=defaultPortfolio(),makerStage=stageMakerQuotes(makerBook,[makerMarket]),makerQuote=makerBook.maker_quotes[0];
|
||
makerQuote.shadow_only=false;
|
||
makerQuote.created_at=hoursAgo(1);
|
||
const makerFirst=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
||
const makerSingleFill=makerBook.positions.length===1&&makerBook.positions[0].side==="YES"&&makerBook.maker_quotes.length===1;
|
||
const legacyUntouchedBook=defaultPortfolio();stageMakerQuotes(legacyUntouchedBook,[makerMarket]);legacyUntouchedBook.maker_quotes[0].shadow_only=false;legacyUntouchedBook.maker_quotes[0].maker_strategy_version=1;
|
||
const legacyUntouched=manageMakerQuotes(legacyUntouchedBook,{"maker-test":makerMarket},{executeTrades:true});
|
||
const legacyOneLegBook=defaultPortfolio();stageMakerQuotes(legacyOneLegBook,[makerMarket]);legacyOneLegBook.maker_quotes[0].shadow_only=false;legacyOneLegBook.maker_quotes[0].created_at=hoursAgo(1);
|
||
manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
||
legacyOneLegBook.maker_quotes[0].maker_strategy_version=1;legacyOneLegBook.positions[0].strategy_version=PREVIOUS_STRATEGY_VERSION;
|
||
const legacyOneLeg=manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.36,no_price:0.64,best_bid:0.35,best_ask:0.37}))},{executeTrades:true});
|
||
const makerSecond=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.60,no_price:0.40,best_bid:0.57,best_ask:0.61}))},{executeTrades:true});
|
||
const makerExpectedProfit=+(makerQuote.units*(1-Number(makerQuote.yes_fill_price)-Number(makerQuote.no_fill_price))).toFixed(2);
|
||
const makerOverlapState=defaultState();makerOverlapState.agents.value=makerBook;reduceStrategyOverlap(makerOverlapState);
|
||
const makerPairSurvivesOverlap=makerBook.positions.length===2&&makerBook.positions.every(pos=>pos.requires_complete_bundle);
|
||
markToMarket(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:1,no_price:0,best_bid:1,best_ask:1,closed:true,accepting_orders:false}))},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const makerExpiryBook=defaultPortfolio();stageMakerQuotes(makerExpiryBook,[makerMarket]);makerExpiryBook.maker_quotes[0].shadow_only=false;makerExpiryBook.maker_quotes[0].created_at=hoursAgo(1);
|
||
manageMakerQuotes(makerExpiryBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
||
makerExpiryBook.maker_quotes[0].created_at=hoursAgo(25);
|
||
const makerExpired=manageMakerQuotes(makerExpiryBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.36,no_price:0.64,best_bid:0.35,best_ask:0.37}))},{executeTrades:true});
|
||
const makerOfflineBook=defaultPortfolio();stageMakerQuotes(makerOfflineBook,[makerMarket]);makerOfflineBook.maker_quotes[0].shadow_only=false;makerOfflineBook.maker_quotes[0].created_at=hoursAgo(1);
|
||
const makerOffline=manageMakerQuotes(makerOfflineBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:false});
|
||
const touchBook=defaultPortfolio();stageMakerQuotes(touchBook,[makerMarket]);const touchQuote=touchBook.maker_quotes[0];touchQuote.shadow_only=false;touchQuote.created_at=hoursAgo(1);touchQuote.yes_quote_updated_at=touchQuote.created_at;touchQuote.no_quote_updated_at=touchQuote.created_at;
|
||
const touchTimestamp=Math.floor((Date.now()-30*60000)/1000),touchMarket=market(Object.assign({},makerMarket,{best_bid:0.40,best_ask:0.44,yes_price:0.42,no_price:0.58}));
|
||
const touchFill=manageMakerQuotes(touchBook,{"maker-test":touchMarket},{executeTrades:true,touchHistory:{yes:[{t:touchTimestamp,p:0.395}],no:[{t:touchTimestamp,p:0.58}]}});
|
||
const offlineTouchBook=defaultPortfolio();stageMakerQuotes(offlineTouchBook,[makerMarket]);offlineTouchBook.maker_quotes[0].shadow_only=false;offlineTouchBook.maker_quotes[0].created_at=hoursAgo(1);
|
||
const offlineTouch=manageMakerQuotes(offlineTouchBook,{"maker-test":touchMarket},{executeTrades:false,touchHistory:{yes:[{t:touchTimestamp,p:0.395}]}});
|
||
const repriceBook=defaultPortfolio();stageMakerQuotes(repriceBook,[makerMarket]);const repriceQuote=repriceBook.maker_quotes[0];repriceQuote.shadow_only=false;repriceQuote.created_at=hoursAgo(2);
|
||
manageMakerQuotes(repriceBook,{"maker-test":market(Object.assign({},makerMarket,{best_bid:0.38,best_ask:0.40,yes_price:0.39,no_price:0.61}))},{executeTrades:true});
|
||
const preRepriceTouch=manageMakerQuotes(repriceBook,{"maker-test":touchMarket},{executeTrades:true,touchHistory:{no:[{t:touchTimestamp,p:0.58}]}});
|
||
const preRepriceIgnored=preRepriceTouch.fills===0&&repriceBook.positions.length===1&&repriceBook.maker_quotes.length===1;
|
||
repriceQuote.no_quote_updated_at=hoursAgo(1);
|
||
const postRepriceTouch=manageMakerQuotes(repriceBook,{"maker-test":touchMarket},{executeTrades:true,touchHistory:{no:[{t:touchTimestamp,p:0.58}]}});
|
||
const unfilledBook=defaultPortfolio();stageMakerQuotes(unfilledBook,[makerMarket]);unfilledBook.maker_quotes[0].shadow_only=false;unfilledBook.maker_quotes[0].created_at=hoursAgo(25);
|
||
const unfilledExpiry=manageMakerQuotes(unfilledBook,{"maker-test":touchMarket},{executeTrades:true});
|
||
const profitableMakerBook=defaultPortfolio(),losingMakerBook=defaultPortfolio();
|
||
for(let i=0;i<24;i++){
|
||
profitableMakerBook.maker_outcomes.push({quote_id:`profit-${i}`,market_id:`profit-${i}`,event_key:`profit-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"locked",pnl:4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION,maker_strategy_version:MAKER_STRATEGY_VERSION});
|
||
losingMakerBook.maker_outcomes.push({quote_id:`loss-${i}`,market_id:`loss-${i}`,event_key:`loss-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"single-exit",pnl:-4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION,maker_strategy_version:MAKER_STRATEGY_VERSION});
|
||
}
|
||
const profitableCandidate=makerCandidateLearning(makerPairCandidate(makerMarket),buildMakerProfile(profitableMakerBook));
|
||
let blockedMarket=null,explorationMarket=null;
|
||
for(let i=0;i<200&&(!blockedMarket||!explorationMarket);i++){
|
||
const id=`maker-learning-${i}`,candidate=makerPairCandidate(Object.assign({},makerMarket,{id})),lane=stableHash(`maker-explore:${id}`)%100;
|
||
if(lane>=20&&!blockedMarket)blockedMarket=candidate;if(lane<20&&!explorationMarket)explorationMarket=candidate;
|
||
}
|
||
const losingProfile=buildMakerProfile(losingMakerBook),blockedMakerCandidate=makerCandidateLearning(blockedMarket,losingProfile),explorationMakerCandidate=makerCandidateLearning(explorationMarket,losingProfile);
|
||
const shadowBook=defaultPortfolio(),shadowStage=stageMakerQuotes(shadowBook,[makerMarket]),shadowQuote=shadowBook.maker_quotes[0];
|
||
shadowQuote.created_at=hoursAgo(4);shadowQuote.yes_quote_updated_at=shadowQuote.created_at;shadowQuote.no_quote_updated_at=shadowQuote.created_at;
|
||
const shadowTouchTimestamp=Math.floor((Date.now()-30*60000)/1000),shadowCashBefore=shadowBook.cash;
|
||
const shadowFinished=manageMakerQuotes(shadowBook,{"maker-test":touchMarket},{executeTrades:true,touchHistory:{yes:[{t:shadowTouchTimestamp,p:0.395}]}});
|
||
const shadowHedgeBook=defaultPortfolio();stageMakerQuotes(shadowHedgeBook,[makerMarket]);const shadowHedgeQuote=shadowHedgeBook.maker_quotes[0];
|
||
shadowHedgeQuote.created_at=hoursAgo(1);shadowHedgeQuote.yes_quote_updated_at=shadowHedgeQuote.created_at;shadowHedgeQuote.no_quote_updated_at=shadowHedgeQuote.created_at;
|
||
const shadowHedgeCashBefore=shadowHedgeBook.cash,shadowHedgeMarket=market(Object.assign({},makerMarket,{best_bid:0.43,best_ask:0.44,yes_price:0.435,no_price:0.565}));
|
||
const shadowHedged=manageMakerQuotes(shadowHedgeBook,{"maker-test":shadowHedgeMarket},{executeTrades:true,touchHistory:{yes:[{t:shadowTouchTimestamp,p:0.395}]}});
|
||
const makerDedupeBook=defaultPortfolio(),dedupeQuote=Object.assign({},makerQuote,{id:"dedupe-maker"});
|
||
recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);
|
||
const makerCompactionBook=defaultPortfolio();makerCompactionBook.maker_quotes=[Object.assign({},makerQuote)];makerCompactionBook.maker_outcomes=[...profitableMakerBook.maker_outcomes];
|
||
const compactedMakerBook=compactPortfolioForSync(makerCompactionBook);
|
||
const sharedMakerState=defaultState();sharedMakerState.agents.value.maker_outcomes=[profitableMakerBook.maker_outcomes[0]];
|
||
sharedMakerState.agents.momentum.maker_outcomes=[profitableMakerBook.maker_outcomes[1]];
|
||
const sharedMakerFixture=buildSharedMakerProfile(sharedMakerState);
|
||
const distributedMarkets=Array.from({length:4},(_,index)=>Object.assign({},makerMarket,{id:`distributed-maker-${index}`,event:`Distributed event ${index}`}));
|
||
const distributedIds=new Set(),distributedEvents=new Set(),distributedBookA=defaultPortfolio(),distributedBookB=defaultPortfolio();
|
||
stageMakerQuotes(distributedBookA,distributedMarkets,{agentIndex:0,agentCount:2,globalMarketIds:distributedIds,globalEventKeys:distributedEvents});
|
||
stageMakerQuotes(distributedBookB,distributedMarkets,{agentIndex:1,agentCount:2,globalMarketIds:distributedIds,globalEventKeys:distributedEvents});
|
||
const distributedA=new Set(distributedBookA.maker_quotes.map(quote=>quote.market_id));
|
||
const distributedB=new Set(distributedBookB.maker_quotes.map(quote=>quote.market_id));
|
||
const bundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-test",title:"Three-way result",slug:"bundle-test",negRisk:true,enableNegRisk:true,tags:[{slug:"sports",label:"Sports"}],markets:[
|
||
{id:"bundle-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.415","0.585"]',clobTokenIds:'["a-yes","a-no"]',bestBid:0.41,bestAsk:0.42,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
{id:"bundle-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.315","0.685"]',clobTokenIds:'["b-yes","b-no"]',bestBid:0.31,bestAsk:0.32,liquidityNum:28000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
{id:"bundle-draw",question:"Draw",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["d-yes","d-no"]',bestBid:0.30,bestAsk:0.31,liquidityNum:20000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
]});
|
||
const yesBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-yes-test",title:"Three-way YES result",slug:"bundle-yes-test",negRisk:true,enableNegRisk:true,markets:[
|
||
{id:"yes-bundle-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.295","0.705"]',clobTokenIds:'["ya-yes","ya-no"]',bestBid:0.29,bestAsk:0.30,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
{id:"yes-bundle-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["yb-yes","yb-no"]',bestBid:0.30,bestAsk:0.31,liquidityNum:28000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
{id:"yes-bundle-draw",question:"Draw",outcomes:'["Yes","No"]',outcomePrices:'["0.335","0.665"]',clobTokenIds:'["yd-yes","yd-no"]',bestBid:0.33,bestAsk:0.34,liquidityNum:20000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||
]});
|
||
const zeroFeeBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-zero-fee",title:"Three-way zero-fee result",slug:"bundle-zero-fee",negRisk:true,enableNegRisk:true,markets:[
|
||
{id:"zero-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.295","0.705"]',clobTokenIds:'["za-yes","za-no"]',bestBid:0.29,bestAsk:0.30,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
|
||
{id:"zero-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["zb-yes","zb-no"]',bestBid:0.30,bestAsk:0.31,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
|
||
{id:"zero-c",question:"C wins",outcomes:'["Yes","No"]',outcomePrices:'["0.335","0.665"]',clobTokenIds:'["zc-yes","zc-no"]',bestBid:0.33,bestAsk:0.34,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
|
||
]});
|
||
const depthVwapFixture=bundleExecutableAsk({asks:[{price:"0.40",size:"30"},{price:"0.39",size:"20"}]},50);
|
||
const tinyReturnBundle=negativeRiskBundleSuggestion({id:"bundle-tiny",title:"Ten-way result",slug:"bundle-tiny",negRisk:true,enableNegRisk:true,markets:Array.from({length:10},(_,i)=>({
|
||
id:`bundle-tiny-${i}`,question:`Outcome ${i+1}`,outcomes:'["Yes","No"]',outcomePrices:'["0.1055","0.8945"]',clobTokenIds:`["tiny-${i}-yes","tiny-${i}-no"]`,
|
||
bestBid:0.1055,bestAsk:0.1065,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true,
|
||
}))});
|
||
const nonBinaryBundleRejected=negativeRiskBundleSuggestion({id:"bundle-label-test",title:"Over/Under",slug:"bundle-label-test",negRisk:true,enableNegRisk:true,markets:[
|
||
{id:"over-a",question:"Over/Under 1.5",outcomes:'["Over","Under"]',outcomePrices:'["0.45","0.55"]',clobTokenIds:'["over-a","under-a"]',bestBid:0.44,bestAsk:0.45,liquidityNum:30000,acceptingOrders:true},
|
||
{id:"over-b",question:"Over/Under 2.5",outcomes:'["Over","Under"]',outcomePrices:'["0.45","0.55"]',clobTokenIds:'["over-b","under-b"]',bestBid:0.44,bestAsk:0.45,liquidityNum:30000,acceptingOrders:true},
|
||
]})===null;
|
||
const dominanceEvent={id:"dominance-test",title:"Acme FDV above ___ one day after launch?",slug:"acme-fdv",tags:[{slug:"crypto",label:"Crypto"}],markets:[
|
||
{id:"dominance-low",question:"Will Acme FDV be above $2B one day after launch?",outcomes:'["Yes","No"]',outcomePrices:'["0.065","0.935"]',clobTokenIds:'["dom-low-yes","dom-low-no"]',bestBid:0.064,bestAsk:0.069,liquidityNum:30000,volumeNum:100000,volume24hr:10000,spread:0.005,acceptingOrders:true},
|
||
{id:"dominance-high",question:"Will Acme FDV be above $3B one day after launch?",outcomes:'["Yes","No"]',outcomePrices:'["0.09","0.91"]',clobTokenIds:'["dom-high-yes","dom-high-no"]',bestBid:0.085,bestAsk:0.095,liquidityNum:28000,volumeNum:100000,volume24hr:10000,spread:0.01,acceptingOrders:true},
|
||
]};
|
||
const dominanceSuggestion=dominanceBundleSuggestions(dominanceEvent)[0];
|
||
const belowDominanceSuggestion=dominanceBundleSuggestions({id:"dominance-below",title:"River level below ___ on August 21?",slug:"river-level",markets:[
|
||
{id:"below-low",question:"Will the river level be below 10 feet on August 21?",outcomes:'["Yes","No"]',outcomePrices:'["0.42","0.58"]',clobTokenIds:'["below-low-yes","below-low-no"]',bestBid:0.41,bestAsk:0.43,liquidityNum:20000,acceptingOrders:true},
|
||
{id:"below-high",question:"Will the river level be below 20 feet on August 21?",outcomes:'["Yes","No"]',outcomePrices:'["0.38","0.62"]',clobTokenIds:'["below-high-yes","below-high-no"]',bestBid:0.37,bestAsk:0.39,liquidityNum:20000,acceptingOrders:true},
|
||
]})[0];
|
||
const mismatchedDominanceRejected=dominanceBundleSuggestions({id:"dominance-mismatch",title:"Mismatched terms",markets:[
|
||
Object.assign({},dominanceEvent.markets[0],{id:"mismatch-a",question:"Will Acme FDV be above $2B one day after launch?"}),
|
||
Object.assign({},dominanceEvent.markets[1],{id:"mismatch-b",question:"Will Acme FDV be above $3B seven days after launch?"}),
|
||
]}).length===0;
|
||
const expensiveDominanceEvent={id:"dominance-expensive",title:"Expensive pair",markets:[
|
||
Object.assign({},dominanceEvent.markets[0],{id:"expensive-a",bestAsk:0.50}),
|
||
Object.assign({},dominanceEvent.markets[1],{id:"expensive-b",bestBid:0.50,bestAsk:0.55}),
|
||
]};
|
||
const unprofitableDominanceRejected=dominanceBundleSuggestions(expensiveDominanceEvent).length===0;
|
||
const unprofitableDominanceAudited=dominanceBundleSuggestions(expensiveDominanceEvent,{includeUnprofitable:true})[0];
|
||
const nonBinaryDominanceRejected=dominanceBundleSuggestions({id:"dominance-labels",title:"Non-binary pair",markets:dominanceEvent.markets.map((row,index)=>Object.assign({},row,{id:`nonbinary-${index}`,outcomes:'["Over","Under"]'}))}).length===0;
|
||
const deadlineEvent={id:"deadline-test",title:"Acme launch deadline",slug:"acme-launch-deadline",tags:[{slug:"business",label:"Business"}],markets:[
|
||
{id:"deadline-early",question:"Will Acme launch by September 30, 2026?",outcomes:'["Yes","No"]',outcomePrices:'["0.595","0.405"]',clobTokenIds:'["deadline-early-yes","deadline-early-no"]',bestBid:0.60,bestAsk:0.61,liquidityNum:30000,volumeNum:100000,volume24hr:10000,spread:0.01,acceptingOrders:true},
|
||
{id:"deadline-late",question:"Will Acme launch by December 31, 2026?",outcomes:'["Yes","No"]',outcomePrices:'["0.545","0.455"]',clobTokenIds:'["deadline-late-yes","deadline-late-no"]',bestBid:0.54,bestAsk:0.55,liquidityNum:28000,volumeNum:100000,volume24hr:10000,spread:0.01,acceptingOrders:true},
|
||
]};
|
||
const deadlineSuggestion=dominanceBundleSuggestions(deadlineEvent)[0];
|
||
const mismatchedDeadlineRejected=dominanceBundleSuggestions({id:"deadline-mismatch",title:"Mismatched deadline terms",markets:[
|
||
Object.assign({},deadlineEvent.markets[0],{id:"deadline-mismatch-a",question:"Will Acme launch by September 30, 2026?"}),
|
||
Object.assign({},deadlineEvent.markets[1],{id:"deadline-mismatch-b",question:"Will Beta launch by December 31, 2026?"}),
|
||
]}).length===0;
|
||
const mixedDeadlineYearRejected=dominanceBundleSuggestions({id:"deadline-year-mismatch",title:"Mixed deadline years",markets:[
|
||
Object.assign({},deadlineEvent.markets[0],{id:"deadline-year-a",question:"Will Acme launch by September 30?"}),
|
||
Object.assign({},deadlineEvent.markets[1],{id:"deadline-year-b",question:"Will Acme launch by December 31, 2026?"}),
|
||
]}).length===0;
|
||
const unprofitableDeadlineRejected=dominanceBundleSuggestions({id:"deadline-expensive",title:"Expensive deadline pair",markets:[
|
||
Object.assign({},deadlineEvent.markets[0],{id:"deadline-expensive-a",bestBid:0.50}),
|
||
Object.assign({},deadlineEvent.markets[1],{id:"deadline-expensive-b",bestAsk:0.50}),
|
||
]}).length===0;
|
||
const invalidDeadlineRejected=parseDominanceDeadline("Will Acme launch by February 30, 2026?")===null;
|
||
[bundleSuggestion,yesBundleSuggestion,dominanceSuggestion,deadlineSuggestion].filter(Boolean).forEach(suggestion=>Object.assign(suggestion,{
|
||
depth_verified:true,fees_verified:true,fee_model:"verified-zero-fee",execution_model:"fixture-vwap",verification_status:"executable",execution_units:1000,
|
||
}));
|
||
const unverifiedBundleBook=defaultPortfolio(),unverifiedBundleOpen=openNegativeRiskBundle(unverifiedBundleBook,AGENTS.find(a=>a.id==="value"),
|
||
Object.assign({},bundleSuggestion,{depth_verified:false,verification_status:"top-of-book-estimate"}),100,null);
|
||
const depthCappedBundleBook=defaultPortfolio(),depthCappedBundle=Object.assign({},bundleSuggestion,{execution_units:50});
|
||
openNegativeRiskBundle(depthCappedBundleBook,AGENTS.find(a=>a.id==="value"),depthCappedBundle,400,null);
|
||
const dominanceBook=defaultPortfolio(),offlineDominanceSuggestion=prepareCycleSuggestions([dominanceSuggestion],"offline-cache",true)[0];
|
||
openPositions(dominanceBook,AGENTS.find(a=>a.id==="value"),[dominanceSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(dominanceBook),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const dominanceOpenedPositions=dominanceBook.positions.length,dominanceUnits=dominanceBook.positions[0]&&dominanceBook.positions[0].shares;
|
||
markToMarket(dominanceBook,{
|
||
"dominance-low":market({id:"dominance-low",yes_price:1,no_price:0,closed:true,accepting_orders:false}),
|
||
"dominance-high":market({id:"dominance-high",yes_price:1,no_price:0,closed:true,accepting_orders:false}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const deadlineBook=defaultPortfolio(),offlineDeadlineSuggestion=prepareCycleSuggestions([deadlineSuggestion],"offline-cache",true)[0];
|
||
openPositions(deadlineBook,AGENTS.find(a=>a.id==="value"),[deadlineSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(deadlineBook),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const deadlineOpenedPositions=deadlineBook.positions.length,deadlineUnits=deadlineBook.positions[0]&&deadlineBook.positions[0].shares;
|
||
markToMarket(deadlineBook,{
|
||
"deadline-early":market({id:"deadline-early",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
"deadline-late":market({id:"deadline-late",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const settleDeadlineCase=(earlyYes,lateYes)=>{
|
||
const book=defaultPortfolio();
|
||
openPositions(book,AGENTS.find(a=>a.id==="value"),[deadlineSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(book),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const units=book.positions[0]&&book.positions[0].shares;
|
||
markToMarket(book,{
|
||
"deadline-early":market({id:"deadline-early",yes_price:earlyYes,no_price:1-earlyYes,closed:true,accepting_orders:false}),
|
||
"deadline-late":market({id:"deadline-late",yes_price:lateYes,no_price:1-lateYes,closed:true,accepting_orders:false}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
return {book,units};
|
||
};
|
||
const deadlineEarlyCase=settleDeadlineCase(1,1),deadlineBetweenCase=settleDeadlineCase(0,1);
|
||
const bundleBook=defaultPortfolio();
|
||
const offlineBundleSuggestion=prepareCycleSuggestions([bundleSuggestion],"offline-cache",true)[0];
|
||
openPositions(bundleBook,AGENTS.find(a=>a.id==="value"),[bundleSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(bundleBook),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const bundleOpenedPositions=bundleBook.positions.length,bundleOpenDecision=JSON.parse(JSON.stringify(bundleBook.lastDecision||{}));
|
||
const bundleUnits=bundleBook.positions[0]&&bundleBook.positions[0].shares;
|
||
markToMarket(bundleBook,{
|
||
"bundle-a":market({id:"bundle-a",yes_price:0.8,no_price:0.2}),
|
||
"bundle-b":market({id:"bundle-b",yes_price:0.1,no_price:0.9}),
|
||
"bundle-draw":market({id:"bundle-draw",yes_price:0.1,no_price:0.9}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const bundleSurvivedIntermediateMove=bundleBook.positions.length===3&&bundleBook.positions.every(pos=>pos.shares===bundleUnits);
|
||
markToMarket(bundleBook,{
|
||
"bundle-a":market({id:"bundle-a",yes_price:1,no_price:0,closed:true,accepting_orders:false}),
|
||
"bundle-b":market({id:"bundle-b",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
"bundle-draw":market({id:"bundle-draw",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const yesBundleBook=defaultPortfolio();
|
||
openPositions(yesBundleBook,AGENTS.find(a=>a.id==="value"),[yesBundleSuggestion],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(yesBundleBook),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const yesBundleOpenedPositions=yesBundleBook.positions.length,yesBundleUnits=yesBundleBook.positions[0]&&yesBundleBook.positions[0].shares;
|
||
markToMarket(yesBundleBook,{
|
||
"yes-bundle-a":market({id:"yes-bundle-a",yes_price:0.05,no_price:0.95}),
|
||
"yes-bundle-b":market({id:"yes-bundle-b",yes_price:0.10,no_price:0.90}),
|
||
"yes-bundle-draw":market({id:"yes-bundle-draw",yes_price:0.85,no_price:0.15}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const yesBundleSurvivedIntermediateMove=yesBundleBook.positions.length===3&&yesBundleBook.positions.every(pos=>pos.shares===yesBundleUnits);
|
||
markToMarket(yesBundleBook,{
|
||
"yes-bundle-a":market({id:"yes-bundle-a",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
"yes-bundle-b":market({id:"yes-bundle-b",yes_price:0,no_price:1,closed:true,accepting_orders:false}),
|
||
"yes-bundle-draw":market({id:"yes-bundle-draw",yes_price:1,no_price:0,closed:true,accepting_orders:false}),
|
||
},AGENTS.find(a=>a.id==="value"),{policyExits:true,executeTrades:true});
|
||
const adaptiveRankFixture=rankBoardRows([
|
||
{c:{id:"legacy-leader"},eq:20000,enginePnl:-200,engineRet:-1},
|
||
{c:{id:"adaptive-leader"},eq:9000,enginePnl:200,engineRet:2},
|
||
]);
|
||
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:1000,current_price:0.5,entry_price:0.5,cost:500,value:500,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);
|
||
const gapMock={agents:{}};AGENTS.forEach(a=>gapMock.agents[a.id]=defaultPortfolio());
|
||
["reversal","conviction"].forEach((id,i)=>gapMock.agents[id].positions.push({market_id:"gap-overlap-test",question:"Gap overlap test",side:"NO",category:"Crypto",signal_type:"reversal",
|
||
shares:2000,current_price:0.21,entry_price:0.21,cost:420,value:420,original_shares:2000,original_cost:420,unrealized_pnl:0,conviction:75-i,opened_at:hoursAgo(24)}));
|
||
reduceStrategyOverlap(gapMock);
|
||
const gapOverlapRemaining=AGENTS.reduce((sum,a)=>sum+gapMock.agents[a.id].positions.filter(p=>p.market_id==="gap-overlap-test").length,0);
|
||
let explorationMarketId="single-explorer-0",explorationIndex=0,explorationOwners=[];
|
||
while(!explorationOwners.length){
|
||
explorationOwners=AGENTS.filter(a=>stableExploration(a.id,explorationMarketId));
|
||
if(!explorationOwners.length)explorationMarketId=`single-explorer-${++explorationIndex}`;
|
||
}
|
||
const explorerReversal=learnedOpportunity(explorationOwners[0],defaultPortfolio(),{market_id:explorationMarketId,signal_type:"reversal",quality:"reversal",category:"Politics",side:"NO",entry_price:0.42});
|
||
mock.agents.tailalpha.positions.push({market_id:"runner-test",question:"Runner test",side:"YES",shares:100,current_price:0.5,entry_price:0.5,cost:50,value:50,unrealized_pnl:0,conviction:70,opened_at:hoursAgo(24)});
|
||
const immaterialRunnerDoesNotBlock=!occupiedStrategyMarkets(mock).has("runner-test");
|
||
const rejectBook=defaultPortfolio();rejectBook.cash=9900;
|
||
rejectBook.positions.push({market_id:trend.market_id,question:trend.question,side:trend.side,shares:100/Number(trend.entry_price),current_price:trend.entry_price,entry_price:trend.entry_price,cost:100,value:100,unrealized_pnl:0,opened_at:hoursAgo(24)});
|
||
openPositions(rejectBook,AGENTS[0],[promotedTrendSuggestion],"All",{minConv:0,maxNew:1,maxFrac:0.04,reserve:0.1,targetExposure:0.6,learning:buildAdaptiveProfile(rejectBook),marketLearning:stablePositiveProfile},new Set(),{});
|
||
const rejectionAccounting=Number(rejectBook.lastDecision&&rejectBook.lastDecision.rejectionCounts&&rejectBook.lastDecision.rejectionCounts.already_held||0)===1;
|
||
const convictionCfg=AGENTS.find(a=>a.id==="conviction"),convictionCapacityCoversTarget=convictionCfg.maxPositions*MAX_AGGRESSIVE_TRADE_LOSS_PCT>=convictionCfg.targetExposure;
|
||
const historyFixture={"2026-01-01":0.40,"2026-01-07":0.50,"2026-01-08":0.55,"2026-01-09":0.90};
|
||
const historicalFeatures=historicalPriceFeatures(historyFixture,"2026-01-08",0.55);
|
||
const backtestNoLookahead=priceOnDay({"2026-01-09":0.90},"2026-01-08",null)===null
|
||
&&historicalFeatures.price_change_1h===0&&historicalFeatures.price_change_1d===0.05&&historicalFeatures.price_change_1w===0.15;
|
||
const markOnlyBook=defaultPortfolio();markOnlyBook.cash=9500;
|
||
markOnlyBook.positions=[{market_id:"offline-mark",question:"Offline mark",side:"YES",shares:1000,entry_price:0.5,current_price:0.5,cost:500,value:500,
|
||
original_shares:1000,original_cost:500,unrealized_pnl:0,opened_at:hoursAgo(48),strategy_version:SUGGESTION_ENGINE_VERSION,quality:"confirmed",signal_type:"trend",gain_stops:{},stop_losses:{}}];
|
||
markToMarket(markOnlyBook,{"offline-mark":market({id:"offline-mark",yes_price:0.8,no_price:0.2})},AGENTS[0],{policyExits:false,executeTrades:false});
|
||
const executableBook=JSON.parse(JSON.stringify(markOnlyBook));
|
||
delete executableBook.positions[0].price_status;
|
||
markToMarket(executableBook,{"offline-mark":market({id:"offline-mark",yes_price:0.8,no_price:0.2})},AGENTS[0],{policyExits:true,executeTrades:true});
|
||
const staleEntryBook=defaultPortfolio(),staleEntryAgent=AGENTS[0];
|
||
openPositions(staleEntryBook,staleEntryAgent,[Object.assign({},trend,{trade_ready:false,entry_candidate:false,watch_only:true})],"All",{
|
||
minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(staleEntryBook),marketLearning:{samples:0,pending:0,buckets:{}},
|
||
},new Set(),{});
|
||
const buildMigrationState=defaultState();
|
||
buildMigrationState.engine_version=58;buildMigrationState.strategy_version=SUGGESTION_ENGINE_VERSION;
|
||
buildMigrationState.agents.value.engine_baseline={version:SUGGESTION_ENGINE_VERSION,started_at:hoursAgo(2),equity:9876.54};
|
||
buildMigrationState.agents.catalyst.cash=9900;
|
||
buildMigrationState.agents.catalyst.positions=[{market_id:"migration-shock",signal_type:"shock-fade-pilot",shock_strategy_version:SHOCK_FADE_STRATEGY_VERSION,
|
||
cost:100,value:96,shares:600,current_price:0.16,target_exit_at:"2026-08-22T09:22:14.606Z"}];
|
||
reconcileStateVersions(buildMigrationState);
|
||
const strategyMigrationState=defaultState();
|
||
strategyMigrationState.engine_version=58;strategyMigrationState.strategy_version=PREVIOUS_STRATEGY_VERSION;
|
||
strategyMigrationState.agents.value.cash=9876.54;
|
||
strategyMigrationState.agents.value.engine_baseline={version:PREVIOUS_STRATEGY_VERSION,started_at:hoursAgo(2),equity:10000};
|
||
reconcileStateVersions(strategyMigrationState);
|
||
const chatMigrationFixture={reversal:[{role:"agent",text:"I am looking for a crowd move that has gone too far. I am Reversal Edge, sitting at -1.00% with $9,900.00 equity. Talk to me normally."}]};
|
||
migrateAgentChatIdentity(chatMigrationFixture);
|
||
const oldCountReport=decisionSummary({lastDecision:{mode:"Test",reason:"test",maxNew:1,maxFrac:0.03,
|
||
marketLearning:{samples:152,current_samples:127,pending:300,buckets:{}}}});
|
||
const freshCountReport=decisionSummary({lastDecision:{mode:"Test",reason:"test",maxNew:1,maxFrac:0.03,
|
||
marketLearning:calibrationDecisionRecord(calibrationProfile)}});
|
||
const runtimeSnapshot=buildPublicRuntimeSnapshot(),runtimeKeys=Object.keys(runtimeSnapshot.items).sort();
|
||
return {buildVersion:BUILD_VERSION,version:SUGGESTION_ENGINE_VERSION,
|
||
autonomousRuntime:{exactPublicKeys:runtimeKeys.join(",")===[AGENTS_KEY,SUG_KEY].sort().join(","),
|
||
excludesPrivateKeys:[PAPER_KEY,EMAIL_ALERT_KEY,INVEST_KEY,LIVE_KEY,CHAT_KEY,PAID_AGENT_CHAT_KEY].every(key=>!runtimeKeys.includes(key)),
|
||
suggestionLimit:PUBLIC_RUNTIME_LIMITS.suggestions,
|
||
readOnlyClientsDoNotAdvance:autonomousRuntimeControlsCycle({read_only:true,source:"github-actions"},false),
|
||
headlessRunnerCanAdvance:!autonomousRuntimeControlsCycle({read_only:true,source:"github-actions"},true)},
|
||
trend:{ready:trend.trade_ready,quality:trend.quality,side:trend.side,margin:trend.net_edge},
|
||
exactFeeLearning:{feeFreeUsesSlippageOnly:feeFreeSignalCost.cost===0.005,
|
||
appliesEntryAndExitFeeCurve:Math.abs(exactFeeSignalCost.cost-0.0393)<0.0000001,
|
||
unknownScheduleUsesConservativeFallback:unknownFeeSignalCost.cost===0.045,
|
||
normalizedFeeScheduleSurvivesCache:compactCachedMarket(market({fees_enabled:true,fee_schedule:{rate:0.07,exponent:1}})).fee_schedule.rate===0.07},
|
||
auditedTrendCategories:{sportsObservationOnly:!sportsTrend.trade_ready&&sportsTrend.signal_type==="trend",cryptoObservationOnly:!cryptoTrend.trade_ready&&cryptoTrend.signal_type==="trend"},
|
||
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),
|
||
adaptation:{samples:learningProfile.samples,trendMultiplier:learnedTrend.multiplier,reversalMultiplier:learnedReversal.multiplier,learnsDirection:learnedTrend.multiplier>learnedReversal.multiplier,
|
||
calibrationSamples:calibrationProfile.samples,trendMarketScore:learnedTrend.market_score,reversalMarketScore:learnedReversal.market_score,
|
||
legacyTradesCannotPromotePersonalCohort:legacyOnlyOpportunity.personal_state==="observing"
|
||
&&Object.keys(learningProfile.current_buckets||{}).length===0,
|
||
currentTradesCanPromotePersonalCohort:currentPositiveOpportunity.personal_state==="promoted"
|
||
&¤tPositiveProfile.current_samples===8&&Object.keys(currentPositiveProfile.current_buckets||{}).length>=5,
|
||
currentCapitalLearningSurvivesOfflineCompaction:offlineCurrentProfile.current_samples===currentPositiveProfile.current_samples
|
||
&&offlineCurrentProfile.current_buckets["signal:catalyst"].score===currentPositiveProfile.current_buckets["signal:catalyst"].score,
|
||
legacyTradesCannotIncreaseCurrentSizing:legacyPositiveDecision.maxFrac===0.045,
|
||
currentProfitsCanIncreaseBoundedSizing:currentPositiveDecision.maxFrac>legacyPositiveDecision.maxFrac
|
||
&¤tPositiveDecision.maxFrac<=0.06,
|
||
calibrationReportsIndependentCounts:calibrationDecisionFixture.marketLearning.samples===96
|
||
&&calibrationDecisionFixture.marketLearning.markets===32&&calibrationDecisionFixture.marketLearning.events===32
|
||
&&calibrationDecisionFixture.marketLearning.current_samples===96&&calibrationDecisionFixture.marketLearning.current_events===32,
|
||
freshCalibrationReportShowsCounts:freshCountReport.includes("across 32 event clusters / 32 markets")
|
||
&&freshCountReport.includes("96 observations / 32 events"),
|
||
oldCalibrationReportDoesNotInventZeroCounts:oldCountReport.includes("counts unavailable")
|
||
&&!oldCountReport.includes("across 0 event clusters"),
|
||
broadTrendStartsObservationOnly:!trend.trade_ready&&trend.entry_candidate&&trend.audited_observation_only,
|
||
recentProofCanUnlockTrend:promotedTrendSuggestion.trade_ready&&promotedTrendSuggestion.adaptive_promotion,
|
||
replayLosingFavoriteTrendCannotPromote:!blockedFavoriteTrendPromotion.trade_ready
|
||
&&!blockedFavoriteTrendOpportunity.allowed&&blockedFavoriteTrendOpportunity.blocked_by==="historical",
|
||
observationsRecordStrategyEligibility:Array.isArray(observationLedgerState.signal_ledger.pending[0].eligible_agent_ids)
|
||
&&observationLedgerState.signal_ledger.pending[0].eligible_agent_ids.join(",")==="value,longshot,diversifier,reversal,tailalpha",
|
||
researchEligibilityIsBroaderThanCapitalEntry:agentObservesSuggestion(agentById("value"),{side:"YES",entry_price:0.42,signal_type:"trend",quality:"watch",
|
||
signal_confidence:0.62,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45})
|
||
&&!agentAcceptsSuggestion(agentById("value"),{side:"YES",entry_price:0.42,signal_type:"trend",quality:"watch",
|
||
signal_confidence:0.62,trade_ready:false,jump_risk:false,category:"Politics",days_to_resolution:45}),
|
||
nonDirectionalSystemsStayOutOfDirectionalLedger:nonDirectionalLedgerState.signal_ledger.pending.length===0,
|
||
unassignedMigrationRecordDoesNotBlockReplacement:unassignedReplacementState.signal_ledger.pending.length===2
|
||
&&unassignedReplacementState.signal_ledger.pending.some(item=>item.agent_learning_version===AGENT_LEARNING_VERSION
|
||
&&Array.isArray(item.eligible_agent_ids)&&item.eligible_agent_ids.length>=5),
|
||
agentCalibrationFiltersOutcomes:momentumScopedProfile.samples===72&&momentumScopedProfile.events===24
|
||
&&momentumScopedProfile.pending===1&&valueScopedProfile.samples===0&&valueScopedProfile.pending===1,
|
||
calibrationReportsQueueComposition:momentumScopedProfile.pending_global===2&&momentumScopedProfile.pending_legacy===0
|
||
&&momentumScopedProfile.pending_scoped===2&&momentumScopedProfile.pending_agent_scoped===1&&momentumScopedProfile.pending_unassigned===0,
|
||
agentCanPromoteWithoutPromotingPeers:momentumPreparedSuggestion.trade_ready&&!valuePreparedSuggestion.trade_ready,
|
||
aggregatePromotionNamesEligibleAgent:agentScopedAggregate.trade_ready
|
||
&&agentScopedAggregate.promoted_for_agents.length===1&&agentScopedAggregate.promoted_for_agents[0]==="momentum",
|
||
strategyScopesSurviveCompaction:compactedScopedState.signal_ledger.pending[0].eligible_agent_ids[0]==="momentum"
|
||
&&compactedScopedState.signal_ledger.outcomes[0].eligible_agent_ids[0]==="momentum"
|
||
&&compactedScopedSuggestion.promoted_for_agents[0]==="momentum",
|
||
offlineCacheRebuildsAgentPromotion:offlineScopedMomentum.trade_ready&&!offlineScopedValue.trade_ready,
|
||
losingCohortDemotesOnlyEligibleAgent:losingValueState.state==="demoted"&&!losingValueState.allowed
|
||
&&unrelatedMomentumProfile.samples===0&&unrelatedMomentumProfile.current_samples===0,
|
||
legacyUnscopedEvidenceRemainsReadable:buildSignalCalibration(legacyUnscopedLedger,"momentum").samples===1
|
||
&&buildSignalCalibration(legacyUnscopedLedger,"value").samples===1,
|
||
explicitEmptyScopeDoesNotLeak:buildSignalCalibration(explicitEmptyScopedLedger,"momentum").samples===0
|
||
&&buildSignalCalibration(explicitEmptyScopedLedger,"value").samples===0,
|
||
historicalPriorBlocksReversal:!priorReversal.allowed&&priorReversal.blocked_by==="historical",
|
||
reversalRemainsBlockedForExplorer:!explorerReversal.allowed&&explorerReversal.blocked_by==="historical",
|
||
recentProofCanUnlockReversal:promotedReversal.allowed&&promotedReversal.market_state==="promoted"&&promotedReversalSuggestion.trade_ready,
|
||
promotedReversalOpensBoundedPosition:promotedOpenBook.positions.length===1&&promotedOpenBook.positions[0].cost<=300,
|
||
hardBlockedCryptoCannotSelfPromote:!blockedCryptoPromotion.trade_ready,
|
||
enduranceReplacesReversal:agentAcceptsSuggestion(AGENTS.find(a=>a.id==="reversal"),trend)&&!agentAcceptsSuggestion(AGENTS.find(a=>a.id==="reversal"),reversal),
|
||
historicalPriorSizesRisk:!priorCryptoLongshot.blocked&&priorCryptoLongshot.multiplier<1&&priorCryptoLongshot.features.length===2,
|
||
historicalPriorBlocksSportsTrend:!priorSportsTrend.allowed&&priorSportsTrend.blocked_by==="historical",
|
||
historicalPriorBlocksCryptoTrend:!priorCryptoTrend.allowed&&priorCryptoTrend.blocked_by==="historical",
|
||
historicalPriorBlocksUnprovenPoliticsTrend:priorPoliticsTrend.blocked&&priorPoliticsTrend.requiresPromotion,
|
||
blocksShortDatedNo:!shortNoPrior.allowed&&shortNoPrior.blocked_by==="historical",
|
||
recentProofCanUnlockShortDatedNo:promotedShortNo.allowed&&promotedShortNo.market_state==="promoted",
|
||
blocksLongDatedNoWithoutProof:longNoPrior.blocked&&longNoPrior.requiresPromotion,
|
||
observesConfirmedSignalsWithoutTrading:observationLedgerState.signal_ledger.pending.length===1
|
||
&&observationLedgerState.signal_ledger.pending[0].trade_ready_at_observation===false
|
||
&&observationLedgerState.signal_ledger.pending[0].signal_policy_version===DIRECTIONAL_SIGNAL_POLICY_VERSION,
|
||
deduplicatesPendingMarketSide:observationLedgerState.signal_ledger.pending.length===1,
|
||
priorFeePolicyDoesNotDelayReplacement:priorPolicyReplacementState.signal_ledger.pending.length===1
|
||
&&priorPolicyReplacementState.signal_ledger.pending.some(item=>item.signal_policy_version===DIRECTIONAL_SIGNAL_POLICY_VERSION),
|
||
oldestPendingSignalsSurviveCapacity:queueLedgerState.signal_ledger.pending.length===SIGNAL_LEDGER_PENDING_LIMIT
|
||
&&queueLedgerState.signal_ledger.pending.some(x=>x.market_id==="old-market-0")
|
||
&&queueLedgerState.signal_ledger.pending.some(x=>x.market_id==="new-market-0")
|
||
&&!queueLedgerState.signal_ledger.pending.some(x=>x.market_id==="new-market-1"),
|
||
underObservedCohortsAndEventsSampleFirst:coveragePriority.map(x=>x.market_id).join(",")==="new-sports,new-politics,popular-repeat",
|
||
firstHorizonGradesWithoutLookahead:ledgerState.signal_ledger.pending.length===1&&ledgerState.signal_ledger.pending[0].graded_horizons.includes(24)
|
||
&&ledgerState.signal_ledger.outcomes.length===1&&ledgerState.signal_ledger.outcomes[0].target_horizon_hours===24&&ledgerState.signal_ledger.outcomes[0].return===0.2375,
|
||
secondHorizonGradesAndCompletes:secondHorizonState.signal_ledger.pending.length===0&&secondHorizonState.signal_ledger.outcomes.length===1
|
||
&&secondHorizonState.signal_ledger.outcomes[0].target_horizon_hours===72,
|
||
holdsSignalsUntilPolicyHorizon:earlyLedgerState.signal_ledger.pending.length===1&&earlyLedgerState.signal_ledger.outcomes.length===0,
|
||
missed24hWindowIsNotBackfilled:missedFirstWindowState.signal_ledger.pending.length===1&&missedFirstWindowState.signal_ledger.outcomes.length===0,
|
||
fetchesMaturedMarketsOutsideActiveScan:dueFetchIds.length===1&&dueFetchIds[0]==="outside-active-scan",
|
||
keepsUnavailableGradesQueued:retryLedgerState.signal_ledger.pending.length===1&&retryLedgerState.signal_ledger.outcomes.length===0,
|
||
expiresOnlyAfterRetryWindow:expiredLedgerState.signal_ledger.pending.length===0&&expiredLedgerState.signal_ledger.expired_ungraded===3,
|
||
ledgerIsNetOfCosts:ledgerState.signal_ledger.outcomes[0].gross_return===0.25&&ledgerState.signal_ledger.outcomes[0].estimated_cost_return===0.0125,
|
||
independentCalibrationConfidence:singleCalibration.confidence<0.06,
|
||
repeatedSnapshotsCountAsOneMarket:repeatedMarketCalibration.markets===1&&repeatedMarketCalibration.buckets["signal:trend"].samples===1
|
||
&&repeatedMarketCalibration.buckets["signal:trend"].observations===72&&repeatedMarketState.state==="observing",
|
||
correlatedMarketsCountAsOneEvent:correlatedEventCalibration.markets===24&&correlatedEventCalibration.events===1
|
||
&&correlatedEventCalibration.buckets["signal:trend"].samples===1&&correlatedEventState.state==="observing",
|
||
stablePositiveEventCount:stablePositiveProfile.events,
|
||
stablePositiveMarketCount:stablePositiveProfile.markets,
|
||
distinctEventsCanPromote:stablePositiveProfile.events===24&&stablePositiveCalibration.state==="promoted"&&stablePositiveCalibration.supporting_features>=2,
|
||
sixHourPositiveEvidenceCannotPromote:earlyPositiveCalibration.state==="observing"&&!earlyPositiveCalibration.promoted,
|
||
sixHourNegativeEvidenceCanDemote:earlyNegativeCalibration.state==="demoted"&&!earlyNegativeCalibration.allowed,
|
||
oneHorizonCannotPromote:oneHorizonPositiveCalibration.state==="observing"&&!oneHorizonPositiveCalibration.promoted,
|
||
compatiblePreviousStrategyCanPromote:compatiblePreviousCalibration.state==="promoted"&&compatiblePreviousCalibration.promoted,
|
||
incompatiblePreviousPolicyCannotPromote:incompatiblePreviousCalibration.state==="observing"&&!incompatiblePreviousCalibration.promoted,
|
||
legacyBuildLineageRemainsHistorical:normalizedStrategyVersion(41)===40,
|
||
compatiblePreviousStrategyKeepsFullWeight:legacyBuildCalibration.current_samples===1&&legacyBuildCalibration.buckets["signal:trend"].weight>0.99,
|
||
currentStrategyKeepsFullWeight:currentStrategyCalibration.current_samples===1&¤tStrategyCalibration.buckets["signal:trend"].weight>0.99,
|
||
baselineSurvivesBuildMigration:buildMigrationState.agents.value.engine_baseline.equity===9876.54&&buildMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
|
||
buildMetadataAdvancesWithoutStrategyReset:buildMigrationState.engine_version===BUILD_VERSION&&buildMigrationState.strategy_version===SUGGESTION_ENGINE_VERSION,
|
||
buildMigrationPreservesOpenShockTrade:buildMigrationState.agents.catalyst.positions.length===1
|
||
&&buildMigrationState.agents.catalyst.positions[0].target_exit_at==="2026-08-22T09:22:14.606Z",
|
||
strategyChangeStartsCleanBaseline:strategyMigrationState.agents.value.engine_baseline.equity===9876.54&&strategyMigrationState.agents.value.engine_baseline.version===SUGGESTION_ENGINE_VERSION,
|
||
singleOutcomeStaysObserving:singleCalibration.state==="observing"&&singleCalibration.multiplier<=1.01,
|
||
stablePositiveIsPromoted:stablePositiveCalibration.state==="promoted"&&stablePositiveCalibration.multiplier>1.02&&stablePositiveCalibration.allowed,
|
||
stableNegativeIsBlocked:stableNegativeCalibration.state==="demoted"&&!stableNegativeCalibration.allowed,
|
||
noisyEvidenceStaysNeutral:noisyCalibration.state==="observing"&&noisyCalibration.multiplier>=0.99&&noisyCalibration.multiplier<=1.01,
|
||
blocksConfirmedLosingRegime:!lossOpportunity.allowed,containmentMode:lossDecision.mode,containmentMaxNew:lossDecision.maxNew,
|
||
urgencyCannotIncreaseSize:urgencyWithoutEvidenceMultiplier===1&&urgencyWithOnePromotionMultiplier===1,
|
||
dualIndependentPromotionAllowsBoundedPeerBoost:dualPromotionMultiplier===1.05,
|
||
legacyLossDoesNotFreezeCurrentEngine:legacyLossDecision.mode!=="Loss Regime Containment"},
|
||
resolutionWeekNoPilot:{
|
||
identifiesFixedDateNoFavorite:Boolean(resolutionWeekSuggestion&&!resolutionWeekSuggestion.trade_ready&&resolutionWeekSuggestion.watch_only
|
||
&&resolutionWeekSuggestion.side==="NO"&&resolutionWeekSuggestion.entry_price===0.5255),
|
||
keepsOnlyCorrectedFourDayDiscoveryWindow:resolutionWeekSuggestion.target_horizon_days===4
|
||
&&unvalidatedThreeDayResolution===null&&unvalidatedFiveDayResolution===null,
|
||
usesExecutableNoAskPlusSlippage:Boolean(resolutionWeekSuggestion&&resolutionWeekSuggestion.market_price===0.52
|
||
&&resolutionWeekSuggestion.friction===0.0055),
|
||
rejectsPathDependentBarrier:pathDependentResolutionWeek===null,
|
||
rejectsSportsMetadata:sportsResolutionWeek===null,
|
||
onlyAggressiveAdoptersAccept:Boolean(resolutionWeekSuggestion&&agentAcceptsSuggestion(resolutionAgent,resolutionWeekSuggestion)
|
||
&&!agentAcceptsSuggestion(AGENTS.find(agent=>agent.id==="value"),resolutionWeekSuggestion)),
|
||
correctedAuditStartsAtZeroCapital:!initialResolutionProfile.allowed&&!initialResolutionProfile.demoted
|
||
&&initialResolutionProfile.position_pct===0,
|
||
stagesOneZeroCapitalObservation:resolutionShadowStage.staged===1&&resolutionShadowBook.cash===resolutionShadowCash,
|
||
tradingPauseDoesNotGrade:pausedResolutionGrade.completed===0,
|
||
verifiedSettlementGradesObservation:settledResolutionGrade.completed===1&&resolutionShadowBook.resolution_week_no_outcomes.length===1,
|
||
offlineSnapshotCannotInventSettlement:offlineResolutionGrade.completed===0&&offlineResolutionBook.resolution_week_no_shadows.length===1,
|
||
forwardLearnerSurvivesCompaction:compactedResolutionBook.resolution_week_no_shadows.length===1,
|
||
fortyPositiveEventsPromoteHalfPercent:promotedResolutionProfile.promoted&&promotedResolutionProfile.allowed
|
||
&&promotedResolutionProfile.position_pct===RESOLUTION_WEEK_NO_PROMOTED_POSITION_PCT,
|
||
opensHalfPercentPositionAfterPromotion:resolutionCost>=49.99&&resolutionCost<=STARTING_BALANCE*RESOLUTION_WEEK_NO_PROMOTED_POSITION_PCT+0.01,
|
||
interimDrawdownDoesNotTriggerStop:resolutionIgnoresInterimStop,
|
||
closesOnlyAfterVerifiedSettlement:resolutionBook.positions.length===0&&resolutionBook.closed.length===1
|
||
&&resolutionBook.closed[0].close_reason==="Resolution-window contract settled",
|
||
losingForwardCohortDisablesAllAdopters:losingResolutionProfile.demoted&&!losingResolutionProfile.allowed
|
||
&&losingResolutionProfile.position_pct===0,
|
||
sharedEvidenceDeduplicatesEvent:duplicateResolutionProfile.events===1,
|
||
globalEventClaimsDeduplicateOwner:claimedResolutionKeys.size===1&&claimedResolutionKeys.has("duplicate-resolution-event"),
|
||
activeLegacyPositionBlocksNewEvidence:legacyResolutionClaimKeys.has("legacy-active-resolution"),
|
||
},
|
||
sportsFavoritePilot:{
|
||
identifiesBoundedContestNoPilot:Boolean(sportsPilot&&sportsPilot.trade_ready&&!sportsPilot.audited_observation_only
|
||
&&sportsPilot.quality==="sports-contest-no-pilot"&&sportsPilot.side==="NO"),
|
||
derivesStableDatedContestKey:Boolean(sportsPilot&&sportsPilot.event_key==="sports:harbor-city-2026-08-24"),
|
||
keepsOnlyHighestNoPerContest:contestCandidates.length===1&&contestCandidates[0].market_id==="contest-high-no",
|
||
usesExecutableAskPlusSlippage:Boolean(sportsPilot&&sportsPilot.market_price===0.28&&sportsPilot.entry_price===0.2875&&sportsPilot.friction===0.0075),
|
||
derivesNoAskFromYesBid:Boolean(noSportsPilot&&noSportsPilot.side==="NO"&&noSportsPilot.market_price===0.70
|
||
&&noSportsPilot.entry_price===0.7075&&noSportsPilot.friction===0.0075),
|
||
includesExactGammaEntryFee:Boolean(feeSportsPilot&&feeSportsPilot.entry_fee>0.017&&feeSportsPilot.entry_price>0.524),
|
||
rejectsUnknownFeeSchedule:expensiveSportsPilot===null,
|
||
onlyFavoriteAgentAccepts:Boolean(sportsPilot&&agentAcceptsSuggestion(favoriteAgent,sportsPilot)
|
||
&&!agentAcceptsSuggestion(AGENTS.find(agent=>agent.id==="momentum"),sportsPilot)),
|
||
stagesOneShadowPerEvent:sportsShadowStage.staged===1&&sportsPilotBook.sports_favorite_outcomes.length===1,
|
||
shadowNeverChangesCash:sportsPilotBook.cash===sportsPilotCash&&sportsPilotBook.positions.length===0,
|
||
tradingPauseDoesNotGrade:pausedSportsGrade.completed===0,
|
||
closedMarketGradesOutcome:settledSportsGrade.completed===1&&sportsPilotBook.sports_favorite_outcomes.length===1
|
||
&&sportsPilotBook.sports_favorite_outcomes[0].net_return>0,
|
||
offlineSnapshotCannotInventSettlement:offlineSportsStayedPending,
|
||
forwardLearnerSurvivesCompaction:compactedSportsBook.sports_favorite_shadows.length===1&&compactedSportsBook.sports_favorite_outcomes.length===1,
|
||
freshOfflineSnapshotKeepsBoundedEntry:freshOfflineSportsPilot.trade_ready&&freshOfflineSportsPilot.entry_candidate,
|
||
offlineTimingIsRechecked:!expiredOfflineSportsPilot.trade_ready&&!expiredOfflineSportsPilot.entry_candidate&&expiredOfflineSportsPilot.watch_only
|
||
&&expiredOfflineSportsPilot.hours_to_start<SPORTS_FAVORITE_TARGET_LEAD_HOURS-SPORTS_FAVORITE_LEAD_TOLERANCE_HOURS,
|
||
completedLossesKeepCapitalDisabled:losingSportsPilotProfile.demoted&&!losingSportsPilotProfile.allowed&&losingSportsPilotProfile.position_pct===0,
|
||
initialExplorationStartsHalfPercent:initialSportsPilotProfile.allowed&&!initialSportsPilotProfile.promoted
|
||
&&initialSportsPilotProfile.position_pct===SPORTS_FAVORITE_PILOT_POSITION_PCT,
|
||
losingSuggestionReturnsToWatch:!unpromotedSportsSuggestion.trade_ready&&unpromotedSportsSuggestion.signal_type==="sports-contest-no-pilot",
|
||
positiveConfidenceCanPromote:promotedSportsPilotProfile.promoted&&promotedSportsPilotProfile.allowed
|
||
&&promotedSportsPilotProfile.position_pct===SPORTS_FAVORITE_PROMOTED_POSITION_PCT,
|
||
promotedSuggestionBecomesTradeReady:promotedSportsSuggestion.trade_ready&&promotedSportsSuggestion.signal_type==="sports-contest-no-pilot",
|
||
promotedPilotOpensOneCappedEvent:promotedSportsPilotBook.positions.length===1
|
||
&&promotedSportsPilotBook.positions[0].event_key==="sports:harbor-city-2026-08-24"
|
||
&&promotedSportsPilotBook.positions[0].cost<=STARTING_BALANCE*SPORTS_FAVORITE_PROMOTED_POSITION_PCT+0.01,
|
||
ordinaryGapExitDoesNotPreemptPilot:sportsPilotExit===null,
|
||
},
|
||
shockFadePilot:{
|
||
detectsAcceleratingThreeHourShock:Boolean(shockSuggestion&&shockSuggestion.side==="NO"&&shockSuggestion.shock_move_3h===0.12
|
||
&&shockSuggestion.shock_move_1h===0.06&&shockSuggestion.shock_prior_move_1h===0.03),
|
||
handlesDownwardShockSymmetrically:Boolean(downwardShock&&downwardShock.side==="YES"&&downwardShock.entry_price===0.1845
|
||
&&downwardShock.shock_move_3h===-0.12),
|
||
usesExecutableAskPlusSlippage:Boolean(shockSuggestion&&shockSuggestion.market_price===0.18&&shockSuggestion.entry_price===0.1845&&shockSuggestion.friction===0.0045),
|
||
rejectsOpposedLastHour:opposedShock===null,
|
||
rejectsDeceleratingLastHour:deceleratingShock===null,
|
||
rejectsMoreThanOneCentEntryFriction:costlyShock===null,
|
||
rejectsSubEightCentEntry:extremeShock===null,
|
||
rejectsPathDependentBarrier:barrierShock===null,
|
||
rejectsExactNumericRange:rangeShock===null,
|
||
requiresCompleteExitRunway:shortWindowShock===null,
|
||
preservesDatedLevelWithRunway:Boolean(fixedDateShock&&fixedDateShock.side==="NO"),
|
||
startsWatchOnly:Boolean(shockSuggestion&&!shockSuggestion.trade_ready&&shockSuggestion.audited_observation_only&&shockSuggestion.requires_live),
|
||
failedIndependentAuditStartsAtZeroCapital:!initialShockProfile.allowed&&!initialShockProfile.qualified&&!initialShockSuggestion.trade_ready
|
||
&&initialShockProfile.position_pct===0&&initialShockBook.positions.length===0&&initialShockBook.cash===STARTING_BALANCE,
|
||
unprovenLaneStillCollectsShadowEvidence:initialShadowStage.staged===1&&initialShockBook.shock_fade_shadows.length===1,
|
||
fortyPositiveEventsQualifyOnePercent:qualifiedShockProfile.allowed&&qualifiedShockProfile.qualified&&!qualifiedShockProfile.promoted
|
||
&&qualifiedShockProfile.position_pct===SHOCK_FADE_INITIAL_POSITION_PCT&&qualifiedShockSuggestion.trade_ready
|
||
&&qualifiedCapitalBook.positions.length===1&&qualifiedCapitalBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_INITIAL_POSITION_PCT+0.01,
|
||
capitalPositionPreventsDuplicateShadow:capitalPositionBlocksShadow.staged===0&&qualifiedCapitalBook.shock_fade_shadows.length===0,
|
||
fiveAggressiveAgentsCanAdoptPilot:qualifiedShockSuggestion&&[...SHOCK_FADE_ADOPTER_IDS].every(id=>agentAcceptsSuggestion(AGENTS.find(agent=>agent.id===id),qualifiedShockSuggestion))
|
||
&&!agentAcceptsSuggestion(AGENTS.find(agent=>agent.id==="momentum"),qualifiedShockSuggestion),
|
||
adopterFallbackRanksValidatedLane:rankSuggestionsForAgent(catalystShockAgent,[qualifiedShockSuggestion])[0]===qualifiedShockSuggestion,
|
||
capsEachAdopterAtTwoNewShockTrades:adopterShockBook.positions.length===SHOCK_FADE_MAX_NEW_PER_AGENT_CYCLE,
|
||
oneCapitalPositionPerEventWithinAgent:sameEventShockBook.positions.length===1,
|
||
oneActivePositionPerUnderlyingRisk:correlatedShockBook.positions.length===2
|
||
&&correlatedShockBook.positions.filter(row=>shockFadeRiskKey(row)==="crypto:ethereum").length===1
|
||
&&correlatedShockBook.positions.filter(row=>shockFadeRiskKey(row)==="crypto:bitcoin").length===1,
|
||
globalRiskClaimsBlockCorrelatedCrypto:shockFadeClaimKeys(ethShockB).some(key=>claimedRiskKeys.has(key))
|
||
&&!shockFadeClaimKeys(btcShock).some(key=>claimedRiskKeys.has(key)),
|
||
legacyActiveRiskStillBlocksCurrentStrategy:shockFadeClaimKeys(ethShockB).some(key=>claimedRiskKeys.has(key)),
|
||
crossAgentEventPreventsDuplicateShadow:crossAgentShadowBlock.staged===0&&crossAgentShadowBook.shock_fade_shadows.length===0,
|
||
stagesOneShadowPerEvent:shockStage.staged===1,
|
||
shadowNeverChangesCash:shockBook.cash===shockCash&&shockBook.positions.length===0,
|
||
doesNotGradeBeforeTwelveHours:earlyShockGrade.completed===0,
|
||
gradesAtExecutableExit:completedShockGrade.completed===1&&shockBook.shock_fade_outcomes.length===1&&shockBook.shock_fade_outcomes[0].exit_price===0.2425,
|
||
capitalIgnoresUnauditedEarlyStop:earlyShockCapitalBook.positions.length===1&&earlyShockCapitalBook.closed.length===0,
|
||
capitalClosesAtTwelveHourExecutableBid:timedShockCapitalBook.positions.length===0&&timedShockCapitalBook.closed.length===1
|
||
&&timedShockCapitalBook.closed[0].current_price===0.2425,
|
||
legacyStrategyTwoKeepsRecordedTwentyFourHourTarget:legacyShockCapitalBook.positions.length===1&&legacyShockCapitalBook.closed.length===0
|
||
&&Math.abs(shockFadeTargetHours(legacyShockCapitalBook.positions[0])-24)<0.001,
|
||
unsafeLegacyCapitalExitsAtExecutableBid:unsafeShockCapitalBook.positions.length===0&&unsafeShockCapitalBook.closed.length===1
|
||
&&unsafeShockCapitalBook.closed[0].close_reason==="Shock contract failed the full-horizon settlement safety gate"
|
||
&&unsafeShockCapitalBook.closed[0].current_price===0.2425,
|
||
offlineCannotStageOrGrade:offlineShockStage.staged===0&&offlineShockStageBook.shock_fade_shadows.length===0
|
||
&&offlineShockGrade.completed===0&&offlineShockBook.shock_fade_shadows.length===1&&offlineShockBook.shock_fade_outcomes.length===0,
|
||
missedWindowExpiresWithoutInventedReturn:expiredShockGrade.expired===1&&expiredShockBook.shock_fade_outcomes.length===0&&expiredShockBook.shock_fade_expired_ungraded===1,
|
||
forwardLedgerSurvivesCompaction:compactedShockBook.shock_fade_shadows.length===1&&compactedShockBook.shock_fade_outcomes.length===1,
|
||
eightyPositiveEventsIncreaseSize:promotedShockProfile.promoted&&promotedShockProfile.lower>0.015&&promotedShockSuggestion.trade_ready
|
||
&&promotedShockProfile.position_pct===SHOCK_FADE_PROMOTED_POSITION_PCT,
|
||
promotedCapitalIsTightlyCapped:promotedShockBook.positions.length===1&&promotedShockBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_PROMOTED_POSITION_PCT+0.01,
|
||
losingForwardEvidenceDemotes:losingShockProfile.demoted&&!losingShockProfile.allowed&&losingShockProfile.position_pct===0
|
||
&&!demotedShockSuggestion.trade_ready,
|
||
sharedEvidencePromotesEveryAdopter:sharedPositiveShockProfile.scope==="shared-adopters"&&sharedPositiveShockProfile.events===SHOCK_FADE_PROMOTION_EVENTS
|
||
&&sharedPositiveShockProfile.promoted&&shockAdopterIds.every(id=>applyShockFadeForwardPromotion(shockSuggestion,sharedPositiveShockState.agents[id],sharedPositiveShockProfile).trade_ready),
|
||
sharedPromotionControlsCapitalSize:sharedPositiveOpenBook.positions.length===1
|
||
&&sharedPositiveOpenBook.positions[0].cost<=STARTING_BALANCE*SHOCK_FADE_PROMOTED_POSITION_PCT+0.01,
|
||
sharedLossesDisableEveryAdopter:sharedLosingShockProfile.demoted&&!sharedLosingShockProfile.allowed
|
||
&&shockAdopterIds.every(id=>!applyShockFadeForwardPromotion(shockSuggestion,sharedLosingShockState.agents[id],sharedLosingShockProfile).trade_ready),
|
||
sharedEventsAreDeduplicatedAndVersioned:duplicateShockProfile.events===1&&duplicateShockProfile.mean===0.05,
|
||
simultaneousUnderlyingMovesCountOnce:correlatedEvidenceProfile.events===1,
|
||
capitalCloseFeedsSharedLearner:sharedCapitalShockProfile.events===1&&sharedCapitalShockProfile.pnl===timedShockCapitalBook.closed[0].realized_pnl,
|
||
},
|
||
riskBudget:{core:coreRiskBudget,aggressiveGap:aggressiveGapBudget,blocksExactRange:intervalContract.jump_risk&&!intervalContract.trade_ready,
|
||
blocksPathDependentBarrier:pathBarrierContract.jump_risk&&!pathBarrierContract.trade_ready,
|
||
explainsPathDependentBarrier:pathBarrierContract.rationale.includes("jump directly to settlement"),preservesFixedDateLevel:!fixedDateLevelContract.jump_risk,
|
||
preservesOrdinaryDatedContract:!ordinaryDatedContract.jump_risk,boundedStake:boundedStakeForRisk(10000,1000,riskCfg,{entry_price:0.82,days_to_resolution:5,price_change_1d:0.06}),
|
||
legacyNormalizedValue:riskPos.value,equityPreserved:+equity(riskBook).toFixed(2),capsBinaryGap:aggressiveGapBudget===0.03&&riskPos.value<=300.01},
|
||
accounting:{
|
||
fullCloseConservesEquity:Math.abs(fullCloseFixture.book.cash-fullCloseEquity)<=0.02&&fullCloseFixture.pos.realized_pnl===200,
|
||
partialSaleConservesEquity:Math.abs(partialEquityAfter-partialEquityBefore)<=0.02&&partialRealizedAfterFirst===50,
|
||
partialThenCloseReconciles:partialFixture.book.cash===10200&&partialFixture.pos.realized_pnl===200,
|
||
stopOutReconciles:stopFixture.book.positions.length===0&&stopFixture.book.cash===9800&&stopFixture.book.closed[0].realized_pnl===-200,
|
||
settlementReconciles:settlementFixture.book.positions.length===0&&settlementFixture.book.cash===11000&&settlementFixture.book.closed[0].realized_pnl===1000,
|
||
suspendedMarketStaysOpen:suspendedFixture.book.positions.length===1&&suspendedFixture.book.closed.length===0
|
||
&&suspendedFixture.book.positions[0].price_status==="orders-paused"&&suspendedFixture.book.cash===9000,
|
||
suspendedMakerInventoryStaysOpen:suspendedMakerFixture.book.positions.length===1&&suspendedMakerFixture.book.closed.length===0
|
||
&&suspendedMakerFixture.book.positions[0].price_status==="orders-paused"&&suspendedMakerFixture.book.cash===9000,
|
||
suspendedBundleLegStaysOpen:suspendedBundleFixture.book.positions.length===1&&suspendedBundleFixture.book.closed.length===0
|
||
&&suspendedBundleFixture.book.positions[0].price_status==="orders-paused"&&suspendedBundleFixture.book.cash===9000,
|
||
retiredDirectionalPositionExitsAtFreshPrice:retiredFixture.book.positions.length===0&&retiredFixture.book.closed[0].close_reason.includes("retired")&&Math.abs(retiredFixture.book.cash-retiredEquityBefore)<=0.02,
|
||
rejectedStrategy57SportsCapitalExitsAtFreshPrice:retiredSportsCapitalFixture.book.positions.length===0
|
||
&&retiredSportsCapitalFixture.book.closed[0].close_reason.includes("retired")&&Math.abs(retiredSportsCapitalFixture.book.cash-retiredSportsCapitalEquity)<=0.02,
|
||
unlabeledLegacyPositionExitsAtFreshPrice:unlabeledLegacyFixture.book.positions.length===0&&unlabeledLegacyFixture.book.closed[0].close_reason.includes("retired")&&Math.abs(unlabeledLegacyFixture.book.cash-unlabeledLegacyEquityBefore)<=0.02,
|
||
currentUnlabeledPositionIsPreserved:currentUnlabeledFixture.book.positions.length===1&¤tUnlabeledFixture.book.closed.length===0,
|
||
riskTrimConservesEquity:+equity(riskBook).toFixed(2)===10000,
|
||
adaptiveReturnSetsLeader:adaptiveRankFixture[0].c.id==="adaptive-leader",
|
||
},
|
||
makerLiquidity:{
|
||
acceptsSharedRewardBookAudit:auditedMakerCandidate&&auditedMakerCandidate.reward_yield_band==="high"&&auditedMakerCandidate.required_capital===19.2,
|
||
enforcesRewardPayoutMinimum:belowPayoutMakerCandidate===null,
|
||
stagesEligiblePairedQuoteAsShadow:makerStage.staged===1&&makerStage.shadowStaged===1&&makerStage.capitalStaged===0&&makerStage.active===1&&makerQuote.paired_cost===0.96,
|
||
unprovenQuoteDoesNotReserveCapital:makerStage.reserved===0,
|
||
untouchedLegacyQuotesAreCanceled:legacyUntouched.expired===1&&legacyUntouchedBook.maker_quotes.length===0&&legacyUntouchedBook.positions.length===0&&legacyUntouchedBook.cash===STARTING_BALANCE,
|
||
filledLegacyInventoryIsUnwoundHonestly:legacyOneLeg.expired===1&&legacyOneLegBook.maker_quotes.length===0&&legacyOneLegBook.positions.length===0&&legacyOneLegBook.cash<STARTING_BALANCE,
|
||
firstCrossCreatesOnlyOneInventoryLeg:makerFirst.fills===1&&makerSingleFill,
|
||
secondCrossLocksComplementaryPair:makerSecond.fills===1&&makerSecond.locked===1&&makerBook.maker_quotes.length===0,
|
||
overlapCleanupPreservesLockedPair:makerPairSurvivesOverlap,
|
||
lockedPairSettlesAtQuotedProfit:makerBook.positions.length===0&&Math.abs(makerBook.cash-(10000+makerExpectedProfit))<=0.02,
|
||
lockedPairRecordsLearningOutcome:makerBook.maker_outcomes.length===1&&makerBook.maker_outcomes[0].status==="locked"&&makerBook.maker_outcomes[0].pnl===makerExpectedProfit,
|
||
unmatchedInventoryExpiresAtExecutableBid:makerExpired.expired===1&&makerExpiryBook.positions.length===0&&makerExpiryBook.maker_quotes.length===0&&Math.abs(makerExpiryBook.cash-9997.5)<=0.02,
|
||
adverseExitRecordsNetLoss:makerExpiryBook.maker_outcomes.length===1&&makerExpiryBook.maker_outcomes[0].status==="single-exit"&&makerExpiryBook.maker_outcomes[0].pnl===-2.5,
|
||
unfilledExpiryRecordsZero:unfilledExpiry.expired===1&&unfilledBook.maker_outcomes.length===1&&unfilledBook.maker_outcomes[0].status==="unfilled"&&unfilledBook.maker_outcomes[0].pnl===0,
|
||
offlineSnapshotCannotInventFills:makerOffline.fills===0&&makerOfflineBook.positions.length===0&&makerOfflineBook.maker_quotes.length===1,
|
||
clobHistoryTouchCanFillRestingBid:touchFill.fills===1&&touchFill.historyTouches===1&&touchBook.positions.length===1&&touchBook.positions[0].entry_reason.includes("clob-price-history"),
|
||
offlineHistoryCannotInventFill:offlineTouch.fills===0&&offlineTouchBook.positions.length===0&&offlineTouchBook.maker_quotes.length===1,
|
||
repricedQuoteRejectsEarlierTouch:preRepriceIgnored,
|
||
repricedQuoteAcceptsLaterTouch:postRepriceTouch.fills===1&&postRepriceTouch.locked===1&&repriceBook.maker_quotes.length===0,
|
||
positiveShadowCohortCannotOverrideFailedBacktest:profitableCandidate&&profitableCandidate.maker_learning_state==="shadow-backtest-blocked"
|
||
&&!profitableCandidate.maker_capital_enabled&&profitableCandidate.units===makerQuote.units,
|
||
promotionRequiresDistinctLockedEvents:buildMakerProfile(profitableMakerBook).global.current_locked_events===24,
|
||
losingCohortRemainsShadowOnly:blockedMakerCandidate&&blockedMakerCandidate.maker_learning_state==="shadow-rejected"&&!blockedMakerCandidate.maker_capital_enabled,
|
||
noRealMoneyExplorationLane:explorationMakerCandidate&&explorationMakerCandidate.maker_learning_state==="shadow-rejected"&&!explorationMakerCandidate.maker_capital_enabled,
|
||
shadowSingleTouchChangesNoCash:shadowFinished.shadowCompleted===1&&shadowBook.cash===shadowCashBefore&&shadowBook.positions.length===0&&shadowBook.maker_quotes.length===0,
|
||
shadowSingleTouchExitsImmediately:shadowBook.maker_outcomes.length===1&&shadowBook.maker_outcomes[0].shadow_only&&shadowBook.maker_outcomes[0].status==="immediate-exit"&&shadowBook.maker_outcomes[0].pnl<0,
|
||
executableComplementCanLockShadowPair:shadowHedged.shadowCompleted===1&&shadowHedged.locked===1&&shadowHedgeBook.cash===shadowHedgeCashBefore
|
||
&&shadowHedgeBook.positions.length===0&&shadowHedgeBook.maker_quotes.length===0&&shadowHedgeBook.maker_outcomes[0].status==="hedged-lock"&&shadowHedgeBook.maker_outcomes[0].pnl>0,
|
||
outcomesAreDeduplicated:makerDedupeBook.maker_outcomes.length===1,
|
||
makerLearningSurvivesCompaction:compactedMakerBook.maker_quotes.length===1&&compactedMakerBook.maker_outcomes.length===24,
|
||
sharedLedgerCombinesAgentEvidence:sharedMakerFixture.global.current_events===2&&sharedMakerFixture.global.current_locked_events===2,
|
||
agentsReceiveDisjointShadowMarkets:distributedA.size===2&&distributedB.size===2&&[...distributedA].every(id=>!distributedB.has(id)),
|
||
noHypotheticalRewardCredited:makerStage.staged===1&&Math.abs(makerBook.cash-(10000+makerExpectedProfit))<=0.02,
|
||
rejectsUnrewardedMarket:makerPairCandidate(Object.assign({},makerMarket,{rewards_daily_rate:0}))===null,
|
||
rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null,
|
||
},
|
||
bundleArbitrage:{
|
||
usesMarketSpecificZeroFee:Boolean(zeroFeeBundleSuggestion&&zeroFeeBundleSuggestion.friction===0
|
||
&&zeroFeeBundleSuggestion.bundle_cost_per_unit===0.95&&zeroFeeBundleSuggestion.bundle_net_profit_per_unit===0.05),
|
||
appliesPublishedFeeCurve:bundleFeePerShare({rate:0.07,exponent:1,takerOnly:true},0.5)===0.0175,
|
||
depthVwapConsumesMultipleAskLevels:depthVwapFixture===0.396,
|
||
blocksUnverifiedBundleEntry:unverifiedBundleOpen===null&&unverifiedBundleBook.positions.length===0&&unverifiedBundleBook.cash===STARTING_BALANCE,
|
||
capsOpenedUnitsToVerifiedDepth:depthCappedBundleBook.positions.length===3
|
||
&&depthCappedBundleBook.positions.every(position=>position.shares===50),
|
||
rejectsNonBinaryMarketLabels:nonBinaryMarketRejected,
|
||
rejectsNonBinaryBundleLabels:nonBinaryBundleRejected,
|
||
identifiesAboveThresholdDominance:Boolean(dominanceSuggestion&&dominanceSuggestion.bundle_logic==="threshold-dominance"
|
||
&&dominanceSuggestion.bundle_cost_per_unit===0.994&&dominanceSuggestion.bundle_net_profit_per_unit===0.006
|
||
&&dominanceSuggestion.bundle_legs[0].side==="YES"&&dominanceSuggestion.bundle_legs[1].side==="NO"),
|
||
identifiesBelowThresholdDominance:Boolean(belowDominanceSuggestion&&belowDominanceSuggestion.bundle_cost_per_unit===0.99
|
||
&&belowDominanceSuggestion.bundle_legs[0].market_id==="below-high"&&belowDominanceSuggestion.bundle_legs[1].market_id==="below-low"),
|
||
rejectsMismatchedDominanceTerms:mismatchedDominanceRejected,
|
||
rejectsUnprofitableDominancePair:unprofitableDominanceRejected,
|
||
retainsUnprofitablePairForTelemetry:Boolean(unprofitableDominanceAudited&&!unprofitableDominanceAudited.trade_ready
|
||
&&unprofitableDominanceAudited.watch_only&&unprofitableDominanceAudited.bundle_net_profit_per_unit<0),
|
||
rejectsNonBinaryDominanceLabels:nonBinaryDominanceRejected,
|
||
identifiesDeadlineDominance:Boolean(deadlineSuggestion&&deadlineSuggestion.bundle_logic==="deadline-dominance"
|
||
&&deadlineSuggestion.bundle_cost_per_unit===0.96&&deadlineSuggestion.bundle_net_profit_per_unit===0.04
|
||
&&deadlineSuggestion.bundle_legs[0].market_id==="deadline-early"&&deadlineSuggestion.bundle_legs[0].side==="NO"
|
||
&&deadlineSuggestion.bundle_legs[1].market_id==="deadline-late"&&deadlineSuggestion.bundle_legs[1].side==="YES"),
|
||
rejectsMismatchedDeadlineTerms:mismatchedDeadlineRejected,
|
||
rejectsMixedExplicitAndImplicitDeadlineYears:mixedDeadlineYearRejected,
|
||
rejectsInvalidCalendarDeadline:invalidDeadlineRejected,
|
||
rejectsUnprofitableDeadlinePair:unprofitableDeadlineRejected,
|
||
deadlineRequiresLivePrices:Boolean(deadlineSuggestion&&deadlineSuggestion.requires_live),
|
||
staleDeadlineSnapshotCannotOpen:!offlineDeadlineSuggestion.trade_ready&&!offlineDeadlineSuggestion.entry_candidate&&offlineDeadlineSuggestion.watch_only,
|
||
opensDeadlineLegsAtomically:deadlineOpenedPositions===2&&deadlineUnits>0&&deadlineBook.closed.length===2,
|
||
deadlineWorstCaseSettlesProfitably:deadlineBook.positions.length===0&&deadlineBook.cash>STARTING_BALANCE
|
||
&&Math.abs(deadlineBook.cash-(STARTING_BALANCE+deadlineUnits*0.04))<=0.03,
|
||
deadlineEarlyOutcomePaysMinimum:deadlineEarlyCase.book.positions.length===0
|
||
&&Math.abs(deadlineEarlyCase.book.cash-(STARTING_BALANCE+deadlineEarlyCase.units*0.04))<=0.03,
|
||
deadlineBetweenDatesPaysBoth:deadlineBetweenCase.book.positions.length===0
|
||
&&Math.abs(deadlineBetweenCase.book.cash-(STARTING_BALANCE+deadlineBetweenCase.units*1.04))<=0.03,
|
||
dominanceRequiresLivePrices:Boolean(dominanceSuggestion&&dominanceSuggestion.requires_live),
|
||
staleDominanceSnapshotCannotOpen:!offlineDominanceSuggestion.trade_ready&&!offlineDominanceSuggestion.entry_candidate&&offlineDominanceSuggestion.watch_only,
|
||
opensDominanceLegsAtomically:dominanceOpenedPositions===2&&dominanceUnits>0&&dominanceBook.closed.length===2,
|
||
dominanceWorstCaseSettlesProfitably:dominanceBook.positions.length===0&&dominanceBook.cash>STARTING_BALANCE
|
||
&&Math.abs(dominanceBook.cash-(STARTING_BALANCE+dominanceUnits*0.006))<=0.03,
|
||
identifiesPositiveCompleteNoBundle:bundleSuggestion&&bundleSuggestion.trade_ready&&bundleSuggestion.bundle_side==="NO"&&bundleSuggestion.bundle_legs.every(leg=>leg.side==="NO")&&bundleSuggestion.bundle_net_profit_per_unit===0.005,
|
||
identifiesPositiveCompleteYesBundle:yesBundleSuggestion&&yesBundleSuggestion.trade_ready&&yesBundleSuggestion.bundle_side==="YES"&&yesBundleSuggestion.bundle_legs.every(leg=>leg.side==="YES")&&yesBundleSuggestion.bundle_net_profit_per_unit===0.035,
|
||
rejectsEconomicallyTinyLargeBundle:tinyReturnBundle===null,
|
||
opensAllNoLegsAtomically:bundleOpenedPositions===3&&bundleUnits>0&&bundleBook.closed.length===3,
|
||
opensAllYesLegsAtomically:yesBundleOpenedPositions===3&&yesBundleUnits>0&&yesBundleBook.closed.length===3,
|
||
ignoresIndividualNoLegStops:bundleSurvivedIntermediateMove,
|
||
ignoresIndividualYesLegStops:yesBundleSurvivedIntermediateMove,
|
||
noSettlementProducesModeledProfit:bundleBook.positions.length===0&&bundleBook.cash>STARTING_BALANCE&&Math.abs(bundleBook.cash-(STARTING_BALANCE+bundleUnits*0.005))<=0.03,
|
||
yesSettlementProducesModeledProfit:yesBundleBook.positions.length===0&&yesBundleBook.cash>STARTING_BALANCE&&Math.abs(yesBundleBook.cash-(STARTING_BALANCE+yesBundleUnits*0.035))<=0.03,
|
||
requiresLivePrices:Boolean(bundleSuggestion&&bundleSuggestion.requires_live),
|
||
staleOfflineSnapshotCannotOpen:!offlineBundleSuggestion.trade_ready&&!offlineBundleSuggestion.entry_candidate&&offlineBundleSuggestion.watch_only,
|
||
noBundle:{openedPositions:bundleOpenedPositions,closedPositions:bundleBook.closed.length,cash:bundleBook.cash,units:bundleUnits||0,decision:bundleOpenDecision},
|
||
yesBundle:{openedPositions:yesBundleOpenedPositions,closedPositions:yesBundleBook.closed.length,cash:yesBundleBook.cash,units:yesBundleUnits||0},
|
||
},
|
||
offline:{fresh:offlineCachePolicy(30*60000),staleEntry:offlineCachePolicy(3*3600000),expired:offlineCachePolicy(25*3600000),
|
||
staleMarkUpdatesValue:markOnlyBook.positions.length===1&&markOnlyBook.positions[0].value===800,
|
||
staleMarkPreservesTrades:markOnlyBook.positions.length===1&&markOnlyBook.positions[0].shares===1000&&markOnlyBook.cash===9500&&markOnlyBook.positions[0].price_status==="cached-mark-only",
|
||
staleCacheBlocksHiddenCandidates:staleEntryBook.positions.length===0&&staleEntryBook.cash===STARTING_BALANCE,
|
||
freshSnapshotExecutesRules:executableBook.positions.length===1&&executableBook.positions[0].shares<1000&&executableBook.cash>9500},
|
||
exits:{youngConflict:exitReason(young,fresh,conflict,AGENTS[0]),matureConflict:exitReason(mature,fresh,conflict,AGENTS[0]),
|
||
politicsEarlyConflict:exitReason(politicsEarly,fresh,conflict,AGENTS[0]),politicsMatureConflict:exitReason(politicsMature,fresh,conflict,AGENTS[0]),trailing:trailingProfitReason(trailing)},
|
||
backtest:{noLookahead:backtestNoLookahead,features:historicalFeatures},
|
||
overlapRemaining,gapOverlapRemaining,singleGlobalExplorer:explorationOwners.length===1,
|
||
chatIdentityMigrates:chatMigrationFixture.reversal[0].text.includes("Shock Reversion")&&!chatMigrationFixture.reversal[0].text.includes("Reversal Edge")&&!chatMigrationFixture.reversal[0].text.includes("Trend Endurance"),
|
||
immaterialRunnerDoesNotBlock,rejectionAccounting,convictionCapacityCoversTarget,rules:window.PMA_ENGINE_DIAGNOSTICS.rules};
|
||
}
|
||
window.PMA_AUTOMATION=Object.freeze({
|
||
status:()=>{const st=loadState();return {build:BUILD_VERSION,strategy:SUGGESTION_ENGINE_VERSION,running:CYCLE_RUNNING,
|
||
seeded:Boolean(st.seeded),last_cycle_hour:st.last_cycle_hour||null,suggestion_count:(loadSuggestions().suggestions||[]).length};},
|
||
runCycle:async()=>{const result=await runDailyCycle();renderAll();return result;},
|
||
exportShared:()=>buildPublicRuntimeSnapshot(),
|
||
importShared:(snapshot)=>{const applied=applyPublicRuntimeSnapshot(snapshot);if(applied)renderAll();return applied;},
|
||
});
|
||
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);
|
||
}
|
||
if(new URLSearchParams(location.search).get("bundle_test")==="1"){
|
||
const output=document.createElement("output");output.id="bundleLiveTest";output.hidden=true;output.textContent="running";document.body.appendChild(output);
|
||
fetchNegativeRiskBundles().then(result=>{output.textContent=JSON.stringify(result);})
|
||
.catch(error=>{output.textContent=JSON.stringify({error:String(error&&error.message||error)});});
|
||
}
|
||
|
||
/* ---------- Wire up ---------- */
|
||
applyPersonalMode();
|
||
initProviderConfig();
|
||
refreshTradeEmailStatus();
|
||
$("runBtn").addEventListener("click",async()=>{
|
||
const btn=$("runBtn");btn.disabled=true;btn.textContent="Running…";
|
||
try{const r=await runDailyCycle();const b=board(),mode=r.offline?(r.executeTrades?"Offline":"Mark-only"):"Live";toast(r.readOnly?"The autonomous shared runner owns this portfolio; refreshed results arrive every five minutes.":r.busy?`Cycle already running · ${b[0].c.emoji} ${b[0].c.name} leads`:r.skipped?`Already ran this minute · ${b[0].c.emoji} ${b[0].c.name} leads`:`${mode} cycle done${r.pendingSync?" · sync queued":""} · ${r.suggestions} ideas · ${b[0].c.emoji} ${b[0].c.name} leads`);renderAll();}
|
||
catch(e){toast("Run failed: "+e.message);setStatus("error",false);}
|
||
btn.disabled=autonomousRuntimeControlsCycle();btn.textContent=btn.disabled?"Auto cycle":"Run cycle";
|
||
});
|
||
const b64Encode=(s)=>btoa(Array.from(new TextEncoder().encode(s),b=>String.fromCharCode(b)).join(""));
|
||
const b64Decode=(s)=>new TextDecoder().decode(Uint8Array.from(atob(s),c=>c.charCodeAt(0)));
|
||
function buildSyncCode(){
|
||
const data={version:1,exported_at:nowIso(),items:collectSyncItems()};
|
||
return "PMA1."+b64Encode(JSON.stringify(data));
|
||
}
|
||
async function readClipboardOrPrompt(){
|
||
try{if(navigator.clipboard&&navigator.clipboard.readText)return (await navigator.clipboard.readText()).trim();}
|
||
catch(e){}
|
||
return (prompt("Paste your Polymarket Arena sync code:")||"").trim();
|
||
}
|
||
$("copyStateBtn").addEventListener("click",async()=>{
|
||
const code=buildSyncCode();
|
||
try{await navigator.clipboard.writeText(code);toast("Sync code copied. Paste it on the other device with Import Sync.");}
|
||
catch(e){prompt("Copy this sync code:",code);}
|
||
});
|
||
$("importStateBtn").addEventListener("click",async()=>{
|
||
const code=await readClipboardOrPrompt();
|
||
if(!code)return;
|
||
try{
|
||
const payload=JSON.parse(b64Decode(code.replace(/^PMA1\./,"")));
|
||
if(!payload||payload.version!==1||!payload.items)throw new Error("Bad sync code");
|
||
if(!confirm("Import this sync state and replace this device's current local portfolio data?"))return;
|
||
applySyncItems(payload.items);
|
||
await pushCloudState(true);
|
||
toast("Sync imported. This device now matches the copied state.");
|
||
renderAll();showTab(location.hash.slice(1)||"portfolio");
|
||
}catch(e){toast("Import failed. Check that the sync code was copied fully.");}
|
||
});
|
||
$("createPaperAccountBtn").addEventListener("click",async()=>{
|
||
const name=($("paperAccountName").value||"Paper Trader").trim().slice(0,40);
|
||
const password=($("paperAccountPassword").value||"").trim();
|
||
try{
|
||
if(password){
|
||
await createCloudPaperAccount(name||"Paper Trader",password);
|
||
toast("Cloud paper account created.");
|
||
}else{
|
||
const store=loadPaperStore(),acct=defaultPaperAccount(name||"Paper Trader",password);
|
||
store.accounts[acct.id]=acct;store.activeId=acct.id;
|
||
savePaperStore(store);toast("Local paper account created.");
|
||
}
|
||
$("paperAccountName").value="";$("paperAccountPassword").value="";
|
||
renderPaperTab();
|
||
}catch(e){toast(e.message);}
|
||
});
|
||
$("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);
|
||
if(!requirePaperUnlock(acct,$("paperPasswordInput")&&$("paperPasswordInput").value))return;
|
||
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.`);
|
||
});
|
||
$("paperDepositBtn").addEventListener("click",()=>paperMoveCash("deposit",$("paperMoneyAmount").value,$("paperPasswordInput").value));
|
||
$("paperWithdrawBtn").addEventListener("click",()=>paperMoveCash("withdraw",$("paperMoneyAmount").value,$("paperPasswordInput").value));
|
||
$("buyAgentBtn").addEventListener("click",()=>paperBuyAgent($("buyAgentSelect").value,$("buyAgentAmount").value));
|
||
$("paperSearchBtn").addEventListener("click",()=>searchPaperMarkets());
|
||
$("paperAllBtn").addEventListener("click",()=>{PAPER_MARKET_QUERY="";PAPER_SEARCH_RESULTS=[];loadPaperMarketPage(true,true);});
|
||
$("paperMoreBtn").addEventListener("click",()=>loadPaperMarketPage(false));
|
||
$("paperClearSearchBtn").addEventListener("click",()=>{PAPER_SEARCH_RESULTS=null;PAPER_MARKET_OFFSET=0;PAPER_MARKET_QUERY="";renderPaperTab();});
|
||
$("paperLoginBtn").addEventListener("click",async()=>{
|
||
const acct=activePaperAccount();
|
||
const cloudName=($("paperAccountName").value||"").trim();
|
||
const topPassword=($("paperAccountPassword").value||"").trim();
|
||
try{
|
||
if(cloudName&&topPassword){
|
||
await loginCloudPaperAccount(cloudName,topPassword);
|
||
$("paperAccountName").value="";$("paperAccountPassword").value="";
|
||
renderPaperTab();toast("Cloud paper account loaded.");
|
||
}else if(acct.cloudBacked){
|
||
await loginCloudPaperAccount(acct.name,$("paperPasswordInput").value);
|
||
renderPaperTab();toast("Cloud paper account loaded.");
|
||
}else if(requirePaperUnlock(acct,$("paperPasswordInput").value)){renderPaperTab();toast("Logged in.");}
|
||
}catch(e){toast(e.message);}
|
||
});
|
||
$("paperSignOutBtn").addEventListener("click",()=>{
|
||
const acct=activePaperAccount();
|
||
sessionStorage.removeItem(unlockKey(acct.id));
|
||
sessionStorage.removeItem(paperSessionKey(acct.id));
|
||
renderPaperTab();toast("Signed out of paper account.");
|
||
});
|
||
$("saveLiveProfileBtn").addEventListener("click",async()=>{
|
||
const s=loadLiveState();
|
||
s.walletAddress=$("liveWalletAddress").value.trim();
|
||
s.jurisdiction=$("liveJurisdiction").value;
|
||
s.depositLimit=Number($("liveDepositLimit").value||0);
|
||
s.withdrawReserve=Number($("liveWithdrawReserve").value||0);
|
||
try{
|
||
await saveRiskProfileToServer(s);
|
||
saveLiveState(s,"Updated live-money wallet/funds profile and server risk profile");
|
||
renderLiveMoneyTab();toast("Live-money readiness profile saved to Neon.");
|
||
}catch(e){
|
||
saveLiveState(s,"Updated local live-money wallet/funds profile");
|
||
renderLiveMoneyTab();toast(e.message||"Saved locally, but server risk profile failed.");
|
||
}
|
||
});
|
||
$("dryRunOrderBtn").addEventListener("click",()=>dryRunLiveOrderIntent());
|
||
$("personalDepositBtn").addEventListener("click",()=>personalCapitalAction("DEPOSIT"));
|
||
$("personalWithdrawBtn").addEventListener("click",()=>personalCapitalAction("WITHDRAW"));
|
||
$("personalBuyAgentBtn").addEventListener("click",()=>personalCapitalAction("BUY_AGENT"));
|
||
$("personalSellAgentBtn").addEventListener("click",()=>personalCapitalAction("SELL_AGENT"));
|
||
$("liveSearchBtn").addEventListener("click",()=>searchLiveMarkets());
|
||
$("liveAllBtn").addEventListener("click",()=>{LIVE_MARKET_QUERY="";LIVE_SEARCH_RESULTS=[];loadLiveMarketPage(true,true);});
|
||
$("liveMoreBtn").addEventListener("click",()=>loadLiveMarketPage(false));
|
||
$("liveClearSearchBtn").addEventListener("click",()=>{LIVE_SEARCH_RESULTS=[];LIVE_MARKET_OFFSET=0;LIVE_MARKET_QUERY="";renderLiveMarkets();});
|
||
$("recordConsentBtn").addEventListener("click",()=>recordLiveConsent());
|
||
async function resumeCycleNow(){
|
||
await refreshFromCloudAndRender(true);
|
||
autoRunDueCycle();
|
||
}
|
||
window.addEventListener("focus",resumeCycleNow);
|
||
document.addEventListener("visibilitychange",()=>{
|
||
if(!document.hidden)resumeCycleNow();
|
||
});
|
||
async function autoRunDueCycle(){
|
||
if(CYCLE_RUNNING||autonomousRuntimeControlsCycle())return;
|
||
const st=loadState();
|
||
if(!st.seeded||st.last_cycle_hour===currentCycleHour())return;
|
||
try{await runDailyCycle();renderAll();toast("Live update ready.");}
|
||
catch(e){setStatus("error — click Run to retry",false);}
|
||
}
|
||
let CYCLE_WORKER=null;
|
||
try{
|
||
if("Worker" in window){CYCLE_WORKER=new Worker("/cycle-worker.js");CYCLE_WORKER.onmessage=e=>{
|
||
if(e.data&&e.data.type==="ready")window.PMA_BACKGROUND_WORKER_READY=true;
|
||
if(e.data&&e.data.type==="cycle"){
|
||
window.PMA_BACKGROUND_LAST_TICK=e.data.at;
|
||
autoRunDueCycle();
|
||
}
|
||
};}
|
||
}catch(e){CYCLE_WORKER=null;}
|
||
setInterval(()=>autoRunDueCycle(),RUN_INTERVAL_MS);
|
||
window.addEventListener("online",async()=>{
|
||
const st=loadState();
|
||
if(st.pending_sync){
|
||
setStatus("reconnecting cloud…",true);
|
||
if(await pushCloudState()){st.pending_sync=false;saveState(st);setStatus("offline work synced",false);toast("Offline cycle synced to the shared portfolio.");}
|
||
}
|
||
autoRunDueCycle();
|
||
});
|
||
if("serviceWorker" in navigator){navigator.serviceWorker.register("/sw.js").catch(()=>{});}
|
||
|
||
/* 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();
|
||
const cloud=await loadAuthoritativeCloudState();
|
||
let st=loadState();
|
||
if(cloud.loaded){
|
||
toast(cloud.read_only?"Autonomous agent state loaded for this device.":"Cloud state loaded for this device.");
|
||
}else if(cloud.ok&&!cloud.degraded&&!cloud.read_only&&st.seeded){
|
||
pushCloudState();
|
||
}else if(cloud.degraded||!cloud.ok){
|
||
setStatus(localOnlyStatus("offline-ready"),false);
|
||
}
|
||
renderAll();
|
||
const btn=$("runBtn");
|
||
if(autonomousRuntimeControlsCycle()){
|
||
btn.disabled=true;btn.textContent="Auto cycle";setStatus("autonomous shared state · refreshes every 5 min",false);return;
|
||
}else if(!st.seeded){
|
||
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=null;st.last_cycle_hour=null;saveState(st);
|
||
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…";
|
||
try{await runDailyCycle();renderAll();toast("Live update ready.");}
|
||
catch(e){setStatus("error — click Run to retry",false);}
|
||
btn.disabled=false;btn.textContent="Run cycle";
|
||
}else{setStatus("up to date",false);}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|