Files
options-pricer/frontend/settings.html
ojy 9de7f14573 Split Positions from Tracker; add Settings (commission)
Tracker is for symbol price/IV history, not positions. The "Save to
Tracker" button now adds the symbol to a watchlist (localStorage); the
Tracker page shows the watchlist as clickable chips.

New "Enter Position" button on the Strategy page posts the active legs
to /api/orders, then opens the new Positions page.

New Positions page (positions.html): lists entered positions with live
mid value, round-trip commission, gross & net P/L (Net = Gross − round-
trip commission), per-symbol filter, summary totals, close/reopen and
remove actions.

New Settings page (settings.html) configures the commission used on
Positions. Defaults to Interactive Brokers Fixed / IBKR Lite: $0.65
per contract, $1.00 minimum per order
(https://www.interactivebrokers.com/en/pricing/commissions-options.php).
Per-leg vs per-order toggle for complex orders.

Sidebar nav now: Dashboard · Options Chain · Vol Surface · Strategy P/L
· Positions · Tracker · Settings.

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

173 lines
9.7 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="#sb-menu"><span class="navbar-toggler-icon"></span></button>
<h1 class="navbar-brand navbar-brand-autodark">
<a href="index.html" class="text-decoration-none d-flex align-items-center gap-2">
<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"><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="sb-menu">
<ul class="navbar-nav pt-lg-3">
<li class="nav-item"><a class="nav-link" href="index.html"><span class="nav-link-title">Dashboard</span></a></li>
<li class="nav-item"><a class="nav-link" href="chain.html"><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-title">Vol Surface</span></a></li>
<li class="nav-item"><a class="nav-link" href="strategy.html"><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-title">Positions</span></a></li>
<li class="nav-item"><a class="nav-link" href="tracker.html"><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-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>