Add strategy P/L analyzer + view-state persistence
- New strategy.html: thinkorswim-style P/L diagram (expiration + T+N curves via Black-Scholes, days-to-expiry slider, net debit/credit, max profit/loss with unbounded detection, breakevens, net Greeks, auto-detected strategy name) - chain.html: per-row Buy/Sell buttons add legs to a localStorage basket; basket badge in toolbar; auto-scroll to ATM row on load - Persist per-page view state (symbol, expiry, loaded data, charts) across navigation via viewstate-store.js for chain/surface/tracker/dashboard - New assets: blackscholes.js (frontend BS port), strategy-store.js, viewstate-store.js - Strategy P/L nav link added to all sidebars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
<link rel="stylesheet" href="/assets/tabler.min.css"/>
|
||||
<link rel="stylesheet" href="/assets/tabler-vendors.min.css"/>
|
||||
<script src="/assets/apexcharts.min.js"></script>
|
||||
<script src="/assets/viewstate-store.js"></script>
|
||||
<script src="/assets/alpine.min.js" defer></script>
|
||||
<script src="/assets/tabler.min.js" defer></script>
|
||||
|
||||
@@ -156,6 +157,17 @@
|
||||
</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="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 19l4 -6l4 2l4 -8l4 5"></path><path d="M4 4v16h16"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="nav-link-title">Strategy P/L</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active-page" href="tracker.html">
|
||||
<span class="nav-link-icon d-md-none d-lg-inline-block">
|
||||
@@ -603,7 +615,22 @@
|
||||
_charts: { atmIv: null, rr25: null, fly25: null },
|
||||
|
||||
async init() {
|
||||
// expirations are built from snapshot data after loadHistory()
|
||||
// restore last loaded history (survives navigating away & back)
|
||||
const vs = ViewState.load('tracker');
|
||||
if (vs) {
|
||||
this.symbol = vs.symbol ?? this.symbol;
|
||||
this.expirations = vs.expirations ?? [];
|
||||
this.expiry = vs.expiry ?? '';
|
||||
this.snapshots = vs.snapshots ?? [];
|
||||
if (this.snapshots.length) this.$nextTick(() => this.renderCharts());
|
||||
}
|
||||
},
|
||||
|
||||
_persist() {
|
||||
ViewState.save('tracker', {
|
||||
symbol: this.symbol, expirations: this.expirations,
|
||||
expiry: this.expiry, snapshots: this.snapshots,
|
||||
});
|
||||
},
|
||||
|
||||
async fetchExpirations() {
|
||||
@@ -615,6 +642,7 @@
|
||||
const env = await res.json();
|
||||
const json = env.data ?? env;
|
||||
this.expirations = Array.isArray(json) ? json : (json.expirations || []);
|
||||
this._persist();
|
||||
} catch (e) {
|
||||
this.expirations = [];
|
||||
}
|
||||
@@ -640,6 +668,7 @@
|
||||
const expSet = new Set(this.snapshots.map(s => s.expiry).filter(Boolean));
|
||||
this.expirations = [...expSet].sort();
|
||||
}
|
||||
this._persist();
|
||||
this.$nextTick(() => this.renderCharts());
|
||||
} catch (e) {
|
||||
this.error = e.message;
|
||||
|
||||
Reference in New Issue
Block a user