Upload index.html
Browse files- index.html +781 -0
index.html
ADDED
|
@@ -0,0 +1,781 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8"/>
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
+
<title>Options Intelligence β MSFT Β· BMNR Β· IREN Β· CRCL</title>
|
| 7 |
+
<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"/>
|
| 8 |
+
<style>
|
| 9 |
+
:root{
|
| 10 |
+
--bg:#F7F8FA;--surface:#fff;--surface2:#F0F2F7;
|
| 11 |
+
--border:#E2E5EE;--border2:#C8CCDB;
|
| 12 |
+
--t1:#0D0F1A;--t2:#3D4460;--t3:#8B92AA;
|
| 13 |
+
--blue:#1A56DB;--blue-l:#EBF1FF;--blue-d:#1341B0;
|
| 14 |
+
--green:#0A7A45;--green-l:#E8F8F0;--green-m:#D1F0E0;
|
| 15 |
+
--red:#CC2828;--red-l:#FFF0F0;--red-m:#FFD8D8;
|
| 16 |
+
--amber:#B45309;--amber-l:#FFF8ED;--amber-m:#FDECC8;
|
| 17 |
+
--purple:#6326C7;--purple-l:#F3EEFF;
|
| 18 |
+
--sh:0 1px 3px rgba(0,0,0,.06),0 1px 2px rgba(0,0,0,.04);
|
| 19 |
+
--sh2:0 4px 16px rgba(0,0,0,.09),0 2px 6px rgba(0,0,0,.05);
|
| 20 |
+
--r:10px;
|
| 21 |
+
}
|
| 22 |
+
*{box-sizing:border-box;margin:0;padding:0}
|
| 23 |
+
body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--t1);-webkit-font-smoothing:antialiased;min-height:100vh}
|
| 24 |
+
a{color:var(--blue);text-decoration:none}a:hover{text-decoration:underline}
|
| 25 |
+
button,input{font-family:inherit;cursor:pointer}
|
| 26 |
+
textarea:focus,input:focus{outline:none}
|
| 27 |
+
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg)}
|
| 28 |
+
::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px}
|
| 29 |
+
|
| 30 |
+
/* ββ HEADER ββ */
|
| 31 |
+
.hdr{background:var(--surface);border-bottom:1px solid var(--border);padding:0 28px;position:sticky;top:0;z-index:100;box-shadow:var(--sh)}
|
| 32 |
+
.hdr-in{max-width:1280px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:58px}
|
| 33 |
+
.logo{display:flex;align-items:center;gap:10px}
|
| 34 |
+
.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)}
|
| 35 |
+
.logo-title{font-size:16px;font-weight:700;letter-spacing:-.4px}
|
| 36 |
+
.logo-sub{font-size:11px;color:var(--t3);margin-top:1px}
|
| 37 |
+
.hdr-right{display:flex;align-items:center;gap:12px}
|
| 38 |
+
.hdr-time{font-size:11px;color:var(--t3)}
|
| 39 |
+
.hdr-vix{font-size:12px;font-weight:600;padding:4px 10px;border-radius:6px;background:var(--surface2);border:1px solid var(--border);display:none}
|
| 40 |
+
.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}
|
| 41 |
+
.btn-refresh:hover:not(:disabled){background:var(--blue-d);transform:translateY(-1px);box-shadow:0 3px 10px rgba(26,86,219,.35)}
|
| 42 |
+
.btn-refresh:disabled{background:var(--border2);color:var(--t3);cursor:not-allowed;transform:none;box-shadow:none}
|
| 43 |
+
@keyframes spin{to{transform:rotate(360deg)}}
|
| 44 |
+
.spin{animation:spin .8s linear infinite}
|
| 45 |
+
@keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
|
| 46 |
+
|
| 47 |
+
/* ββ GATE ββ */
|
| 48 |
+
#gate{min-height:calc(100vh - 58px);display:flex;align-items:center;justify-content:center;padding:32px 16px}
|
| 49 |
+
.gate-card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:40px;max-width:460px;width:100%;box-shadow:var(--sh2)}
|
| 50 |
+
.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)}
|
| 51 |
+
.gate-h{font-size:21px;font-weight:700;text-align:center;margin-bottom:8px;letter-spacing:-.4px}
|
| 52 |
+
.gate-p{font-size:13px;color:var(--t2);text-align:center;line-height:1.65;margin-bottom:28px}
|
| 53 |
+
.gate-lbl{font-size:12px;font-weight:600;color:var(--t2);margin-bottom:6px}
|
| 54 |
+
.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}
|
| 55 |
+
.gate-inp:focus{border-color:var(--blue);background:#fff}
|
| 56 |
+
.gate-hint{font-size:11px;color:var(--t3);line-height:1.6;margin-bottom:20px}
|
| 57 |
+
.gate-err{font-size:12px;color:var(--red);margin-bottom:10px;display:none}
|
| 58 |
+
.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}
|
| 59 |
+
.btn-start:hover{background:var(--blue-d)}
|
| 60 |
+
.feat-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:24px}
|
| 61 |
+
.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}
|
| 62 |
+
.feat-ic{font-size:15px;flex-shrink:0}
|
| 63 |
+
|
| 64 |
+
/* ββ PAGE ββ */
|
| 65 |
+
.page{max-width:1280px;margin:0 auto;padding:24px 28px 48px}
|
| 66 |
+
|
| 67 |
+
/* ββ SUMMARY STRIP ββ */
|
| 68 |
+
.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)}
|
| 69 |
+
.summary-hdr{padding:12px 20px;background:var(--surface2);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
|
| 70 |
+
.summary-hdr-title{font-size:12px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--t3)}
|
| 71 |
+
.summary-table{width:100%;border-collapse:collapse}
|
| 72 |
+
.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)}
|
| 73 |
+
.summary-table td{padding:13px 16px;font-size:13px;border-bottom:1px solid var(--border);vertical-align:middle}
|
| 74 |
+
.summary-table tr:last-child td{border-bottom:none}
|
| 75 |
+
.summary-table tr:hover td{background:#FAFBFF}
|
| 76 |
+
.sticker{font-size:14px;font-weight:800;letter-spacing:-.3px}
|
| 77 |
+
.sprice{font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:500}
|
| 78 |
+
.schg{font-size:11px;font-weight:600;padding:2px 7px;border-radius:4px;font-family:'JetBrains Mono',monospace;margin-left:6px}
|
| 79 |
+
.up{background:var(--green-m);color:var(--green)}.dn{background:var(--red-m);color:var(--red)}.fl{background:var(--surface2);color:var(--t3)}
|
| 80 |
+
.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}
|
| 81 |
+
.conf-badge{font-size:10px;font-weight:700;padding:2px 7px;border-radius:4px;text-transform:uppercase;letter-spacing:.3px}
|
| 82 |
+
.conf-high{background:var(--green-m);color:var(--green)}
|
| 83 |
+
.conf-med{background:var(--amber-m);color:var(--amber)}
|
| 84 |
+
.conf-low{background:var(--surface2);color:var(--t3)}
|
| 85 |
+
.mono{font-family:'JetBrains Mono',monospace;font-size:12px}
|
| 86 |
+
.summary-loading td{color:var(--t3);font-style:italic;font-size:12px}
|
| 87 |
+
|
| 88 |
+
/* ββ TICKER CARDS ββ */
|
| 89 |
+
.cards-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
|
| 90 |
+
@media(max-width:900px){.cards-grid{grid-template-columns:1fr}}
|
| 91 |
+
|
| 92 |
+
.tcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);animation:fadeUp .35s ease both}
|
| 93 |
+
.tcard:hover{box-shadow:var(--sh2)}
|
| 94 |
+
|
| 95 |
+
.tcard-hdr{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
|
| 96 |
+
.tcard-ticker{font-size:20px;font-weight:800;letter-spacing:-.5px}
|
| 97 |
+
.tcard-name{font-size:11px;color:var(--t3);margin-top:2px}
|
| 98 |
+
.tcard-price{text-align:right;flex-shrink:0}
|
| 99 |
+
.price-big{font-size:20px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
| 100 |
+
.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}
|
| 101 |
+
|
| 102 |
+
/* stats row */
|
| 103 |
+
.stats-band{display:grid;grid-template-columns:repeat(4,1fr);border-bottom:1px solid var(--border)}
|
| 104 |
+
.sc{padding:11px 14px;border-right:1px solid var(--border)}
|
| 105 |
+
.sc:last-child{border-right:none}
|
| 106 |
+
.sc-l{font-size:10px;font-weight:600;letter-spacing:.7px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
|
| 107 |
+
.sc-v{font-size:14px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
| 108 |
+
.sc-s{font-size:10px;color:var(--t3);margin-top:2px}
|
| 109 |
+
|
| 110 |
+
/* IV bar */
|
| 111 |
+
.iv-bar-wrap{padding:13px 20px;border-bottom:1px solid var(--border)}
|
| 112 |
+
.iv-bar-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:7px}
|
| 113 |
+
.iv-bar-lbl{font-size:11px;font-weight:600;color:var(--t2)}
|
| 114 |
+
.iv-regime-tag{font-size:10px;font-weight:700;padding:2px 9px;border-radius:10px;text-transform:uppercase;letter-spacing:.4px}
|
| 115 |
+
.rt-sell{background:var(--red-l);color:var(--red)}
|
| 116 |
+
.rt-buy{background:var(--green-l);color:var(--green)}
|
| 117 |
+
.rt-neutral{background:var(--amber-l);color:var(--amber)}
|
| 118 |
+
.iv-track{height:6px;background:var(--surface2);border-radius:3px;overflow:hidden;border:1px solid var(--border)}
|
| 119 |
+
.iv-fill{height:100%;border-radius:3px;transition:width .8s ease}
|
| 120 |
+
|
| 121 |
+
/* news pill */
|
| 122 |
+
.news-row{padding:9px 20px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;gap:8px;background:#FAFBFE}
|
| 123 |
+
.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}
|
| 124 |
+
.news-text{font-size:11px;color:var(--t2);line-height:1.55;flex:1}
|
| 125 |
+
.news-src{font-size:10px;color:var(--t3);margin-top:2px}
|
| 126 |
+
|
| 127 |
+
/* recommendation block */
|
| 128 |
+
.rec-block{padding:18px 20px;border-bottom:1px solid var(--border)}
|
| 129 |
+
.rec-block-title{font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:12px}
|
| 130 |
+
|
| 131 |
+
.rec-banner{border-radius:9px;padding:14px;margin-bottom:14px;border:1.5px solid}
|
| 132 |
+
.rec-top-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}
|
| 133 |
+
.rec-strat-name{font-size:15px;font-weight:800;letter-spacing:-.3px}
|
| 134 |
+
.rec-dir{font-size:11px;color:var(--t2);margin-top:2px}
|
| 135 |
+
.rec-conf{text-align:right;flex-shrink:0}
|
| 136 |
+
.rec-strike-row{display:flex;flex-wrap:wrap;gap:10px}
|
| 137 |
+
.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)}
|
| 138 |
+
.rec-chip-l{font-size:9px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--t3);margin-bottom:3px}
|
| 139 |
+
.rec-chip-v{font-size:13px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
| 140 |
+
|
| 141 |
+
/* P&L row */
|
| 142 |
+
.pnl-row{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px}
|
| 143 |
+
.pnl-box{padding:9px 11px;border-radius:7px;border:1px solid var(--border);background:var(--bg)}
|
| 144 |
+
.pnl-l{font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:3px}
|
| 145 |
+
.pnl-v{font-size:12px;font-weight:700;font-family:'JetBrains Mono',monospace}
|
| 146 |
+
|
| 147 |
+
/* greeks row */
|
| 148 |
+
.greeks-row{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:14px}
|
| 149 |
+
.greek-box{padding:8px 10px;background:var(--surface2);border-radius:6px;text-align:center}
|
| 150 |
+
.greek-sym{font-size:14px;font-weight:700;color:var(--t2);margin-bottom:2px}
|
| 151 |
+
.greek-val{font-size:11px;font-family:'JetBrains Mono',monospace;color:var(--t1);font-weight:500}
|
| 152 |
+
.greek-lbl{font-size:9px;color:var(--t3);margin-top:1px}
|
| 153 |
+
|
| 154 |
+
/* plain english */
|
| 155 |
+
.plain-block{padding:16px 20px;border-bottom:1px solid var(--border);background:#FAFCFF}
|
| 156 |
+
.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}
|
| 157 |
+
.plain-text{font-size:13px;line-height:1.75;color:var(--t2)}
|
| 158 |
+
.plain-text strong{color:var(--t1);font-weight:600}
|
| 159 |
+
|
| 160 |
+
/* risk + pass */
|
| 161 |
+
.risk-block{padding:12px 20px;border-bottom:1px solid var(--border);background:#FFFBF5}
|
| 162 |
+
.risk-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--amber);margin-bottom:5px}
|
| 163 |
+
.risk-text{font-size:12px;line-height:1.65;color:#78350F}
|
| 164 |
+
|
| 165 |
+
.pass-block{padding:12px 20px;border-bottom:1px solid var(--border)}
|
| 166 |
+
.pass-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:5px}
|
| 167 |
+
.pass-text{font-size:12px;line-height:1.65;color:var(--t2)}
|
| 168 |
+
|
| 169 |
+
/* alt trade */
|
| 170 |
+
.alt-block{padding:12px 20px;border-bottom:1px solid var(--border)}
|
| 171 |
+
.alt-tag{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--t3);margin-bottom:6px}
|
| 172 |
+
.alt-inner{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;background:var(--bg);border-radius:7px;border:1px solid var(--border)}
|
| 173 |
+
.alt-text{font-size:12px;line-height:1.6;color:var(--t2)}
|
| 174 |
+
|
| 175 |
+
/* scores */
|
| 176 |
+
.scores-block{padding:14px 20px}
|
| 177 |
+
.scores-title{font-size:10px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--t3);margin-bottom:10px}
|
| 178 |
+
.score-row{display:flex;align-items:center;gap:8px;margin-bottom:5px}
|
| 179 |
+
.score-nm{font-size:11px;color:var(--t2);min-width:130px}
|
| 180 |
+
.score-tr{flex:1;height:5px;background:var(--surface2);border-radius:3px;overflow:hidden}
|
| 181 |
+
.score-fi{height:100%;border-radius:3px;transition:width 1s ease}
|
| 182 |
+
.score-n{font-size:11px;font-weight:700;min-width:26px;text-align:right;font-family:'JetBrains Mono',monospace}
|
| 183 |
+
|
| 184 |
+
/* loading state */
|
| 185 |
+
.card-loading{padding:32px 20px;display:flex;flex-direction:column;align-items:center;gap:10px}
|
| 186 |
+
.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}
|
| 187 |
+
.ld-text{font-size:13px;color:var(--t3)}
|
| 188 |
+
.ld-steps{font-size:11px;color:var(--border2);text-align:center;line-height:1.8}
|
| 189 |
+
|
| 190 |
+
/* error state */
|
| 191 |
+
.card-err{padding:28px 20px;text-align:center}
|
| 192 |
+
.card-err-ico{font-size:30px;margin-bottom:8px}
|
| 193 |
+
.card-err-msg{font-size:13px;color:var(--t2);line-height:1.6;margin-bottom:14px}
|
| 194 |
+
.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}
|
| 195 |
+
.btn-retry:hover{background:var(--blue-l)}
|
| 196 |
+
|
| 197 |
+
/* strategy color classes */
|
| 198 |
+
.s-sp{background:var(--purple-l);border-color:#C4B5FD}.b-sp{background:var(--purple-l);color:var(--purple)}
|
| 199 |
+
.s-sc{background:var(--amber-l);border-color:#FCD34D}.b-sc{background:var(--amber-l);color:var(--amber)}
|
| 200 |
+
.s-bc{background:var(--green-l);border-color:#6EE7B7}.b-bc{background:var(--green-l);color:var(--green)}
|
| 201 |
+
.s-bp{background:var(--red-l);border-color:#FCA5A5}.b-bp{background:var(--red-l);color:var(--red)}
|
| 202 |
+
.s-st{background:var(--blue-l);border-color:#93C5FD}.b-st{background:var(--blue-l);color:var(--blue)}
|
| 203 |
+
.s-sg{background:var(--blue-l);border-color:#BFDBFE}.b-sg{background:var(--blue-l);color:var(--blue)}
|
| 204 |
+
.s-ic{background:#F0FDF4;border-color:#86EFAC}.b-ic{background:#F0FDF4;color:#16A34A}
|
| 205 |
+
.s-bs{background:var(--green-l);border-color:#6EE7B7}.b-bs{background:var(--green-l);color:var(--green)}
|
| 206 |
+
.s-ps{background:var(--red-l);border-color:#FCA5A5}.b-ps{background:var(--red-l);color:var(--red)}
|
| 207 |
+
.s-df{background:var(--bg);border-color:var(--border)}.b-df{background:var(--bg);color:var(--t2)}
|
| 208 |
+
|
| 209 |
+
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)}
|
| 210 |
+
@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}}
|
| 211 |
+
</style>
|
| 212 |
+
</head>
|
| 213 |
+
<body>
|
| 214 |
+
|
| 215 |
+
<!-- HEADER -->
|
| 216 |
+
<header class="hdr">
|
| 217 |
+
<div class="hdr-in">
|
| 218 |
+
<div class="logo">
|
| 219 |
+
<div class="logo-mark">π</div>
|
| 220 |
+
<div>
|
| 221 |
+
<div class="logo-title">Options Intelligence</div>
|
| 222 |
+
<div class="logo-sub">MSFT Β· BMNR Β· IREN Β· CRCL β Live AI Recommendations</div>
|
| 223 |
+
</div>
|
| 224 |
+
</div>
|
| 225 |
+
<div class="hdr-right">
|
| 226 |
+
<span class="hdr-vix" id="hdr-vix"></span>
|
| 227 |
+
<span class="hdr-time" id="hdr-time"></span>
|
| 228 |
+
<button class="btn-refresh" id="btn-refresh" onclick="refreshAll()" disabled>
|
| 229 |
+
<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>
|
| 230 |
+
Refresh
|
| 231 |
+
</button>
|
| 232 |
+
</div>
|
| 233 |
+
</div>
|
| 234 |
+
</header>
|
| 235 |
+
|
| 236 |
+
<!-- GATE -->
|
| 237 |
+
<div id="gate">
|
| 238 |
+
<div class="gate-card">
|
| 239 |
+
<div class="gate-logo">π</div>
|
| 240 |
+
<div class="gate-h">Options Intelligence Dashboard</div>
|
| 241 |
+
<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>
|
| 242 |
+
<div class="gate-lbl">Anthropic API Key</div>
|
| 243 |
+
<input type="password" id="gate-key" class="gate-inp" placeholder="sk-ant-api03-β¦"/>
|
| 244 |
+
<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>
|
| 245 |
+
<div class="gate-err" id="gate-err"></div>
|
| 246 |
+
<button class="btn-start" onclick="startApp()">Launch Dashboard β</button>
|
| 247 |
+
<div class="feat-grid">
|
| 248 |
+
<div class="feat"><div class="feat-ic">π</div><div>Live web search for current prices, IV and news on every refresh</div></div>
|
| 249 |
+
<div class="feat"><div class="feat-ic">π°</div><div>News sources shown β know where the information came from</div></div>
|
| 250 |
+
<div class="feat"><div class="feat-ic">π¬</div><div>Plain English explanation of every recommendation</div></div>
|
| 251 |
+
<div class="feat"><div class="feat-ic">π―</div><div>Exact strike, expiry, entry price and breakevens</div></div>
|
| 252 |
+
<div class="feat"><div class="feat-ic">β‘</div><div>Greeks: Delta, Gamma, Theta, Vega for every trade</div></div>
|
| 253 |
+
<div class="feat"><div class="feat-ic">β οΈ</div><div>Risk scenario and "skip if" conditions per ticker</div></div>
|
| 254 |
+
</div>
|
| 255 |
+
</div>
|
| 256 |
+
</div>
|
| 257 |
+
|
| 258 |
+
<!-- APP -->
|
| 259 |
+
<div id="app" style="display:none">
|
| 260 |
+
<div class="page">
|
| 261 |
+
|
| 262 |
+
<!-- Summary strip -->
|
| 263 |
+
<div class="summary-strip">
|
| 264 |
+
<div class="summary-hdr">
|
| 265 |
+
<span class="summary-hdr-title">π At-a-Glance Summary β What to trade today</span>
|
| 266 |
+
<span style="font-size:11px;color:var(--t3)" id="summary-ts"></span>
|
| 267 |
+
</div>
|
| 268 |
+
<table class="summary-table" id="summary-tbl">
|
| 269 |
+
<thead>
|
| 270 |
+
<tr>
|
| 271 |
+
<th>Ticker</th>
|
| 272 |
+
<th>Price</th>
|
| 273 |
+
<th>Strategy</th>
|
| 274 |
+
<th>Strike</th>
|
| 275 |
+
<th>Expiry / DTE</th>
|
| 276 |
+
<th>Entry (Mid)</th>
|
| 277 |
+
<th>Max Profit</th>
|
| 278 |
+
<th>Max Loss</th>
|
| 279 |
+
<th>Confidence</th>
|
| 280 |
+
</tr>
|
| 281 |
+
</thead>
|
| 282 |
+
<tbody id="summary-body">
|
| 283 |
+
<tr class="summary-loading">
|
| 284 |
+
<td colspan="9" style="padding:18px 16px;color:var(--t3);font-style:italic;font-size:12px">β³ Loading live dataβ¦</td>
|
| 285 |
+
</tr>
|
| 286 |
+
</tbody>
|
| 287 |
+
</table>
|
| 288 |
+
</div>
|
| 289 |
+
|
| 290 |
+
<!-- Ticker cards -->
|
| 291 |
+
<div class="cards-grid" id="cards-grid"></div>
|
| 292 |
+
|
| 293 |
+
</div>
|
| 294 |
+
<footer>
|
| 295 |
+
Options Intelligence Β· Powered by Claude AI + Live Web Search<br/>
|
| 296 |
+
<strong>β Not financial advice.</strong> Options trading involves substantial risk of loss. Always verify recommendations independently before placing any trade.
|
| 297 |
+
</footer>
|
| 298 |
+
</div>
|
| 299 |
+
|
| 300 |
+
<script>
|
| 301 |
+
'use strict';
|
| 302 |
+
|
| 303 |
+
// ββ CONFIG ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 304 |
+
const TICKERS = [
|
| 305 |
+
{sym:'MSFT', name:'Microsoft Corporation'},
|
| 306 |
+
{sym:'BMNR', name:'BitMine Immersion Technologies'},
|
| 307 |
+
{sym:'IREN', name:'Iris Energy'},
|
| 308 |
+
{sym:'CRCL', name:'Circle Internet Financial'},
|
| 309 |
+
];
|
| 310 |
+
|
| 311 |
+
// Strategy type β CSS class key
|
| 312 |
+
const TYPE_MAP = {
|
| 313 |
+
sell_put:'sp', sell_call:'sc', buy_call:'bc', buy_put:'bp',
|
| 314 |
+
straddle:'st', strangle:'sg', condor:'ic',
|
| 315 |
+
bull_spread:'bs', bear_spread:'ps',
|
| 316 |
+
};
|
| 317 |
+
|
| 318 |
+
let API_KEY = '';
|
| 319 |
+
let running = false;
|
| 320 |
+
let results = {}; // sym β parsed data
|
| 321 |
+
|
| 322 |
+
// ββ GATE βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 323 |
+
document.getElementById('gate-key').addEventListener('keydown', e => { if(e.key==='Enter') startApp(); });
|
| 324 |
+
|
| 325 |
+
function startApp(){
|
| 326 |
+
const k = document.getElementById('gate-key').value.trim();
|
| 327 |
+
const err = document.getElementById('gate-err');
|
| 328 |
+
if(!k.startsWith('sk-ant-')){ err.textContent='β Key must start with sk-ant-'; err.style.display='block'; return; }
|
| 329 |
+
err.style.display='none';
|
| 330 |
+
API_KEY = k;
|
| 331 |
+
document.getElementById('gate').style.display='none';
|
| 332 |
+
document.getElementById('app').style.display='block';
|
| 333 |
+
document.getElementById('btn-refresh').disabled=false;
|
| 334 |
+
initCards();
|
| 335 |
+
refreshAll();
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
// ββ INIT CARDS ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 339 |
+
function initCards(){
|
| 340 |
+
const grid = document.getElementById('cards-grid');
|
| 341 |
+
grid.innerHTML='';
|
| 342 |
+
TICKERS.forEach((t,i)=>{
|
| 343 |
+
const d=document.createElement('div');
|
| 344 |
+
d.className='tcard';
|
| 345 |
+
d.id='card-'+t.sym;
|
| 346 |
+
d.style.animationDelay=(i*0.08)+'s';
|
| 347 |
+
d.innerHTML=loadingHTML(t);
|
| 348 |
+
grid.appendChild(d);
|
| 349 |
+
});
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
function loadingHTML(t){
|
| 353 |
+
return `<div class="tcard-hdr"><div><div class="tcard-ticker">${t.sym}</div><div class="tcard-name">${t.name}</div></div></div>
|
| 354 |
+
<div class="card-loading"><div class="ld-spinner"></div><div class="ld-text">Searching live dataβ¦</div>
|
| 355 |
+
<div class="ld-steps">Fetching price & options chain<br>Calculating IV Rank & Greeks<br>Generating AI recommendation</div></div>`;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
// ββ REFRESH ALL βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 359 |
+
async function refreshAll(){
|
| 360 |
+
if(running) return;
|
| 361 |
+
running=true;
|
| 362 |
+
results={};
|
| 363 |
+
const btn=document.getElementById('btn-refresh');
|
| 364 |
+
const ico=document.getElementById('ref-icon');
|
| 365 |
+
btn.disabled=true; ico.classList.add('spin');
|
| 366 |
+
document.getElementById('summary-body').innerHTML=
|
| 367 |
+
'<tr><td colspan="9" style="padding:18px 16px;color:var(--t3);font-style:italic;font-size:12px">β³ Loadingβ¦</td></tr>';
|
| 368 |
+
|
| 369 |
+
// Reset cards
|
| 370 |
+
TICKERS.forEach(t=>{ document.getElementById('card-'+t.sym).innerHTML=loadingHTML(t); });
|
| 371 |
+
|
| 372 |
+
// Run all in parallel
|
| 373 |
+
await Promise.allSettled(TICKERS.map(t=>fetchAndRender(t)));
|
| 374 |
+
|
| 375 |
+
// Rebuild summary after all done
|
| 376 |
+
buildSummary();
|
| 377 |
+
|
| 378 |
+
const now=new Date();
|
| 379 |
+
const ts=now.toLocaleTimeString('en-US',{hour:'2-digit',minute:'2-digit'});
|
| 380 |
+
document.getElementById('hdr-time').textContent='Updated '+ts;
|
| 381 |
+
document.getElementById('summary-ts').textContent='Last updated '+ts;
|
| 382 |
+
|
| 383 |
+
ico.classList.remove('spin');
|
| 384 |
+
btn.disabled=false;
|
| 385 |
+
running=false;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
// ββ FETCH + RENDER ONE TICKER βββββββββββββββββββββββββββββββββ
|
| 389 |
+
async function fetchAndRender(ticker){
|
| 390 |
+
try{
|
| 391 |
+
const d=await callClaude(ticker);
|
| 392 |
+
results[ticker.sym]=d;
|
| 393 |
+
renderCard(ticker,d);
|
| 394 |
+
}catch(e){
|
| 395 |
+
renderError(ticker,e.message);
|
| 396 |
+
}
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
// ββ CLAUDE API ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 400 |
+
async function callClaude(ticker){
|
| 401 |
+
const today=new Date().toISOString().split('T')[0];
|
| 402 |
+
const dow=new Date().toLocaleDateString('en-US',{weekday:'long'});
|
| 403 |
+
|
| 404 |
+
const resp=await fetch('https://api.anthropic.com/v1/messages',{
|
| 405 |
+
method:'POST',
|
| 406 |
+
headers:{
|
| 407 |
+
'Content-Type':'application/json',
|
| 408 |
+
'x-api-key':API_KEY,
|
| 409 |
+
'anthropic-version':'2023-06-01',
|
| 410 |
+
'anthropic-dangerous-direct-browser-access':'true',
|
| 411 |
+
},
|
| 412 |
+
body:JSON.stringify({
|
| 413 |
+
model:'claude-sonnet-4-20250514',
|
| 414 |
+
max_tokens:1800,
|
| 415 |
+
tools:[{type:'web_search_20250305',name:'web_search'}],
|
| 416 |
+
system:`You are a senior options trader and quantitative analyst. Today is ${dow} ${today}.
|
| 417 |
+
Your job: Search for LIVE current data on the given stock ticker, then produce a precise options recommendation as JSON.
|
| 418 |
+
RULES:
|
| 419 |
+
1. Always search the web first β never use stale training data for prices or IV
|
| 420 |
+
2. Search for: current stock price, today's % change, options IV, recent news/catalysts, upcoming earnings, current VIX
|
| 421 |
+
3. After gathering data, return ONLY valid JSON matching the exact schema. No markdown fences, no prose outside the JSON.
|
| 422 |
+
4. Include the actual source name (e.g. "Reuters", "Yahoo Finance", "Bloomberg") for any news cited.`,
|
| 423 |
+
messages:[{role:'user',content:buildPrompt(ticker,today)}],
|
| 424 |
+
}),
|
| 425 |
+
});
|
| 426 |
+
|
| 427 |
+
if(!resp.ok){
|
| 428 |
+
const e=await resp.json().catch(()=>({}));
|
| 429 |
+
throw new Error((e.error&&e.error.message)||'API error '+resp.status);
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
const data=await resp.json();
|
| 433 |
+
// Extract text blocks (web search tool may produce mixed content blocks)
|
| 434 |
+
const text=(data.content||[]).filter(b=>b.type==='text').map(b=>b.text).join('');
|
| 435 |
+
|
| 436 |
+
// Robust JSON extraction
|
| 437 |
+
let js=text;
|
| 438 |
+
const fm=js.match(/```(?:json)?\s*([\s\S]*?)```/);
|
| 439 |
+
if(fm) js=fm[1];
|
| 440 |
+
const a=js.indexOf('{'), b=js.lastIndexOf('}');
|
| 441 |
+
if(a!==-1&&b!==-1) js=js.slice(a,b+1);
|
| 442 |
+
|
| 443 |
+
try{ return JSON.parse(js); }
|
| 444 |
+
catch(e){ throw new Error('Could not parse AI response β try refreshing.'); }
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
// βοΏ½οΏ½ PROMPT ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 448 |
+
function buildPrompt(ticker,today){
|
| 449 |
+
return `Analyse ticker: ${ticker.sym} (${ticker.name}). Today: ${today}.
|
| 450 |
+
|
| 451 |
+
STEP 1 β Search the web for:
|
| 452 |
+
β’ Current price and today's % change for ${ticker.sym}
|
| 453 |
+
β’ Current implied volatility (IV) and options chain activity
|
| 454 |
+
β’ Most recent news or catalyst (past 7 days) β note the SOURCE
|
| 455 |
+
β’ Next earnings date for ${ticker.sym}
|
| 456 |
+
β’ Current VIX level and US 3-month T-bill yield
|
| 457 |
+
|
| 458 |
+
STEP 2 β Respond with ONLY this exact JSON (no other text):
|
| 459 |
+
|
| 460 |
+
{
|
| 461 |
+
"ticker": "${ticker.sym}",
|
| 462 |
+
"company": "${ticker.name}",
|
| 463 |
+
"price": <number>,
|
| 464 |
+
"price_change_pct": <number e.g. 1.23>,
|
| 465 |
+
"price_trend": "<BULLISH|BEARISH|NEUTRAL>",
|
| 466 |
+
"vix": <number>,
|
| 467 |
+
"risk_free_rate": <number e.g. 4.8>,
|
| 468 |
+
"iv_estimate": <number e.g. 42.0>,
|
| 469 |
+
"iv_rank": <number 0-100>,
|
| 470 |
+
"iv_regime": "<HIGH_SELL_PREMIUM|LOW_BUY_OPTIONS|NEUTRAL>",
|
| 471 |
+
"hv_30d": <number>,
|
| 472 |
+
"rsi_estimate": <number>,
|
| 473 |
+
"sma20": <number>,
|
| 474 |
+
"sma50": <number>,
|
| 475 |
+
"earnings_date": "<YYYY-MM-DD or Unknown>",
|
| 476 |
+
"days_to_earnings": <number or null>,
|
| 477 |
+
"has_liquid_options": <true|false>,
|
| 478 |
+
"options_note": "<one line on liquidity>",
|
| 479 |
+
"news_headline": "<most important recent news headline in one sentence>",
|
| 480 |
+
"news_source": "<source name e.g. Reuters, Yahoo Finance, Bloomberg, SEC filing>",
|
| 481 |
+
"news_date": "<YYYY-MM-DD or approximate>",
|
| 482 |
+
"news_impact": "<BULLISH|BEARISH|NEUTRAL β how this news affects the options strategy>",
|
| 483 |
+
"data_quality": "<LIVE|ESTIMATED>",
|
| 484 |
+
|
| 485 |
+
"top_strategy": {
|
| 486 |
+
"name": "<Sell Put|Sell Covered Call|Buy Call|Buy Put|Buy Straddle|Buy Strangle|Iron Condor|Bull Call Spread|Bear Put Spread>",
|
| 487 |
+
"type_class": "<sell_put|sell_call|buy_call|buy_put|straddle|strangle|condor|bull_spread|bear_spread>",
|
| 488 |
+
"direction": "<Bullish|Bearish|Neutral|Non-Directional>",
|
| 489 |
+
"confidence": "<HIGH|MEDIUM|LOW>",
|
| 490 |
+
"strike": "<e.g. $415>",
|
| 491 |
+
"expiry": "<e.g. Apr 17 2025>",
|
| 492 |
+
"dte": "<e.g. 42 DTE>",
|
| 493 |
+
"entry_mid": "<e.g. $3.20>",
|
| 494 |
+
"max_profit": "<e.g. $320 per contract>",
|
| 495 |
+
"max_loss": "<e.g. $680 per contract>",
|
| 496 |
+
"breakeven": "<e.g. $411.80 or Up: $420 / Down: $400>",
|
| 497 |
+
"delta": "<e.g. -0.28>",
|
| 498 |
+
"gamma": "<e.g. 0.012>",
|
| 499 |
+
"theta": "<e.g. -$0.09/day>",
|
| 500 |
+
"vega": "<e.g. $0.18 per 1pt IV>",
|
| 501 |
+
"technical_rationale": "<2-3 sentences: the professional analysis using IV rank, trend, Greeks, VIX>",
|
| 502 |
+
"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.>",
|
| 503 |
+
"risk_scenario": "<one specific event that immediately kills this trade>",
|
| 504 |
+
"pass_if": "<two specific conditions under which you skip this trade today>"
|
| 505 |
+
},
|
| 506 |
+
|
| 507 |
+
"alternative_strategy": {
|
| 508 |
+
"name": "<strategy name>",
|
| 509 |
+
"type_class": "<type class>",
|
| 510 |
+
"direction": "<direction>",
|
| 511 |
+
"strike": "<strike>",
|
| 512 |
+
"expiry": "<expiry>",
|
| 513 |
+
"entry_mid": "<mid price>",
|
| 514 |
+
"rationale": "<2 sentences: why this is the second best option>"
|
| 515 |
+
},
|
| 516 |
+
|
| 517 |
+
"strategy_scores": [
|
| 518 |
+
{"name":"Sell Put","score":<0-100>},
|
| 519 |
+
{"name":"Sell Call","score":<0-100>},
|
| 520 |
+
{"name":"Buy Call","score":<0-100>},
|
| 521 |
+
{"name":"Buy Put","score":<0-100>},
|
| 522 |
+
{"name":"Buy Straddle","score":<0-100>},
|
| 523 |
+
{"name":"Iron Condor","score":<0-100>},
|
| 524 |
+
{"name":"Bull Spread","score":<0-100>},
|
| 525 |
+
{"name":"Bear Spread","score":<0-100>}
|
| 526 |
+
]
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
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.`;
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
// ββ BUILD SUMMARY TABLE βββββββββββββββββββββββββββββββββββββββ
|
| 533 |
+
function buildSummary(){
|
| 534 |
+
const tbody=document.getElementById('summary-body');
|
| 535 |
+
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; }
|
| 536 |
+
tbody.innerHTML='';
|
| 537 |
+
TICKERS.forEach(t=>{
|
| 538 |
+
const d=results[t.sym];
|
| 539 |
+
if(!d){
|
| 540 |
+
const tr=document.createElement('tr');
|
| 541 |
+
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>`;
|
| 542 |
+
tbody.appendChild(tr);
|
| 543 |
+
return;
|
| 544 |
+
}
|
| 545 |
+
const ts=d.top_strategy||{};
|
| 546 |
+
const tc=TYPE_MAP[ts.type_class]||'df';
|
| 547 |
+
const chgSign=d.price_change_pct>=0?'+':'';
|
| 548 |
+
const chgCls=d.price_change_pct>0?'up':d.price_change_pct<0?'dn':'fl';
|
| 549 |
+
const confCls=ts.confidence==='HIGH'?'conf-high':ts.confidence==='MEDIUM'?'conf-med':'conf-low';
|
| 550 |
+
const tr=document.createElement('tr');
|
| 551 |
+
tr.innerHTML=`
|
| 552 |
+
<td><span class="sticker">${t.sym}</span><br><span style="font-size:10px;color:var(--t3)">${t.name}</span></td>
|
| 553 |
+
<td><span class="sprice">$${fmt(d.price)}</span><span class="schg ${chgCls}">${chgSign}${fmt(d.price_change_pct)}%</span></td>
|
| 554 |
+
<td><span class="strategy-pill b-${tc}">${ts.name||'β'}</span></td>
|
| 555 |
+
<td><span class="mono">${ts.strike||'β'}</span></td>
|
| 556 |
+
<td><span class="mono" style="font-size:11px">${ts.expiry||'β'}<br><span style="color:var(--t3)">${ts.dte||''}</span></span></td>
|
| 557 |
+
<td><span class="mono">${ts.entry_mid||'β'}</span></td>
|
| 558 |
+
<td style="color:var(--green);font-weight:600;font-size:12px">${ts.max_profit||'β'}</td>
|
| 559 |
+
<td style="color:var(--red);font-weight:600;font-size:12px">${ts.max_loss||'β'}</td>
|
| 560 |
+
<td><span class="conf-badge ${confCls}">${ts.confidence||'β'}</span></td>`;
|
| 561 |
+
tbody.appendChild(tr);
|
| 562 |
+
});
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
// ββ RENDER CARD βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 566 |
+
function renderCard(ticker,d){
|
| 567 |
+
const card=document.getElementById('card-'+ticker.sym);
|
| 568 |
+
if(!card) return;
|
| 569 |
+
|
| 570 |
+
// Update VIX in header once
|
| 571 |
+
if(d.vix && document.getElementById('hdr-vix').style.display==='none'||!document.getElementById('hdr-vix').textContent){
|
| 572 |
+
const vixEl=document.getElementById('hdr-vix');
|
| 573 |
+
vixEl.textContent=`VIX ${fmt(d.vix)}`;
|
| 574 |
+
vixEl.style.display='block';
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
const ts=d.top_strategy||{};
|
| 578 |
+
const alt=d.alternative_strategy||{};
|
| 579 |
+
const tc=TYPE_MAP[ts.type_class]||'df';
|
| 580 |
+
const altc=TYPE_MAP[alt.type_class]||'df';
|
| 581 |
+
const chgSign=d.price_change_pct>=0?'+':'';
|
| 582 |
+
const chgCls=d.price_change_pct>0?'up':d.price_change_pct<0?'dn':'fl';
|
| 583 |
+
const ivFill=Math.min(100,d.iv_rank||0);
|
| 584 |
+
const ivColor=d.iv_rank>60?'var(--red)':d.iv_rank<35?'var(--green)':'var(--amber)';
|
| 585 |
+
const regClass=d.iv_regime==='HIGH_SELL_PREMIUM'?'rt-sell':d.iv_regime==='LOW_BUY_OPTIONS'?'rt-buy':'rt-neutral';
|
| 586 |
+
const regLabel=d.iv_regime==='HIGH_SELL_PREMIUM'?'Sell Premium β':d.iv_regime==='LOW_BUY_OPTIONS'?'Buy Options β':'Neutral IV';
|
| 587 |
+
const trendColor=d.price_trend==='BULLISH'?'var(--green)':d.price_trend==='BEARISH'?'var(--red)':'var(--amber)';
|
| 588 |
+
const dq=d.data_quality||'';
|
| 589 |
+
const dqPill=dq.includes('LIVE')?
|
| 590 |
+
`<span style="font-size:10px;padding:2px 7px;border-radius:4px;background:var(--green-m);color:var(--green);font-weight:700">β LIVE</span>`:
|
| 591 |
+
`<span style="font-size:10px;padding:2px 7px;border-radius:4px;background:var(--amber-m);color:var(--amber);font-weight:700">β EST</span>`;
|
| 592 |
+
const confCls=ts.confidence==='HIGH'?'conf-high':ts.confidence==='MEDIUM'?'conf-med':'conf-low';
|
| 593 |
+
const scores=(d.strategy_scores||[]).sort((a,b)=>b.score-a.score);
|
| 594 |
+
const newsImpactColor=d.news_impact==='BULLISH'?'var(--green)':d.news_impact==='BEARISH'?'var(--red)':'var(--t3)';
|
| 595 |
+
|
| 596 |
+
card.innerHTML=`
|
| 597 |
+
<!-- Header -->
|
| 598 |
+
<div class="tcard-hdr">
|
| 599 |
+
<div>
|
| 600 |
+
<div class="tcard-ticker">${d.ticker} ${dqPill}</div>
|
| 601 |
+
<div class="tcard-name">${d.company}</div>
|
| 602 |
+
</div>
|
| 603 |
+
<div class="tcard-price">
|
| 604 |
+
<div class="price-big">$${fmt(d.price)}</div>
|
| 605 |
+
<div class="price-chg ${chgCls}">${chgSign}${fmt(d.price_change_pct)}%</div>
|
| 606 |
+
</div>
|
| 607 |
+
</div>
|
| 608 |
+
|
| 609 |
+
<!-- Stats -->
|
| 610 |
+
<div class="stats-band">
|
| 611 |
+
<div class="sc">
|
| 612 |
+
<div class="sc-l">Trend</div>
|
| 613 |
+
<div class="sc-v" style="color:${trendColor}">${d.price_trend||'β'}</div>
|
| 614 |
+
<div class="sc-s">RSI ${fmt(d.rsi_estimate,0)}</div>
|
| 615 |
+
</div>
|
| 616 |
+
<div class="sc">
|
| 617 |
+
<div class="sc-l">ATM IV</div>
|
| 618 |
+
<div class="sc-v">${fmt(d.iv_estimate)}%</div>
|
| 619 |
+
<div class="sc-s">HV30 ${fmt(d.hv_30d)}%</div>
|
| 620 |
+
</div>
|
| 621 |
+
<div class="sc">
|
| 622 |
+
<div class="sc-l">Earnings</div>
|
| 623 |
+
<div class="sc-v" style="font-size:12px">${d.earnings_date&&d.earnings_date!=='Unknown'?d.earnings_date:'β'}</div>
|
| 624 |
+
<div class="sc-s">${d.days_to_earnings?d.days_to_earnings+'d away':''}</div>
|
| 625 |
+
</div>
|
| 626 |
+
<div class="sc">
|
| 627 |
+
<div class="sc-l">Options</div>
|
| 628 |
+
<div class="sc-v" style="font-size:12px;color:${d.has_liquid_options?'var(--green)':'var(--red)'}">
|
| 629 |
+
${d.has_liquid_options?'β Liquid':'β Thin'}
|
| 630 |
+
</div>
|
| 631 |
+
<div class="sc-s" style="font-size:9px">${(d.options_note||'').substring(0,28)}</div>
|
| 632 |
+
</div>
|
| 633 |
+
</div>
|
| 634 |
+
|
| 635 |
+
<!-- IV Rank -->
|
| 636 |
+
<div class="iv-bar-wrap">
|
| 637 |
+
<div class="iv-bar-row">
|
| 638 |
+
<span class="iv-bar-lbl">IV Rank: <strong>${fmt(d.iv_rank,0)}</strong> / 100</span>
|
| 639 |
+
<span class="iv-regime-tag ${regClass}">${regLabel}</span>
|
| 640 |
+
</div>
|
| 641 |
+
<div class="iv-track"><div class="iv-fill" style="width:${ivFill}%;background:${ivColor}"></div></div>
|
| 642 |
+
</div>
|
| 643 |
+
|
| 644 |
+
<!-- News -->
|
| 645 |
+
${d.news_headline?`<div class="news-row">
|
| 646 |
+
<span class="news-tag" style="color:${newsImpactColor}">News</span>
|
| 647 |
+
<div>
|
| 648 |
+
<div class="news-text">${esc(d.news_headline)}</div>
|
| 649 |
+
<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>
|
| 650 |
+
</div>
|
| 651 |
+
</div>`:''}
|
| 652 |
+
|
| 653 |
+
<!-- Recommendation Banner -->
|
| 654 |
+
<div class="rec-block">
|
| 655 |
+
<div class="rec-block-title">β Top Recommendation</div>
|
| 656 |
+
|
| 657 |
+
<div class="rec-banner s-${tc}">
|
| 658 |
+
<div class="rec-top-row">
|
| 659 |
+
<div>
|
| 660 |
+
<div class="rec-strat-name">${ts.name||'β'}</div>
|
| 661 |
+
<div class="rec-dir">${ts.direction||''}</div>
|
| 662 |
+
</div>
|
| 663 |
+
<div class="rec-conf">
|
| 664 |
+
<span class="conf-badge ${confCls}">${ts.confidence||'β'}</span>
|
| 665 |
+
<div style="font-size:9px;color:var(--t3);margin-top:4px;text-align:right">confidence</div>
|
| 666 |
+
</div>
|
| 667 |
+
</div>
|
| 668 |
+
<div class="rec-strike-row">
|
| 669 |
+
<div class="rec-chip">
|
| 670 |
+
<span class="rec-chip-l">Strike</span>
|
| 671 |
+
<span class="rec-chip-v">${ts.strike||'β'}</span>
|
| 672 |
+
</div>
|
| 673 |
+
<div class="rec-chip">
|
| 674 |
+
<span class="rec-chip-l">Expiry</span>
|
| 675 |
+
<span class="rec-chip-v" style="font-size:11px">${ts.expiry||'β'}</span>
|
| 676 |
+
</div>
|
| 677 |
+
<div class="rec-chip">
|
| 678 |
+
<span class="rec-chip-l">DTE</span>
|
| 679 |
+
<span class="rec-chip-v">${ts.dte||'β'}</span>
|
| 680 |
+
</div>
|
| 681 |
+
<div class="rec-chip">
|
| 682 |
+
<span class="rec-chip-l">Entry Mid</span>
|
| 683 |
+
<span class="rec-chip-v">${ts.entry_mid||'β'}</span>
|
| 684 |
+
</div>
|
| 685 |
+
</div>
|
| 686 |
+
</div>
|
| 687 |
+
|
| 688 |
+
<!-- P&L -->
|
| 689 |
+
<div class="pnl-row">
|
| 690 |
+
<div class="pnl-box"><div class="pnl-l">Max Profit</div><div class="pnl-v" style="color:var(--green)">${ts.max_profit||'β'}</div></div>
|
| 691 |
+
<div class="pnl-box"><div class="pnl-l">Max Loss</div><div class="pnl-v" style="color:var(--red)">${ts.max_loss||'β'}</div></div>
|
| 692 |
+
<div class="pnl-box"><div class="pnl-l">Breakeven</div><div class="pnl-v" style="font-size:11px">${ts.breakeven||'β'}</div></div>
|
| 693 |
+
</div>
|
| 694 |
+
|
| 695 |
+
<!-- Greeks -->
|
| 696 |
+
<div class="greeks-row">
|
| 697 |
+
<div class="greek-box"><div class="greek-sym">Ξ</div><div class="greek-val">${ts.delta||'β'}</div><div class="greek-lbl">Delta</div></div>
|
| 698 |
+
<div class="greek-box"><div class="greek-sym">Ξ</div><div class="greek-val">${ts.gamma||'β'}</div><div class="greek-lbl">Gamma</div></div>
|
| 699 |
+
<div class="greek-box"><div class="greek-sym">ΞΈ</div><div class="greek-val">${ts.theta||'β'}</div><div class="greek-lbl">Theta/day</div></div>
|
| 700 |
+
<div class="greek-box"><div class="greek-sym">Ξ½</div><div class="greek-val">${ts.vega||'β'}</div><div class="greek-lbl">Vega/pt</div></div>
|
| 701 |
+
</div>
|
| 702 |
+
|
| 703 |
+
<!-- Professional rationale -->
|
| 704 |
+
${ts.technical_rationale?`<div style="font-size:12px;line-height:1.7;color:var(--t2);margin-bottom:0">${esc(ts.technical_rationale)}</div>`:''}
|
| 705 |
+
</div>
|
| 706 |
+
|
| 707 |
+
<!-- Plain English -->
|
| 708 |
+
${ts.plain_english?`<div class="plain-block">
|
| 709 |
+
<div class="plain-title">π¬ In Plain English</div>
|
| 710 |
+
<div class="plain-text">${esc(ts.plain_english)}</div>
|
| 711 |
+
</div>`:''}
|
| 712 |
+
|
| 713 |
+
<!-- Risk -->
|
| 714 |
+
${ts.risk_scenario?`<div class="risk-block">
|
| 715 |
+
<div class="risk-tag">β Key Risk</div>
|
| 716 |
+
<div class="risk-text">${esc(ts.risk_scenario)}</div>
|
| 717 |
+
</div>`:''}
|
| 718 |
+
|
| 719 |
+
<!-- Pass if -->
|
| 720 |
+
${ts.pass_if?`<div class="pass-block">
|
| 721 |
+
<div class="pass-tag">π« Skip this trade ifβ¦</div>
|
| 722 |
+
<div class="pass-text">${esc(ts.pass_if)}</div>
|
| 723 |
+
</div>`:''}
|
| 724 |
+
|
| 725 |
+
<!-- Alternative -->
|
| 726 |
+
${alt.name?`<div class="alt-block">
|
| 727 |
+
<div class="alt-tag">π Alternative Trade</div>
|
| 728 |
+
<div class="alt-inner">
|
| 729 |
+
<span class="strategy-pill b-${altc}" style="font-size:10px;flex-shrink:0">${alt.name}</span>
|
| 730 |
+
<div class="alt-text"><strong>${alt.strike||''} ${alt.expiry||''}</strong> Β· Mid ${alt.entry_mid||'β'}<br>${esc(alt.rationale||'')}</div>
|
| 731 |
+
</div>
|
| 732 |
+
</div>`:''}
|
| 733 |
+
|
| 734 |
+
<!-- Strategy Scores -->
|
| 735 |
+
<div class="scores-block">
|
| 736 |
+
<div class="scores-title">All Strategy Scores</div>
|
| 737 |
+
${scores.map(s=>{
|
| 738 |
+
const sc=s.score||0;
|
| 739 |
+
const col=sc>=70?'var(--green)':sc>=50?'var(--blue)':sc>=35?'var(--amber)':'var(--border2)';
|
| 740 |
+
return `<div class="score-row">
|
| 741 |
+
<span class="score-nm">${s.name}</span>
|
| 742 |
+
<div class="score-tr"><div class="score-fi" style="width:${sc}%;background:${col}"></div></div>
|
| 743 |
+
<span class="score-n" style="color:${col}">${sc}</span>
|
| 744 |
+
</div>`;
|
| 745 |
+
}).join('')}
|
| 746 |
+
</div>`;
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
// ββ RENDER ERROR ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 750 |
+
function renderError(ticker,msg){
|
| 751 |
+
const card=document.getElementById('card-'+ticker.sym);
|
| 752 |
+
if(!card) return;
|
| 753 |
+
card.innerHTML=`
|
| 754 |
+
<div class="tcard-hdr"><div><div class="tcard-ticker">${ticker.sym}</div><div class="tcard-name">${ticker.name}</div></div></div>
|
| 755 |
+
<div class="card-err">
|
| 756 |
+
<div class="card-err-ico">β οΈ</div>
|
| 757 |
+
<div class="card-err-msg">${esc(msg)}</div>
|
| 758 |
+
<button class="btn-retry" onclick="retryOne('${ticker.sym}')">β© Retry ${ticker.sym}</button>
|
| 759 |
+
</div>`;
|
| 760 |
+
}
|
| 761 |
+
|
| 762 |
+
async function retryOne(sym){
|
| 763 |
+
const t=TICKERS.find(x=>x.sym===sym);
|
| 764 |
+
if(!t) return;
|
| 765 |
+
document.getElementById('card-'+sym).innerHTML=loadingHTML(t);
|
| 766 |
+
await fetchAndRender(t);
|
| 767 |
+
buildSummary();
|
| 768 |
+
}
|
| 769 |
+
|
| 770 |
+
// ββ HELPERS βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 771 |
+
function fmt(n,dec=2){
|
| 772 |
+
if(n===null||n===undefined||n==='') return 'β';
|
| 773 |
+
const x=parseFloat(n);
|
| 774 |
+
return isNaN(x)?String(n):x.toFixed(dec);
|
| 775 |
+
}
|
| 776 |
+
function esc(s){
|
| 777 |
+
return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
| 778 |
+
}
|
| 779 |
+
</script>
|
| 780 |
+
</body>
|
| 781 |
+
</html>
|