From 5348bf643af81daf35daa4832a111a5156a0bfe8 Mon Sep 17 00:00:00 2001 From: ojy Date: Wed, 13 May 2026 04:28:21 +0000 Subject: [PATCH] Round leg prices to 2 decimals everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Entry price, mark and strike are now rounded to 2dp on add, on edit, on market reload, and on load (self-heals legacy entries) — fixes display of float artifacts like "15.000000001". Also persist the leg's locked/currentMark fields in the store. Co-Authored-By: Claude Sonnet 4.6 --- frontend/assets/strategy-store.js | 33 ++++++++++++++++++++++++------- frontend/strategy.html | 2 +- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/frontend/assets/strategy-store.js b/frontend/assets/strategy-store.js index 22f9075..cd59470 100644 --- a/frontend/assets/strategy-store.js +++ b/frontend/assets/strategy-store.js @@ -21,6 +21,12 @@ const VERSION = 1; const MULTIPLIER = 100; // standard equity option contract size + /** Round a price to 2 decimals (kills float artifacts like 15.000000001). */ + function round2(v) { + const n = Number(v); + return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0; + } + function empty() { return { v: VERSION, symbol: "", spotSnapshot: 0, updatedAt: null, legs: [] }; } @@ -31,10 +37,15 @@ if (!raw) return empty(); const obj = JSON.parse(raw); if (!obj || obj.v !== VERSION || !Array.isArray(obj.legs)) return empty(); - // sanitize: drop legs with no usable strike / type - obj.legs = obj.legs.filter( - (l) => l && Number.isFinite(Number(l.strike)) && (l.type === "call" || l.type === "put") - ); + // sanitize: drop legs with no usable strike / type; round prices to 2dp (self-heal) + obj.legs = obj.legs + .filter((l) => l && Number.isFinite(Number(l.strike)) && (l.type === "call" || l.type === "put")) + .map((l) => ({ + ...l, + strike: round2(l.strike), + entryPrice: round2(l.entryPrice), + currentMark: (typeof l.currentMark === "number" ? round2(l.currentMark) : (l.currentMark ?? null)), + })); return obj; } catch { return empty(); @@ -84,11 +95,13 @@ symbol: leg.symbol || state.symbol, expiry: leg.expiry, type: leg.type, - strike: strike, + strike: round2(strike), side: leg.side === "short" ? "short" : "long", qty: Math.max(1, Math.round(Number(leg.qty) || 1)), - entryPrice: Number(leg.entryPrice) || 0, + entryPrice: round2(leg.entryPrice), iv: Number(leg.iv) || 0, + locked: leg.locked === true, + currentMark: (typeof leg.currentMark === "number" ? round2(leg.currentMark) : null), }); save(state); return { state, replacedSymbol }; @@ -105,7 +118,13 @@ function updateLeg(id, patch) { const state = load(); const leg = state.legs.find((l) => l.id === id); - if (leg) Object.assign(leg, patch); + if (leg) { + const p = { ...patch }; + if ("entryPrice" in p) p.entryPrice = round2(p.entryPrice); + if ("currentMark" in p && p.currentMark != null) p.currentMark = round2(p.currentMark); + if ("strike" in p) p.strike = round2(p.strike); + Object.assign(leg, p); + } save(state); return state; } diff --git a/frontend/strategy.html b/frontend/strategy.html index b43bfe1..ba628f2 100644 --- a/frontend/strategy.html +++ b/frontend/strategy.html @@ -468,7 +468,7 @@ if (!map) continue; const o = map[Number(leg.strike) + '@' + leg.type]; if (!o) continue; - const mark = o.midPrice ?? o.mid ?? o.bsPrice ?? 0; + const mark = Math.round((o.midPrice ?? o.mid ?? o.bsPrice ?? 0) * 100) / 100; leg.currentMark = mark; if (o.iv > 0) leg.iv = o.iv; if (!leg.locked && mark > 0) { leg.entryPrice = mark; relinked++; }