diff --git a/frontend/strategy.html b/frontend/strategy.html
index 3de3dc2..022f694 100644
--- a/frontend/strategy.html
+++ b/frontend/strategy.html
@@ -165,7 +165,12 @@
|
|
- |
+
+
+
+ |
|
@@ -314,15 +319,59 @@
refreshing: false, showManual: false, toast: '',
manual: { side:'long', type:'call', qty:1, strike:null, expiry:'', entryPrice:null, ivPct:null },
chart: null, _renderTimer: null,
+ _chainCache: {}, // "SYMBOL@EXPIRY" -> { "strike@type": optionRow }
init() {
this.reload();
- // pull live spot / marks / IVs on open (does NOT touch entry prices)
+ this._chainCache = this._seedChainCache();
+ // pull live spot / marks / IVs (and per-expiry chains) on open
if (this.legs.length > 0 && this.symbol) this.reloadMarket(false);
// re-sync if another tab changed the basket
window.addEventListener('storage', (e) => { if (e.key === StrategyStore.KEY) this.reload(); });
},
+ // seed the strike picker from whatever chain was last loaded on chain.html
+ _seedChainCache() {
+ try {
+ const vs = (typeof ViewState !== 'undefined') ? ViewState.load('chain') : null;
+ if (!vs || !vs.symbol || !vs.expiry) return {};
+ const map = {};
+ for (const o of (vs.calls || [])) map[Number(o.strike) + '@call'] = o;
+ for (const o of (vs.puts || [])) map[Number(o.strike) + '@put'] = o;
+ return Object.keys(map).length ? { [vs.symbol + '@' + vs.expiry]: map } : {};
+ } catch { return {}; }
+ },
+
+ _legMap(lv) { return this._chainCache[lv.symbol + '@' + lv.expiry] || null; },
+ hasStrikeOpts(lv) {
+ const m = this._legMap(lv);
+ if (!m) return false;
+ for (const k of Object.keys(m)) if (k.endsWith('@' + lv.type)) return true;
+ return false;
+ },
+ strikeOpts(lv) {
+ const m = this._legMap(lv);
+ const out = [];
+ if (m) for (const k of Object.keys(m)) { if (k.endsWith('@' + lv.type)) out.push(parseFloat(k)); }
+ if (!out.includes(lv.strike)) out.push(lv.strike);
+ return out.sort((a, b) => a - b);
+ },
+ changeStrike(id, newStrike) {
+ const leg = this.legs.find(l => l.id === id);
+ if (!leg || !Number.isFinite(newStrike) || newStrike === leg.strike) return;
+ const m = this._legMap(leg);
+ const o = m && m[Number(newStrike) + '@' + leg.type];
+ const patch = { strike: newStrike };
+ if (o) {
+ const mid = Math.round(((o.midPrice ?? o.mid ?? o.bsPrice ?? leg.entryPrice) || 0) * 100) / 100;
+ patch.entryPrice = mid;
+ patch.currentMark = mid;
+ if (o.iv > 0) patch.iv = o.iv;
+ patch.locked = false; // it's a different contract now — start fresh
+ }
+ this.updateLeg(id, patch);
+ },
+
reload() {
const st = StrategyStore.load();
this.symbol = st.symbol || '';
@@ -492,6 +541,10 @@
}
byExpiry[exp] = map;
}
+ // make these chains available to the strike picker
+ const cacheAdds = {};
+ for (const exp of Object.keys(byExpiry)) cacheAdds[this.symbol + '@' + exp] = byExpiry[exp];
+ this._chainCache = { ...this._chainCache, ...cacheAdds };
const st = StrategyStore.load();
let updated = 0, relinked = 0;
for (const leg of st.legs) {
|