Files
options-pricer/frontend/settings.html
ojy 2cd3d6ece8 IV Spike Scanner
Backend: GET /api/scan?symbols=SYM1,SYM2,... — for each symbol fetches
the front-expiry options chain plus 30-day realized vol and returns
{ spot, change, changePct, atmIv, hv30, ivHv, spike, expiry }. Spike flag
is on when IV/HV ≥ 1.5 or |today's % change| ≥ 3. Defaults to ~15 popular
tickers when no list is given; cap of 30 symbols/scan.

Frontend: new scanner.html page — symbol input (with "Use defaults" / "Use
watchlist" shortcuts), summary cards (count · spikes · biggest mover ·
highest IV/HV), sortable results table with spike rows highlighted, and
shortcut buttons to open each symbol on Chain or Surface.

Scanner added to all sidebars between Vol Surface and Strategy P/L.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:32:14 +00:00

281 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Settings — Options Pricer</title>
<link rel="stylesheet" href="/assets/tabler.min.css" />
<link rel="stylesheet" href="/assets/tabler-vendors.min.css" />
<style>
.mono { font-family:'JetBrains Mono','Fira Code',monospace; }
[x-cloak] { display:none !important; }
</style>
</head>
<body class="antialiased">
<div class="wrapper" x-data="settingsApp()" x-init="init()">
<!-- Sidebar -->
<aside class="navbar navbar-vertical navbar-expand-lg" data-bs-theme="dark">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar-menu" aria-controls="sidebar-menu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<h1 class="navbar-brand navbar-brand-autodark">
<a href="index.html" class="d-flex align-items-center gap-2 text-decoration-none">
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="text-primary" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="4" y="8" width="4" height="8" rx="1"/>
<line x1="6" y1="4" x2="6" y2="8"/>
<line x1="6" y1="16" x2="6" y2="20"/>
<rect x="16" y="6" width="4" height="10" rx="1"/>
<line x1="18" y1="2" x2="18" y2="6"/>
<line x1="18" y1="16" x2="18" y2="22"/>
</svg>
<span class="fw-bold">Options Pricer</span>
</a>
</h1>
<div class="collapse navbar-collapse" id="sidebar-menu">
<ul class="navbar-nav pt-lg-3">
<li class="nav-item ">
<a class="nav-link" href="index.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="4" y="4" width="6" height="5" rx="2"/>
<rect x="4" y="13" width="6" height="7" rx="2"/>
<rect x="14" y="4" width="6" height="11" rx="2"/>
<rect x="14" y="19" width="6" height="1" rx=".5"/>
</svg>
</span>
<span class="nav-link-title">Dashboard</span>
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="chain.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="3" y="5" width="18" height="14" rx="2"/>
<path d="M3 10l18 0"/>
<path d="M10 5v14"/>
</svg>
</span>
<span class="nav-link-title">Options Chain</span>
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="surface.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 12c1.333 -4.667 2.667 -7 4 -7s2.667 2.333 4 7s2.667 7 4 7s2.667 -2.333 4 -7"/>
</svg>
</span>
<span class="nav-link-title">Vol Surface</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="scanner.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="11" cy="11" r="7"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
<path d="M11 8v6"/>
<path d="M8 11h6"/>
</svg>
</span>
<span class="nav-link-title">Scanner</span>
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="strategy.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M4 19l4 -6l4 2l4 -8l4 5"/>
<path d="M4 4v16h16"/>
</svg>
</span>
<span class="nav-link-title">Strategy P/L</span>
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="positions.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="3" y="7" width="18" height="13" rx="2"/>
<path d="M8 7v-2a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v2"/>
<line x1="12" y1="12" x2="12" y2="12.01"/>
<path d="M3 13a20 20 0 0 0 18 0"/>
</svg>
</span>
<span class="nav-link-title">Positions</span>
</a>
</li>
<li class="nav-item ">
<a class="nav-link" href="tracker.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/>
<path d="M12 12m-5 0a5 5 0 1 0 10 0a5 5 0 1 0 -10 0"/>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"/>
<path d="M15 12l-3 -3"/>
</svg>
</span>
<span class="nav-link-title">Tracker</span>
</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="settings.html">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</span>
<span class="nav-link-title">Settings</span>
</a>
</li>
</ul>
</div>
</div>
</aside>
<!-- Page -->
<div class="page-wrapper">
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col"><h2 class="page-title">Settings</h2></div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="card mb-3" style="background:#161824; border:1px solid #2d3045;">
<div class="card-header" style="border-bottom:1px solid #2d3045;">
<h3 class="card-title text-white mb-0">Options Commission</h3>
</div>
<div class="card-body">
<p class="text-secondary small mb-3">
Used when computing Net P/L on the <a href="positions.html">Positions</a> page.
Defaults match <strong>Interactive Brokers Fixed / IBKR Lite</strong> for US equity options:
<span class="mono">$0.65 per contract, $1.00 minimum per order</span>
(<a href="https://www.interactivebrokers.com/en/pricing/commissions-options.php" target="_blank" rel="noopener">IBKR docs</a>).
</p>
<div class="row g-3">
<div class="col-12">
<label class="form-label small text-secondary">Plan</label>
<div class="btn-group" role="group" aria-label="Commission plan preset">
<button class="btn btn-sm" :class="c.plan === 'ibkr-fixed' ? 'btn-primary' : 'btn-outline-secondary'" @click="setPlan('ibkr-fixed')">IBKR Fixed / Lite</button>
<button class="btn btn-sm" :class="c.plan === 'ibkr-tiered' ? 'btn-primary' : 'btn-outline-secondary'" @click="setPlan('ibkr-tiered')">IBKR Tiered</button>
<button class="btn btn-sm" :class="c.plan === 'custom' ? 'btn-primary' : 'btn-outline-secondary'" @click="setPlan('custom')">Custom</button>
</div>
<div class="text-secondary small mt-2" x-show="c.plan === 'ibkr-tiered'">
IBKR Tiered: ~$0.15$0.65 per contract by volume, plus exchange / regulatory fees. Estimated effective ~$0.55/ct, $1.00 min.
</div>
</div>
<div class="col-md-4">
<label class="form-label small text-secondary" for="cm-pc">$ per contract</label>
<input id="cm-pc" type="number" min="0" step="0.01" class="form-control" :value="c.perContract" @change="update('perContract', +$event.target.value || 0)" />
</div>
<div class="col-md-4">
<label class="form-label small text-secondary" for="cm-min">$ min per order</label>
<input id="cm-min" type="number" min="0" step="0.01" class="form-control" :value="c.perOrderMin" @change="update('perOrderMin', +$event.target.value || 0)" />
</div>
<div class="col-md-4">
<label class="form-label small text-secondary" for="cm-max">$ max per order <span class="text-secondary">(0 = none)</span></label>
<input id="cm-max" type="number" min="0" step="0.01" class="form-control" :value="c.perOrderMax" @change="update('perOrderMax', +$event.target.value || 0)" />
</div>
<div class="col-12">
<div class="form-check form-switch">
<input id="cm-leg" type="checkbox" class="form-check-input" :checked="c.applyPerLeg" @change="update('applyPerLeg', $event.target.checked)" />
<label class="form-check-label small" for="cm-leg">Charge each leg as a separate order
<span class="text-secondary">— off (default): multi-leg complex orders count as one, so the per-order minimum applies once.</span>
</label>
</div>
</div>
</div>
<hr class="my-4" style="border-color:#2d3045;">
<h4 class="text-secondary text-uppercase small mb-2" style="letter-spacing:.05em;">Preview</h4>
<div class="row g-3">
<div class="col-md-4"><div class="p-3" style="background:#1e2030; border:1px solid #2d3045; border-radius:.5rem;">
<div class="text-secondary small">1 single-leg, 1 contract</div>
<div class="mono fs-5">Round-trip: <span x-text="'$' + preview([{qty:1}]).toFixed(2)"></span></div>
</div></div>
<div class="col-md-4"><div class="p-3" style="background:#1e2030; border:1px solid #2d3045; border-radius:.5rem;">
<div class="text-secondary small">2-leg vertical, 1 contract each</div>
<div class="mono fs-5">Round-trip: <span x-text="'$' + preview([{qty:1},{qty:1}]).toFixed(2)"></span></div>
</div></div>
<div class="col-md-4"><div class="p-3" style="background:#1e2030; border:1px solid #2d3045; border-radius:.5rem;">
<div class="text-secondary small">4-leg iron condor, 1 contract each</div>
<div class="mono fs-5">Round-trip: <span x-text="'$' + preview([{qty:1},{qty:1},{qty:1},{qty:1}]).toFixed(2)"></span></div>
</div></div>
</div>
<div class="mt-4 d-flex justify-content-between align-items-center">
<button class="btn btn-outline-danger btn-sm" @click="resetDefaults()">Reset to IBKR Fixed defaults</button>
<span class="text-secondary small" x-show="savedFlash" x-cloak x-text="savedFlash"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/assets/tabler.min.js" defer></script>
<script src="/assets/settings-store.js"></script>
<script src="/assets/alpine.min.js" defer></script>
<script>
function settingsApp() {
return {
c: { plan:'ibkr-fixed', perContract:0.65, perOrderMin:1.00, perOrderMax:0, applyPerLeg:false },
savedFlash: '',
init() { this.c = SettingsStore.load().commission; },
update(key, val) {
this.c[key] = val;
// changing numeric fields manually -> mark as custom
if (['perContract','perOrderMin','perOrderMax','applyPerLeg'].includes(key)) this.c.plan = 'custom';
this._save();
},
setPlan(plan) {
this.c.plan = plan;
if (plan === 'ibkr-fixed') { this.c.perContract = 0.65; this.c.perOrderMin = 1.00; this.c.perOrderMax = 0; this.c.applyPerLeg = false; }
if (plan === 'ibkr-tiered') { this.c.perContract = 0.55; this.c.perOrderMin = 1.00; this.c.perOrderMax = 0; this.c.applyPerLeg = false; }
this._save();
},
resetDefaults() {
this.c = SettingsStore.reset().commission;
this._save(true);
},
_save(silent) {
SettingsStore.save({ commission: this.c });
if (!silent) {
this.savedFlash = 'Saved · ' + new Date().toLocaleTimeString();
clearTimeout(this._t); this._t = setTimeout(() => { this.savedFlash = ''; }, 1500);
}
},
preview(legs) { return SettingsStore.estimate(legs).roundTrip; },
};
}
</script>
</body>
</html>