diff --git a/frontend/strategy.html b/frontend/strategy.html
index 13742a3..b43bfe1 100644
--- a/frontend/strategy.html
+++ b/frontend/strategy.html
@@ -65,8 +65,8 @@
-
@@ -133,7 +133,7 @@
| Show |
Side | Qty | Type | Strike | Expiry |
- Entry $ | IV |
+ Entry $ | Mark | IV |
Cost | ฮ | ฮ/d | |
@@ -150,7 +150,18 @@
|
|
|
- |
+
+
+
+
+
+ |
+
+
+ $
+
+ โ
+ |
|
|
|
@@ -169,6 +180,7 @@
|
|
|
+ |
|
Add leg |
@@ -296,7 +308,7 @@
this.symbol = st.symbol || '';
this.spot = st.spotSnapshot || 0;
this.legs = st.legs || [];
- this.dteOffset = 0;
+ if (this.dteOffset > this.maxDTE) this.dteOffset = this.maxDTE;
if (this.legs.length > 0) this.$nextTick(() => this.renderChart());
},
@@ -426,26 +438,53 @@
this.reload();
this.flash('Leg added');
},
- async refreshSpot() {
- if (!this.symbol) return;
+ // Re-fetch spot + each leg's current mark & IV. Unlocked legs also get
+ // their entry price reset to the current mark; locked legs keep their entry.
+ async reloadMarket() {
+ if (!this.symbol || this.legs.length === 0) return;
this.refreshing = true;
try {
- // grab the nearest expiry to read current spot
- const er = await fetch('/api/expirations?symbol=' + encodeURIComponent(this.symbol));
- const ee = await er.json(); const ed = ee.data ?? ee;
- const exps = ed.expirations ?? (Array.isArray(ed) ? ed : []);
- const exp = exps[0];
- if (!exp) throw new Error('no expirations');
- const cr = await fetch('/api/chain?symbol=' + encodeURIComponent(this.symbol) + '&expiry=' + encodeURIComponent(exp));
- const ce = await cr.json(); const snap = ce.data?.snapshots?.[0] ?? {};
- if (snap.spot > 0) {
- this.spot = snap.spot;
- const st = StrategyStore.load(); st.spotSnapshot = snap.spot; StrategyStore.save(st);
- this.renderChart();
- this.flash('Spot updated: $' + snap.spot.toFixed(2));
+ const expiries = [...new Set(this.legs.map(l => l.expiry).filter(Boolean))];
+ const byExpiry = {}; // expiry -> { strike@type -> option }
+ let spot = 0;
+ for (const exp of expiries) {
+ const r = await fetch('/api/chain?symbol=' + encodeURIComponent(this.symbol) + '&expiry=' + encodeURIComponent(exp));
+ if (!r.ok) continue;
+ const e = await r.json();
+ const snap = e.data?.snapshots?.[0];
+ if (!snap) continue;
+ if (snap.spot > 0) spot = snap.spot;
+ const map = {};
+ for (const o of (snap.chain || [])) {
+ const t = (o.type || o.optionType || '').toLowerCase();
+ map[Number(o.strike) + '@' + t] = o;
+ }
+ byExpiry[exp] = map;
}
+ const st = StrategyStore.load();
+ let updated = 0, relinked = 0;
+ for (const leg of st.legs) {
+ const map = byExpiry[leg.expiry];
+ if (!map) continue;
+ const o = map[Number(leg.strike) + '@' + leg.type];
+ if (!o) continue;
+ const mark = o.midPrice ?? o.mid ?? o.bsPrice ?? 0;
+ leg.currentMark = mark;
+ if (o.iv > 0) leg.iv = o.iv;
+ if (!leg.locked && mark > 0) { leg.entryPrice = mark; relinked++; }
+ updated++;
+ }
+ if (spot > 0) st.spotSnapshot = spot;
+ StrategyStore.save(st);
+ this.legs = st.legs;
+ if (spot > 0) this.spot = spot;
+ if (this.dteOffset > this.maxDTE) this.dteOffset = this.maxDTE;
+ this.$nextTick(() => this.renderChart());
+ this.flash(updated > 0
+ ? `Reloaded ${updated} leg${updated===1?'':'s'}${relinked?` (${relinked} re-priced)`:''} ยท spot $${(spot||this.spot).toFixed(2)}`
+ : 'Reloaded โ no matching contracts found in the current chain');
} catch (e) {
- this.flash('Refresh failed: ' + e.message);
+ this.flash('Reload failed: ' + e.message);
} finally {
this.refreshing = false;
}