File size: 8,073 Bytes
7477017
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<!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>