finance / static /console.html
betterwithage's picture
PURIQ Finance Engine v2 — source 9e3fd501e50a
7477017 verified
Raw History Blame Contribute Delete
8.07 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>PURIQ Finance — Signal Console · SZL Holdings</title>
<style>
:root{--bg:#05070d;--panel:#0b1020;--edge:#1b2b4a;--ink:#dbe7ff;--dim:#5f7398;--acc:#35e0c2;--warn:#ffb454;--bad:#ff5470;--good:#54ff9f}
*{box-sizing:border-box}body{margin:0;background:radial-gradient(1200px 800px at 70% -10%,#0d1c33 0%,var(--bg) 55%);color:var(--ink);font:14px/1.5 "SF Mono",ui-monospace,Menlo,Consolas,monospace;min-height:100vh}
header{padding:22px 28px;border-bottom:1px solid var(--edge);display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:8px}
h1{font-size:18px;letter-spacing:.22em;margin:0;color:var(--acc);text-transform:uppercase}
.truth{font-size:11px;color:var(--warn);letter-spacing:.08em}
main{display:grid;grid-template-columns:1fr 1fr;gap:18px;padding:22px 28px;max-width:1280px;margin:0 auto}
@media(max-width:900px){main{grid-template-columns:1fr}}
section{background:linear-gradient(180deg,rgba(27,43,74,.25),rgba(11,16,32,.9));border:1px solid var(--edge);border-radius:10px;padding:18px 20px;box-shadow:0 0 24px rgba(53,224,194,.05) inset}
h2{font-size:12px;letter-spacing:.2em;color:var(--dim);margin:0 0 14px;text-transform:uppercase}
input,textarea,select{width:100%;background:#060a14;border:1px solid var(--edge);border-radius:6px;color:var(--ink);padding:9px 10px;font:inherit;margin-bottom:10px}
textarea{min-height:110px;resize:vertical}
button{background:transparent;border:1px solid var(--acc);color:var(--acc);border-radius:6px;padding:9px 16px;font:inherit;letter-spacing:.12em;cursor:pointer;text-transform:uppercase}
button:hover{background:rgba(53,224,194,.1)}
.row{display:flex;gap:10px}.row>*{flex:1}
.badge{display:inline-block;padding:2px 10px;border-radius:20px;font-size:11px;letter-spacing:.1em;border:1px solid}
.b-MEASURED{color:var(--good);border-color:var(--good)}.b-BLOCKED,.b-FAILED,.b-INVALID{color:var(--bad);border-color:var(--bad)}.b-ADVISORY{color:var(--warn);border-color:var(--warn)}
.kv{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:13px}
.kv b{color:var(--dim);font-weight:400}.kv span{text-align:right}
.big{font-size:30px;letter-spacing:.15em;margin:6px 0}
.BULLISH{color:var(--good)}.BEARISH{color:var(--bad)}.NEUTRAL{color:var(--dim)}
.mono{font-size:11px;color:var(--dim);word-break:break-all}
.err{color:var(--bad);font-size:12px;white-space:pre-wrap}
a{color:var(--acc)}
footer{padding:14px 28px;color:var(--dim);font-size:11px;border-top:1px solid var(--edge)}
#health{font-size:11px}
</style>
</head>
<body>
<header>
<h1>&#9672; PURIQ Finance</h1>
<span class="truth">ADVISORY-ONLY &middot; PAPER-ONLY &middot; NOT FINANCIAL ADVICE</span>
<span id="health">probing&hellip;</span>
</header>
<main>
<section>
<h2>Signal Suite</h2>
<div class="row">
<input id="sym" placeholder="symbol (AAPL)" value="AAPL"/>
<select id="origin" style="max-width:140px">
<option value="stooq">stooq &middot; live</option>
<option value="fixture">fixture &middot; offline</option>
</select>
</div>
<input id="bench" placeholder="benchmark for beta (e.g. SPY, optional)"/>
<button onclick="runSignals()">Run Analysis</button>
<div id="sigOut" style="margin-top:14px"></div>
</section>
<section>
<h2>Portfolio Analyzer</h2>
<textarea id="pf" spellcheck="false">{"AAA":[100,101,99,103,104,102,105,107,106,109],"BBB":[50,51,50,52,53,51,54,55,54,56]}</textarea>
<button onclick="runPortfolio()">Analyze</button>
<div id="pfOut" style="margin-top:14px"></div>
</section>
<section style="grid-column:1/-1">
<h2>Provenance &middot; Receipt Ledger &mdash; <a href="/panels">verification desk &rarr;</a></h2>
<button onclick="runVerify()">Verify Chain</button>
<div id="rcOut" style="margin-top:10px"></div>
</section>
</main>
<footer>PURIQ FINANCE ENGINE v2 &middot; schema szl.finance-engine/v2 &middot; receipts UNSIGNED_HONEST &middot; fail-closed by doctrine v11 &middot; source: szl-holdings/vertical-services/services/finance</footer>
<script>
const $=id=>document.getElementById(id);
const esc=s=>String(s).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
const badge=s=>`<span class="badge b-${esc(s)}">${esc(s)}</span>`;
async function api(path,opts){const r=await fetch(path,opts);const j=await r.json().catch(()=>({state:"FAILED"}));j._http=r.status;return j}
async function health(){try{const j=await api("/healthz");$("health").innerHTML=j.ok?`engine <span style="color:var(--good)">UP</span> &middot; ${esc(j.schema)}`:"engine UNKNOWN"}catch(e){$("health").textContent="engine UNAVAILABLE"}}
async function runSignals(){
const sym=encodeURIComponent($("sym").value||"AAPL");
const b=$("bench").value.trim();
const lane=$("origin").value;
const j=await api(`/api/finance/v2/signals/${sym}?origin=${lane}`);
if(j.state!=="MEASURED"){$("sigOut").innerHTML=`${badge(j.state||"FAILED")} <span class="err">${esc(j.reason||"unavailable")}</span>`;return}
const q=await api(`/api/finance/v2/quote/${sym}?origin=${lane}${b?`&benchmark=${encodeURIComponent(b)}`:""}`);
const strip=q.state==="MEASURED"
?`<div class="kv" style="margin:8px 0"><b>last</b><span>${q.stats.last.toFixed(2)} &middot; ${q.stats.bars} bars</span><b>ann. vol</b><span>${(q.stats.volatility*100).toFixed(1)}%</span><b>sharpe</b><span>${q.stats.sharpe.toFixed(2)}</span><b>max dd</b><span>${(q.stats.max_drawdown*100).toFixed(1)}%</span>${q.stats.beta!==undefined?`<b>beta vs ${esc(q.stats.beta_vs)}</b><span>${q.stats.beta.toFixed(2)}</span>`:""}</div>`
:`<div class="err" style="margin:8px 0">risk strip ${esc(q.state||"FAILED")}: ${esc(q.reason||"")}</div>`;
const i=j.indicators;
$("sigOut").innerHTML=`${badge(j.state)} ${badge("ADVISORY")}${strip}<div class="big ${j.verdict}">${esc(j.verdict)} <span style="font-size:14px;color:var(--dim)">votes ${j.vote_score}</span></div>
<div class="kv">
<b>symbol</b><span>${esc(j.symbol)} &middot; ${esc(j.data_origin)} &middot; ${j.series_len} bars</span>
<b>SMA 10/30</b><span>${i.sma_fast.toFixed(2)} / ${i.sma_slow.toFixed(2)}</span>
<b>RSI 14</b><span>${i.rsi_14.toFixed(2)}</span>
<b>MACD hist</b><span>${i.macd.histogram.toFixed(4)}</span>
<b>Bollinger z</b><span>${i.bollinger_z_20.toFixed(2)}</span>
</div><div class="mono">input_digest ${esc(j.input_digest)}<br>receipt ${esc(j.receipt_sha256)}</div>`;
}
async function runPortfolio(){
let body;try{body=JSON.parse($("pf").value)}catch(e){$("pfOut").innerHTML='<span class="err">invalid JSON</span>';return}
const j=await api("/api/finance/v2/portfolio",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});
if(j.state!=="MEASURED"){$("pfOut").innerHTML=`${badge(j.state||"FAILED")} <span class="err">${esc(j.reason||"")}</span>`;return}
const rows=j.constituents.map(k=>{const r=j.per_asset[k];return `<div class="kv" style="margin-bottom:8px"><b>${esc(k)}</b><span>vol ${(r.volatility*100).toFixed(1)}% &middot; sharpe ${r.sharpe.toFixed(2)} &middot; mdd ${(r.max_drawdown*100).toFixed(1)}%</span></div>`}).join("");
$("pfOut").innerHTML=`${badge(j.state)} ${badge("ADVISORY")}<div style="margin-top:10px">${rows}</div>
<div class="kv"><b>mean sharpe</b><span>${j.aggregate.mean_sharpe.toFixed(3)}</span><b>worst drawdown</b><span>${(j.aggregate.worst_drawdown*100).toFixed(1)}%</span></div>
<div class="mono">report_digest ${esc(j.report_digest)}<br>receipt ${esc(j.receipt_sha256)}</div>`;
}
async function runVerify(){const j=await api("/api/finance/v2/receipts/verify");$("rcOut").innerHTML=`${badge(j.state==="CONSISTENT"?"MEASURED":"INVALID")} <b style="font-weight:400">${esc(j.state)}</b> &middot; length ${j.length??"-"} <span class="err">${esc(j.reason||"")}</span>`}
health();setInterval(health,30000);
</script>
</body>
</html>