Nagaraj81's picture
Upload index.html
63b0483 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Options Intelligence β€” MSFT Β· BMNR Β· IREN Β· CRCL</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
<style>
:root{
--bg:#F7F8FA;--surface:#fff;--surface2:#F0F2F7;
--border:#E2E5EE;--border2:#C8CCDB;
--t1:#0D0F1A;--t2:#3D4460;--t3:#8B92AA;
--blue:#1A56DB;--blue-l:#EBF1FF;--blue-d:#1341B0;
--green:#0A7A45;--green-l:#E8F8F0;--green-m:#D1F0E0;
--red:#CC2828;--red-l:#FFF0F0;--red-m:#FFD8D8;
--amber:#B45309;--amber-l:#FFF8ED;--amber-m:#FDECC8;
--purple:#6326C7;--purple-l:#F3EEFF;
--sh:0 1px 3px rgba(0,0,0,.06),0 1px 2px rgba(0,0,0,.04);
--sh2:0 4px 16px rgba(0,0,0,.09),0 2px 6px rgba(0,0,0,.05);
--r:10px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--t1);-webkit-font-smoothing:antialiased;min-height:100vh}
a{color:var(--blue);text-decoration:none}a:hover{text-decoration:underline}
button,input{font-family:inherit;cursor:pointer}
textarea:focus,input:focus{outline:none}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg)}
::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px}
/* ── HEADER ── */
.hdr{background:var(--surface);border-bottom:1px solid var(--border);padding:0 28px;position:sticky;top:0;z-index:100;box-shadow:var(--sh)}
.hdr-in{max-width:1280px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:58px}
.logo{display:flex;align-items:center;gap:10px}
.logo-mark{width:34px;height:34px;background:linear-gradient(135deg,#1A56DB,#6326C7);border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:17px;box-shadow:0 2px 8px rgba(26,86,219,.3)}
.logo-title{font-size:16px;font-weight:700;letter-spacing:-.4px}
.logo-sub{font-size:11px;color:var(--t3);margin-top:1px}
.hdr-right{display:flex;align-items:center;gap:12px}
.hdr-time{font-size:11px;color:var(--t3)}
.hdr-vix{font-size:12px;font-weight:600;padding:4px 10px;border-radius:6px;background:var(--surface2);border:1px solid var(--border);display:none}
.btn-refresh{display:flex;align-items:center;gap:6px;padding:8px 18px;background:var(--blue);color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;letter-spacing:-.1px;transition:all .15s}
.btn-refresh:hover:not(:disabled){background:var(--blue-d);transform:translateY(-1px);box-shadow:0 3px 10px rgba(26,86,219,.35)}
.btn-refresh:disabled{background:var(--border2);color:var(--t3);cursor:not-allowed;transform:none;box-shadow:none}
@keyframes spin{to{transform:rotate(360deg)}}
.spin{animation:spin .8s linear infinite}
@keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
/* ── GATE ── */
#gate{min-height:calc(100vh - 58px);display:flex;align-items:center;justify-content:center;padding:32px 16px}
.gate-card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:40px;max-width:460px;width:100%;box-shadow:var(--sh2)}
.gate-logo{width:52px;height:52px;background:linear-gradient(135deg,#1A56DB,#6326C7);border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:26px;margin:0 auto 20px;box-shadow:0 4px 12px rgba(26,86,219,.3)}
.gate-h{font-size:21px;font-weight:700;text-align:center;margin-bottom:8px;letter-spacing:-.4px}
.gate-p{font-size:13px;color:var(--t2);text-align:center;line-height:1.65;margin-bottom:28px}
.gate-lbl{font-size:12px;font-weight:600;color:var(--t2);margin-bottom:6px}
.gate-inp{width:100%;padding:11px 14px;border:1.5px solid var(--border);border-radius:8px;font-size:13px;font-family:'JetBrains Mono',monospace;color:var(--t1);background:var(--bg);transition:border-color .15s;margin-bottom:6px}
.gate-inp:focus{border-color:var(--blue);background:#fff}
.gate-hint{font-size:11px;color:var(--t3);line-height:1.6;margin-bottom:20px}
.gate-err{font-size:12px;color:var(--red);margin-bottom:10px;display:none}
.btn-start{width:100%;padding:12px;background:var(--blue);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:700;transition:all .15s}
.btn-start:hover{background:var(--blue-d)}
.feat-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:24px}
.feat{padding:11px 14px;background:var(--bg);border-radius:8px;border:1px solid var(--border);font-size:11px;color:var(--t2);display:flex;align-items:flex-start;gap:8px;line-height:1.4}
.feat-ic{font-size:15px;flex-shrink:0}
/* ── PAGE ── */
.page{max-width:1280px;margin:0 auto;padding:24px 28px 48px}
/* ── SUMMARY STRIP ── */
.summary-strip{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);padding:0;margin-bottom:24px;overflow:hidden;box-shadow:var(--sh)}
.summary-hdr{padding:12px 20px;background:var(--surface2);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.summary-hdr-title{font-size:12px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--t3)}
.summary-table{width:100%;border-collapse:collapse}
.summary-table th{padding:10px 16px;font-size:10px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;color:var(--t3);text-align:left;border-bottom:1px solid var(--border);background:var(--surface)}
.summary-table td{padding:13px 16px;font-size:13px;border-bottom:1px solid var(--border);vertical-align:middle}
.summary-table tr:last-child td{border-bottom:none}
.summary-table tr:hover td{background:#FAFBFF}
.sticker{font-size:14px;font-weight:800;letter-spacing:-.3px}
.sprice{font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:500}
.schg{font-size:11px;font-weight:600;padding:2px 7px;border-radius:4px;font-family:'JetBrains Mono',monospace;margin-left:6px}
.up{background:var(--green-m);color:var(--green)}.dn{background:var(--red-m);color:var(--red)}.fl{background:var(--surface2);color:var(--t3)}
.strategy-pill{display:inline-flex;align-items:center;padding:4px 10px;border-radius:6px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;white-space:nowrap}
.conf-badge{font-size:10px;font-weight:700;padding:2px 7px;border-radius:4px;text-transform:uppercase;letter-spacing:.3px}
.conf-high{background:var(--green-m);color:var(--green)}
.conf-med{background:var(--amber-m);color:var(--amber)}
.conf-low{background:var(--surface2);color:var(--t3)}
.mono{font-family:'JetBrains Mono',monospace;font-size:12px}
.summary-loading td{color:var(--t3);font-style:italic;font-size:12px}
/* ── TICKER CARDS ── */
.cards-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
@media(max-width:900px){.cards-grid{grid-template-columns:1fr}}
.tcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);animation:fadeUp .35s ease both}
.tcard:hover{box-shadow:var(--sh2)}
.tcard-hdr{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.tcard-ticker{font-size:20px;font-weight:800;letter-spacing:-.5px}
.tcard-name{font-size:11px;color:var(--t3);margin-top:2px}
.tcard-price{text-align:right;flex-shrink:0}
.price-big{font-size:20px;font-weight:700;font-family:'JetBrains Mono',monospace}
.price-chg{font-size:11px;font-weight:600;padding:2px 8px;border-radius:5px;margin-top:4px;display:inline-block;font-family:'JetBrains Mono',monospace}
/* stats row */
.stats-band{display:grid;grid-template-columns:repeat(4,1fr);border-bottom:1px solid var(--border)}
.sc{padding:11px 14px;border-right:1px solid var(--border)}
.sc:last-child{border-right:none}
.sc-l{font-size:10px;font-weight:600;letter-spacing:.7px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
.sc-v{font-size:14px;font-weight:700;font-family:'JetBrains Mono',monospace}
.sc-s{font-size:10px;color:var(--t3);margin-top:2px}
/* IV bar */
.iv-bar-wrap{padding:13px 20px;border-bottom:1px solid var(--border)}
.iv-bar-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:7px}
.iv-bar-lbl{font-size:11px;font-weight:600;color:var(--t2)}
.iv-regime-tag{font-size:10px;font-weight:700;padding:2px 9px;border-radius:10px;text-transform:uppercase;letter-spacing:.4px}
.rt-sell{background:var(--red-l);color:var(--red)}
.rt-buy{background:var(--green-l);color:var(--green)}
.rt-neutral{background:var(--amber-l);color:var(--amber)}
.iv-track{height:6px;background:var(--surface2);border-radius:3px;overflow:hidden;border:1px solid var(--border)}
.iv-fill{height:100%;border-radius:3px;transition:width .8s ease}
/* news pill */
.news-row{padding:9px 20px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;gap:8px;background:#FAFBFE}
.news-tag{font-size:10px;font-weight:700;padding:2px 7px;border-radius:4px;background:var(--blue-l);color:var(--blue);flex-shrink:0;margin-top:1px;text-transform:uppercase;letter-spacing:.3px}
.news-text{font-size:11px;color:var(--t2);line-height:1.55;flex:1}
.news-src{font-size:10px;color:var(--t3);margin-top:2px}
/* recommendation block */
.rec-block{padding:18px 20px;border-bottom:1px solid var(--border)}
.rec-block-title{font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:12px}
.rec-banner{border-radius:9px;padding:14px;margin-bottom:14px;border:1.5px solid}
.rec-top-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}
.rec-strat-name{font-size:15px;font-weight:800;letter-spacing:-.3px}
.rec-dir{font-size:11px;color:var(--t2);margin-top:2px}
.rec-conf{text-align:right;flex-shrink:0}
.rec-strike-row{display:flex;flex-wrap:wrap;gap:10px}
.rec-chip{display:flex;flex-direction:column;padding:7px 12px;border-radius:6px;background:rgba(255,255,255,.7);border:1px solid rgba(0,0,0,.06)}
.rec-chip-l{font-size:9px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--t3);margin-bottom:3px}
.rec-chip-v{font-size:13px;font-weight:700;font-family:'JetBrains Mono',monospace}
/* P&L row */
.pnl-row{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px}
.pnl-box{padding:9px 11px;border-radius:7px;border:1px solid var(--border);background:var(--bg)}
.pnl-l{font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:3px}
.pnl-v{font-size:12px;font-weight:700;font-family:'JetBrains Mono',monospace}
/* greeks row */
.greeks-row{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:14px}
.greek-box{padding:8px 10px;background:var(--surface2);border-radius:6px;text-align:center}
.greek-sym{font-size:14px;font-weight:700;color:var(--t2);margin-bottom:2px}
.greek-val{font-size:11px;font-family:'JetBrains Mono',monospace;color:var(--t1);font-weight:500}
.greek-lbl{font-size:9px;color:var(--t3);margin-top:1px}
/* plain english */
.plain-block{padding:16px 20px;border-bottom:1px solid var(--border);background:#FAFCFF}
.plain-title{font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--blue);margin-bottom:8px;display:flex;align-items:center;gap:5px}
.plain-text{font-size:13px;line-height:1.75;color:var(--t2)}
.plain-text strong{color:var(--t1);font-weight:600}
/* risk + pass */
.risk-block{padding:12px 20px;border-bottom:1px solid var(--border);background:#FFFBF5}
.risk-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--amber);margin-bottom:5px}
.risk-text{font-size:12px;line-height:1.65;color:#78350F}
.pass-block{padding:12px 20px;border-bottom:1px solid var(--border)}
.pass-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:5px}
.pass-text{font-size:12px;line-height:1.65;color:var(--t2)}
/* alt trade */
.alt-block{padding:12px 20px;border-bottom:1px solid var(--border)}
.alt-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:6px}
.alt-inner{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;background:var(--bg);border-radius:7px;border:1px solid var(--border)}
.alt-text{font-size:12px;line-height:1.6;color:var(--t2)}
/* scores */
.scores-block{padding:14px 20px}
.scores-title{font-size:10px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--t3);margin-bottom:10px}
.score-row{display:flex;align-items:center;gap:8px;margin-bottom:5px}
.score-nm{font-size:11px;color:var(--t2);min-width:130px}
.score-tr{flex:1;height:5px;background:var(--surface2);border-radius:3px;overflow:hidden}
.score-fi{height:100%;border-radius:3px;transition:width 1s ease}
.score-n{font-size:11px;font-weight:700;min-width:26px;text-align:right;font-family:'JetBrains Mono',monospace}
/* loading state */
.card-loading{padding:32px 20px;display:flex;flex-direction:column;align-items:center;gap:10px}
.ld-spinner{width:28px;height:28px;border:2.5px solid var(--border);border-top-color:var(--blue);border-radius:50%;animation:spin .7s linear infinite}
.ld-text{font-size:13px;color:var(--t3)}
.ld-steps{font-size:11px;color:var(--border2);text-align:center;line-height:1.8}
/* error state */
.card-err{padding:28px 20px;text-align:center}
.card-err-ico{font-size:30px;margin-bottom:8px}
.card-err-msg{font-size:13px;color:var(--t2);line-height:1.6;margin-bottom:14px}
.btn-retry{padding:7px 16px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--blue);font-size:12px;font-weight:600;cursor:pointer;font-family:inherit;transition:background .15s}
.btn-retry:hover{background:var(--blue-l)}
/* strategy color classes */
.s-sp{background:var(--purple-l);border-color:#C4B5FD}.b-sp{background:var(--purple-l);color:var(--purple)}
.s-sc{background:var(--amber-l);border-color:#FCD34D}.b-sc{background:var(--amber-l);color:var(--amber)}
.s-bc{background:var(--green-l);border-color:#6EE7B7}.b-bc{background:var(--green-l);color:var(--green)}
.s-bp{background:var(--red-l);border-color:#FCA5A5}.b-bp{background:var(--red-l);color:var(--red)}
.s-st{background:var(--blue-l);border-color:#93C5FD}.b-st{background:var(--blue-l);color:var(--blue)}
.s-sg{background:var(--blue-l);border-color:#BFDBFE}.b-sg{background:var(--blue-l);color:var(--blue)}
.s-ic{background:#F0FDF4;border-color:#86EFAC}.b-ic{background:#F0FDF4;color:#16A34A}
.s-bs{background:var(--green-l);border-color:#6EE7B7}.b-bs{background:var(--green-l);color:var(--green)}
.s-ps{background:var(--red-l);border-color:#FCA5A5}.b-ps{background:var(--red-l);color:var(--red)}
.s-df{background:var(--bg);border-color:var(--border)}.b-df{background:var(--bg);color:var(--t2)}
footer{padding:20px 28px;text-align:center;font-size:11px;color:var(--t3);border-top:1px solid var(--border);line-height:1.8;background:var(--surface)}
@media(max-width:700px){.page{padding:14px}.stats-band{grid-template-columns:repeat(2,1fr)}.pnl-row{grid-template-columns:repeat(2,1fr)}.greeks-row{grid-template-columns:repeat(2,1fr)}.hdr{padding:0 14px}}
</style>
</head>
<body>
<!-- HEADER -->
<header class="hdr">
<div class="hdr-in">
<div class="logo">
<div class="logo-mark">πŸ“ˆ</div>
<div>
<div class="logo-title">Options Intelligence</div>
<div class="logo-sub">MSFT Β· BMNR Β· IREN Β· CRCL β€” Live AI Recommendations</div>
</div>
</div>
<div class="hdr-right">
<span class="hdr-vix" id="hdr-vix"></span>
<span class="hdr-time" id="hdr-time"></span>
<button class="btn-refresh" id="btn-refresh" onclick="refreshAll()" disabled>
<svg id="ref-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 019-9 9.75 9.75 0 016.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 01-9 9 9.75 9.75 0 01-6.74-2.74L3 16"/><path d="M3 21v-5h5"/></svg>
Refresh
</button>
</div>
</div>
</header>
<!-- GATE -->
<div id="gate">
<div class="gate-card">
<div class="gate-logo">πŸ“Š</div>
<div class="gate-h">Options Intelligence Dashboard</div>
<div class="gate-p">Live AI-powered options recommendations for <strong>MSFT, BMNR, IREN, CRCL</strong>. Each refresh searches live market data and generates a fresh trade recommendation with plain English explanations.</div>
<div class="gate-lbl">Anthropic API Key</div>
<input type="password" id="gate-key" class="gate-inp" placeholder="sk-ant-api03-…"/>
<div class="gate-hint">Your key stays in your browser only β€” never stored anywhere. Get one free at <a href="https://console.anthropic.com" target="_blank">console.anthropic.com</a>. Cost per refresh: ~$0.08–0.12.</div>
<div class="gate-err" id="gate-err"></div>
<button class="btn-start" onclick="startApp()">Launch Dashboard β†’</button>
<div class="feat-grid">
<div class="feat"><div class="feat-ic">πŸ”</div><div>Live web search for current prices, IV and news on every refresh</div></div>
<div class="feat"><div class="feat-ic">πŸ“°</div><div>News sources shown β€” know where the information came from</div></div>
<div class="feat"><div class="feat-ic">πŸ’¬</div><div>Plain English explanation of every recommendation</div></div>
<div class="feat"><div class="feat-ic">🎯</div><div>Exact strike, expiry, entry price and breakevens</div></div>
<div class="feat"><div class="feat-ic">⚑</div><div>Greeks: Delta, Gamma, Theta, Vega for every trade</div></div>
<div class="feat"><div class="feat-ic">⚠️</div><div>Risk scenario and "skip if" conditions per ticker</div></div>
</div>
</div>
</div>
<!-- APP -->
<div id="app" style="display:none">
<div class="page">
<!-- Summary strip -->
<div class="summary-strip">
<div class="summary-hdr">
<span class="summary-hdr-title">πŸ“‹ At-a-Glance Summary β€” What to trade today</span>
<span style="font-size:11px;color:var(--t3)" id="summary-ts"></span>
</div>
<table class="summary-table" id="summary-tbl">
<thead>
<tr>
<th>Ticker</th>
<th>Price</th>
<th>Strategy</th>
<th>Strike</th>
<th>Expiry / DTE</th>
<th>Entry (Mid)</th>
<th>Max Profit</th>
<th>Max Loss</th>
<th>Confidence</th>
</tr>
</thead>
<tbody id="summary-body">
<tr class="summary-loading">
<td colspan="9" style="padding:18px 16px;color:var(--t3);font-style:italic;font-size:12px">⏳ Loading live data…</td>
</tr>
</tbody>
</table>
</div>
<!-- Ticker cards -->
<div class="cards-grid" id="cards-grid"></div>
</div>
<footer>
Options Intelligence Β· Powered by Claude AI + Live Web Search<br/>
<strong>⚠ Not financial advice.</strong> Options trading involves substantial risk of loss. Always verify recommendations independently before placing any trade.
</footer>
</div>
<script>
'use strict';
// ── CONFIG ──────────────────────────────────────────────────
const TICKERS = [
{sym:'MSFT', name:'Microsoft Corporation'},
{sym:'BMNR', name:'BitMine Immersion Technologies'},
{sym:'IREN', name:'Iris Energy'},
{sym:'CRCL', name:'Circle Internet Financial'},
];
// Strategy type β†’ CSS class key
const TYPE_MAP = {
sell_put:'sp', sell_call:'sc', buy_call:'bc', buy_put:'bp',
straddle:'st', strangle:'sg', condor:'ic',
bull_spread:'bs', bear_spread:'ps',
};
let API_KEY = '';
let running = false;
let results = {}; // sym β†’ parsed data
// ── GATE ─────────────────────────────────────────────────────
document.getElementById('gate-key').addEventListener('keydown', e => { if(e.key==='Enter') startApp(); });
function startApp(){
const k = document.getElementById('gate-key').value.trim();
const err = document.getElementById('gate-err');
if(!k.startsWith('sk-ant-')){ err.textContent='⚠ Key must start with sk-ant-'; err.style.display='block'; return; }
err.style.display='none';
API_KEY = k;
document.getElementById('gate').style.display='none';
document.getElementById('app').style.display='block';
document.getElementById('btn-refresh').disabled=false;
initCards();
refreshAll();
}
// ── INIT CARDS ────────────────────────────────────────────────
function initCards(){
const grid = document.getElementById('cards-grid');
grid.innerHTML='';
TICKERS.forEach((t,i)=>{
const d=document.createElement('div');
d.className='tcard';
d.id='card-'+t.sym;
d.style.animationDelay=(i*0.08)+'s';
d.innerHTML=loadingHTML(t);
grid.appendChild(d);
});
}
function loadingHTML(t){
return `<div class="tcard-hdr"><div><div class="tcard-ticker">${t.sym}</div><div class="tcard-name">${t.name}</div></div></div>
<div class="card-loading"><div class="ld-spinner"></div><div class="ld-text">Searching live data…</div>
<div class="ld-steps">Fetching price &amp; options chain<br>Calculating IV Rank &amp; Greeks<br>Generating AI recommendation</div></div>`;
}
// ── REFRESH ALL ───────────────────────────────────────────────
async function refreshAll(){
if(running) return;
running=true;
results={};
const btn=document.getElementById('btn-refresh');
const ico=document.getElementById('ref-icon');
btn.disabled=true; ico.classList.add('spin');
document.getElementById('summary-body').innerHTML=
'<tr><td colspan="9" style="padding:18px 16px;color:var(--t3);font-style:italic;font-size:12px">⏳ Loading…</td></tr>';
// Reset cards
TICKERS.forEach(t=>{ document.getElementById('card-'+t.sym).innerHTML=loadingHTML(t); });
// Run all in parallel
await Promise.allSettled(TICKERS.map(t=>fetchAndRender(t)));
// Rebuild summary after all done
buildSummary();
const now=new Date();
const ts=now.toLocaleTimeString('en-US',{hour:'2-digit',minute:'2-digit'});
document.getElementById('hdr-time').textContent='Updated '+ts;
document.getElementById('summary-ts').textContent='Last updated '+ts;
ico.classList.remove('spin');
btn.disabled=false;
running=false;
}
// ── FETCH + RENDER ONE TICKER ─────────────────────────────────
async function fetchAndRender(ticker){
try{
const d=await callClaude(ticker);
results[ticker.sym]=d;
renderCard(ticker,d);
}catch(e){
renderError(ticker,e.message);
}
}
// ── CLAUDE API ────────────────────────────────────────────────
async function callClaude(ticker){
const today=new Date().toISOString().split('T')[0];
const dow=new Date().toLocaleDateString('en-US',{weekday:'long'});
const resp=await fetch('https://api.anthropic.com/v1/messages',{
method:'POST',
headers:{
'Content-Type':'application/json',
'x-api-key':API_KEY,
'anthropic-version':'2023-06-01',
'anthropic-dangerous-direct-browser-access':'true',
},
body:JSON.stringify({
model:'claude-sonnet-4-20250514',
max_tokens:1800,
tools:[{type:'web_search_20250305',name:'web_search'}],
system:`You are a senior options trader and quantitative analyst. Today is ${dow} ${today}.
Your job: Search for LIVE current data on the given stock ticker, then produce a precise options recommendation as JSON.
RULES:
1. Always search the web first β€” never use stale training data for prices or IV
2. Search for: current stock price, today's % change, options IV, recent news/catalysts, upcoming earnings, current VIX
3. After gathering data, return ONLY valid JSON matching the exact schema. No markdown fences, no prose outside the JSON.
4. Include the actual source name (e.g. "Reuters", "Yahoo Finance", "Bloomberg") for any news cited.`,
messages:[{role:'user',content:buildPrompt(ticker,today)}],
}),
});
if(!resp.ok){
const e=await resp.json().catch(()=>({}));
throw new Error((e.error&&e.error.message)||'API error '+resp.status);
}
const data=await resp.json();
// Extract text blocks (web search tool may produce mixed content blocks)
const text=(data.content||[]).filter(b=>b.type==='text').map(b=>b.text).join('');
// Robust JSON extraction
let js=text;
const fm=js.match(/```(?:json)?\s*([\s\S]*?)```/);
if(fm) js=fm[1];
const a=js.indexOf('{'), b=js.lastIndexOf('}');
if(a!==-1&&b!==-1) js=js.slice(a,b+1);
try{ return JSON.parse(js); }
catch(e){ throw new Error('Could not parse AI response β€” try refreshing.'); }
}
// ── PROMPT ────────────────────────────────────────────────────
function buildPrompt(ticker,today){
return `Analyse ticker: ${ticker.sym} (${ticker.name}). Today: ${today}.
STEP 1 β€” Search the web for:
β€’ Current price and today's % change for ${ticker.sym}
β€’ Current implied volatility (IV) and options chain activity
β€’ Most recent news or catalyst (past 7 days) β€” note the SOURCE
β€’ Next earnings date for ${ticker.sym}
β€’ Current VIX level and US 3-month T-bill yield
STEP 2 β€” Respond with ONLY this exact JSON (no other text):
{
"ticker": "${ticker.sym}",
"company": "${ticker.name}",
"price": <number>,
"price_change_pct": <number e.g. 1.23>,
"price_trend": "<BULLISH|BEARISH|NEUTRAL>",
"vix": <number>,
"risk_free_rate": <number e.g. 4.8>,
"iv_estimate": <number e.g. 42.0>,
"iv_rank": <number 0-100>,
"iv_regime": "<HIGH_SELL_PREMIUM|LOW_BUY_OPTIONS|NEUTRAL>",
"hv_30d": <number>,
"rsi_estimate": <number>,
"sma20": <number>,
"sma50": <number>,
"earnings_date": "<YYYY-MM-DD or Unknown>",
"days_to_earnings": <number or null>,
"has_liquid_options": <true|false>,
"options_note": "<one line on liquidity>",
"news_headline": "<most important recent news headline in one sentence>",
"news_source": "<source name e.g. Reuters, Yahoo Finance, Bloomberg, SEC filing>",
"news_date": "<YYYY-MM-DD or approximate>",
"news_impact": "<BULLISH|BEARISH|NEUTRAL β€” how this news affects the options strategy>",
"data_quality": "<LIVE|ESTIMATED>",
"top_strategy": {
"name": "<Sell Put|Sell Covered Call|Buy Call|Buy Put|Buy Straddle|Buy Strangle|Iron Condor|Bull Call Spread|Bear Put Spread>",
"type_class": "<sell_put|sell_call|buy_call|buy_put|straddle|strangle|condor|bull_spread|bear_spread>",
"direction": "<Bullish|Bearish|Neutral|Non-Directional>",
"confidence": "<HIGH|MEDIUM|LOW>",
"strike": "<e.g. $415>",
"expiry": "<e.g. Apr 17 2025>",
"dte": "<e.g. 42 DTE>",
"entry_mid": "<e.g. $3.20>",
"max_profit": "<e.g. $320 per contract>",
"max_loss": "<e.g. $680 per contract>",
"breakeven": "<e.g. $411.80 or Up: $420 / Down: $400>",
"delta": "<e.g. -0.28>",
"gamma": "<e.g. 0.012>",
"theta": "<e.g. -$0.09/day>",
"vega": "<e.g. $0.18 per 1pt IV>",
"technical_rationale": "<2-3 sentences: the professional analysis using IV rank, trend, Greeks, VIX>",
"plain_english": "<3-4 sentences in simple language explaining what this trade is, why it makes sense right now, and what outcome you are expecting. Avoid jargon. Write as if explaining to a smart friend who does not trade options.>",
"risk_scenario": "<one specific event that immediately kills this trade>",
"pass_if": "<two specific conditions under which you skip this trade today>"
},
"alternative_strategy": {
"name": "<strategy name>",
"type_class": "<type class>",
"direction": "<direction>",
"strike": "<strike>",
"expiry": "<expiry>",
"entry_mid": "<mid price>",
"rationale": "<2 sentences: why this is the second best option>"
},
"strategy_scores": [
{"name":"Sell Put","score":<0-100>},
{"name":"Sell Call","score":<0-100>},
{"name":"Buy Call","score":<0-100>},
{"name":"Buy Put","score":<0-100>},
{"name":"Buy Straddle","score":<0-100>},
{"name":"Iron Condor","score":<0-100>},
{"name":"Bull Spread","score":<0-100>},
{"name":"Bear Spread","score":<0-100>}
]
}
If ${ticker.sym} has no liquid options (very small cap / newly listed), set has_liquid_options:false and adjust top_strategy to an equity-level recommendation. Always include your news source.`;
}
// ── BUILD SUMMARY TABLE ───────────────────────────────────────
function buildSummary(){
const tbody=document.getElementById('summary-body');
if(!Object.keys(results).length){ tbody.innerHTML='<tr><td colspan="9" style="padding:16px;color:var(--t3);font-size:12px">No results yet.</td></tr>'; return; }
tbody.innerHTML='';
TICKERS.forEach(t=>{
const d=results[t.sym];
if(!d){
const tr=document.createElement('tr');
tr.innerHTML=`<td class="sticker">${t.sym}</td><td colspan="8" style="color:var(--t3);font-size:12px;font-style:italic">Failed to load</td>`;
tbody.appendChild(tr);
return;
}
const ts=d.top_strategy||{};
const tc=TYPE_MAP[ts.type_class]||'df';
const chgSign=d.price_change_pct>=0?'+':'';
const chgCls=d.price_change_pct>0?'up':d.price_change_pct<0?'dn':'fl';
const confCls=ts.confidence==='HIGH'?'conf-high':ts.confidence==='MEDIUM'?'conf-med':'conf-low';
const tr=document.createElement('tr');
tr.innerHTML=`
<td><span class="sticker">${t.sym}</span><br><span style="font-size:10px;color:var(--t3)">${t.name}</span></td>
<td><span class="sprice">$${fmt(d.price)}</span><span class="schg ${chgCls}">${chgSign}${fmt(d.price_change_pct)}%</span></td>
<td><span class="strategy-pill b-${tc}">${ts.name||'β€”'}</span></td>
<td><span class="mono">${ts.strike||'β€”'}</span></td>
<td><span class="mono" style="font-size:11px">${ts.expiry||'β€”'}<br><span style="color:var(--t3)">${ts.dte||''}</span></span></td>
<td><span class="mono">${ts.entry_mid||'β€”'}</span></td>
<td style="color:var(--green);font-weight:600;font-size:12px">${ts.max_profit||'β€”'}</td>
<td style="color:var(--red);font-weight:600;font-size:12px">${ts.max_loss||'β€”'}</td>
<td><span class="conf-badge ${confCls}">${ts.confidence||'β€”'}</span></td>`;
tbody.appendChild(tr);
});
}
// ── RENDER CARD ───────────────────────────────────────────────
function renderCard(ticker,d){
const card=document.getElementById('card-'+ticker.sym);
if(!card) return;
// Update VIX in header once
if(d.vix && document.getElementById('hdr-vix').style.display==='none'||!document.getElementById('hdr-vix').textContent){
const vixEl=document.getElementById('hdr-vix');
vixEl.textContent=`VIX ${fmt(d.vix)}`;
vixEl.style.display='block';
}
const ts=d.top_strategy||{};
const alt=d.alternative_strategy||{};
const tc=TYPE_MAP[ts.type_class]||'df';
const altc=TYPE_MAP[alt.type_class]||'df';
const chgSign=d.price_change_pct>=0?'+':'';
const chgCls=d.price_change_pct>0?'up':d.price_change_pct<0?'dn':'fl';
const ivFill=Math.min(100,d.iv_rank||0);
const ivColor=d.iv_rank>60?'var(--red)':d.iv_rank<35?'var(--green)':'var(--amber)';
const regClass=d.iv_regime==='HIGH_SELL_PREMIUM'?'rt-sell':d.iv_regime==='LOW_BUY_OPTIONS'?'rt-buy':'rt-neutral';
const regLabel=d.iv_regime==='HIGH_SELL_PREMIUM'?'Sell Premium ↑':d.iv_regime==='LOW_BUY_OPTIONS'?'Buy Options ↓':'Neutral IV';
const trendColor=d.price_trend==='BULLISH'?'var(--green)':d.price_trend==='BEARISH'?'var(--red)':'var(--amber)';
const dq=d.data_quality||'';
const dqPill=dq.includes('LIVE')?
`<span style="font-size:10px;padding:2px 7px;border-radius:4px;background:var(--green-m);color:var(--green);font-weight:700">● LIVE</span>`:
`<span style="font-size:10px;padding:2px 7px;border-radius:4px;background:var(--amber-m);color:var(--amber);font-weight:700">◐ EST</span>`;
const confCls=ts.confidence==='HIGH'?'conf-high':ts.confidence==='MEDIUM'?'conf-med':'conf-low';
const scores=(d.strategy_scores||[]).sort((a,b)=>b.score-a.score);
const newsImpactColor=d.news_impact==='BULLISH'?'var(--green)':d.news_impact==='BEARISH'?'var(--red)':'var(--t3)';
card.innerHTML=`
<!-- Header -->
<div class="tcard-hdr">
<div>
<div class="tcard-ticker">${d.ticker} ${dqPill}</div>
<div class="tcard-name">${d.company}</div>
</div>
<div class="tcard-price">
<div class="price-big">$${fmt(d.price)}</div>
<div class="price-chg ${chgCls}">${chgSign}${fmt(d.price_change_pct)}%</div>
</div>
</div>
<!-- Stats -->
<div class="stats-band">
<div class="sc">
<div class="sc-l">Trend</div>
<div class="sc-v" style="color:${trendColor}">${d.price_trend||'β€”'}</div>
<div class="sc-s">RSI ${fmt(d.rsi_estimate,0)}</div>
</div>
<div class="sc">
<div class="sc-l">ATM IV</div>
<div class="sc-v">${fmt(d.iv_estimate)}%</div>
<div class="sc-s">HV30 ${fmt(d.hv_30d)}%</div>
</div>
<div class="sc">
<div class="sc-l">Earnings</div>
<div class="sc-v" style="font-size:12px">${d.earnings_date&&d.earnings_date!=='Unknown'?d.earnings_date:'β€”'}</div>
<div class="sc-s">${d.days_to_earnings?d.days_to_earnings+'d away':''}</div>
</div>
<div class="sc">
<div class="sc-l">Options</div>
<div class="sc-v" style="font-size:12px;color:${d.has_liquid_options?'var(--green)':'var(--red)'}">
${d.has_liquid_options?'βœ“ Liquid':'βœ— Thin'}
</div>
<div class="sc-s" style="font-size:9px">${(d.options_note||'').substring(0,28)}</div>
</div>
</div>
<!-- IV Rank -->
<div class="iv-bar-wrap">
<div class="iv-bar-row">
<span class="iv-bar-lbl">IV Rank: <strong>${fmt(d.iv_rank,0)}</strong> / 100</span>
<span class="iv-regime-tag ${regClass}">${regLabel}</span>
</div>
<div class="iv-track"><div class="iv-fill" style="width:${ivFill}%;background:${ivColor}"></div></div>
</div>
<!-- News -->
${d.news_headline?`<div class="news-row">
<span class="news-tag" style="color:${newsImpactColor}">News</span>
<div>
<div class="news-text">${esc(d.news_headline)}</div>
<div class="news-src">πŸ“° Source: <strong>${esc(d.news_source||'Unknown')}</strong>${d.news_date?' Β· '+d.news_date:''} Β· Impact: <span style="color:${newsImpactColor};font-weight:600">${d.news_impact||'β€”'}</span></div>
</div>
</div>`:''}
<!-- Recommendation Banner -->
<div class="rec-block">
<div class="rec-block-title">⭐ Top Recommendation</div>
<div class="rec-banner s-${tc}">
<div class="rec-top-row">
<div>
<div class="rec-strat-name">${ts.name||'β€”'}</div>
<div class="rec-dir">${ts.direction||''}</div>
</div>
<div class="rec-conf">
<span class="conf-badge ${confCls}">${ts.confidence||'β€”'}</span>
<div style="font-size:9px;color:var(--t3);margin-top:4px;text-align:right">confidence</div>
</div>
</div>
<div class="rec-strike-row">
<div class="rec-chip">
<span class="rec-chip-l">Strike</span>
<span class="rec-chip-v">${ts.strike||'β€”'}</span>
</div>
<div class="rec-chip">
<span class="rec-chip-l">Expiry</span>
<span class="rec-chip-v" style="font-size:11px">${ts.expiry||'β€”'}</span>
</div>
<div class="rec-chip">
<span class="rec-chip-l">DTE</span>
<span class="rec-chip-v">${ts.dte||'β€”'}</span>
</div>
<div class="rec-chip">
<span class="rec-chip-l">Entry Mid</span>
<span class="rec-chip-v">${ts.entry_mid||'β€”'}</span>
</div>
</div>
</div>
<!-- P&L -->
<div class="pnl-row">
<div class="pnl-box"><div class="pnl-l">Max Profit</div><div class="pnl-v" style="color:var(--green)">${ts.max_profit||'β€”'}</div></div>
<div class="pnl-box"><div class="pnl-l">Max Loss</div><div class="pnl-v" style="color:var(--red)">${ts.max_loss||'β€”'}</div></div>
<div class="pnl-box"><div class="pnl-l">Breakeven</div><div class="pnl-v" style="font-size:11px">${ts.breakeven||'β€”'}</div></div>
</div>
<!-- Greeks -->
<div class="greeks-row">
<div class="greek-box"><div class="greek-sym">Ξ”</div><div class="greek-val">${ts.delta||'β€”'}</div><div class="greek-lbl">Delta</div></div>
<div class="greek-box"><div class="greek-sym">Ξ“</div><div class="greek-val">${ts.gamma||'β€”'}</div><div class="greek-lbl">Gamma</div></div>
<div class="greek-box"><div class="greek-sym">ΞΈ</div><div class="greek-val">${ts.theta||'β€”'}</div><div class="greek-lbl">Theta/day</div></div>
<div class="greek-box"><div class="greek-sym">Ξ½</div><div class="greek-val">${ts.vega||'β€”'}</div><div class="greek-lbl">Vega/pt</div></div>
</div>
<!-- Professional rationale -->
${ts.technical_rationale?`<div style="font-size:12px;line-height:1.7;color:var(--t2);margin-bottom:0">${esc(ts.technical_rationale)}</div>`:''}
</div>
<!-- Plain English -->
${ts.plain_english?`<div class="plain-block">
<div class="plain-title">πŸ’¬ In Plain English</div>
<div class="plain-text">${esc(ts.plain_english)}</div>
</div>`:''}
<!-- Risk -->
${ts.risk_scenario?`<div class="risk-block">
<div class="risk-tag">⚠ Key Risk</div>
<div class="risk-text">${esc(ts.risk_scenario)}</div>
</div>`:''}
<!-- Pass if -->
${ts.pass_if?`<div class="pass-block">
<div class="pass-tag">🚫 Skip this trade if…</div>
<div class="pass-text">${esc(ts.pass_if)}</div>
</div>`:''}
<!-- Alternative -->
${alt.name?`<div class="alt-block">
<div class="alt-tag">πŸ”€ Alternative Trade</div>
<div class="alt-inner">
<span class="strategy-pill b-${altc}" style="font-size:10px;flex-shrink:0">${alt.name}</span>
<div class="alt-text"><strong>${alt.strike||''} ${alt.expiry||''}</strong> Β· Mid ${alt.entry_mid||'β€”'}<br>${esc(alt.rationale||'')}</div>
</div>
</div>`:''}
<!-- Strategy Scores -->
<div class="scores-block">
<div class="scores-title">All Strategy Scores</div>
${scores.map(s=>{
const sc=s.score||0;
const col=sc>=70?'var(--green)':sc>=50?'var(--blue)':sc>=35?'var(--amber)':'var(--border2)';
return `<div class="score-row">
<span class="score-nm">${s.name}</span>
<div class="score-tr"><div class="score-fi" style="width:${sc}%;background:${col}"></div></div>
<span class="score-n" style="color:${col}">${sc}</span>
</div>`;
}).join('')}
</div>`;
}
// ── RENDER ERROR ──────────────────────────────────────────────
function renderError(ticker,msg){
const card=document.getElementById('card-'+ticker.sym);
if(!card) return;
card.innerHTML=`
<div class="tcard-hdr"><div><div class="tcard-ticker">${ticker.sym}</div><div class="tcard-name">${ticker.name}</div></div></div>
<div class="card-err">
<div class="card-err-ico">⚠️</div>
<div class="card-err-msg">${esc(msg)}</div>
<button class="btn-retry" onclick="retryOne('${ticker.sym}')">↩ Retry ${ticker.sym}</button>
</div>`;
}
async function retryOne(sym){
const t=TICKERS.find(x=>x.sym===sym);
if(!t) return;
document.getElementById('card-'+sym).innerHTML=loadingHTML(t);
await fetchAndRender(t);
buildSummary();
}
// ── HELPERS ───────────────────────────────────────────────────
function fmt(n,dec=2){
if(n===null||n===undefined||n==='') return 'β€”';
const x=parseFloat(n);
return isNaN(x)?String(n):x.toFixed(dec);
}
function esc(s){
return (s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
</script>
</body>
</html>