| <!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 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> |
|
|
| |
| <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> |
|
|
| |
| <div id="app" style="display:none"> |
| <div class="page"> |
|
|
| |
| <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> |
|
|
| |
| <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 & options chain<br>Calculating IV Rank & 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=` |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| ${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>`:''} |
| |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| ${ts.technical_rationale?`<div style="font-size:12px;line-height:1.7;color:var(--t2);margin-bottom:0">${esc(ts.technical_rationale)}</div>`:''} |
| </div> |
| |
| |
| ${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>`:''} |
| |
| |
| ${ts.risk_scenario?`<div class="risk-block"> |
| <div class="risk-tag">β Key Risk</div> |
| <div class="risk-text">${esc(ts.risk_scenario)}</div> |
| </div>`:''} |
| |
| |
| ${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>`:''} |
| |
| |
| ${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>`:''} |
| |
| |
| <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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); |
| } |
| </script> |
| </body> |
| </html> |
|
|