diff --git a/frontend/strategy.html b/frontend/strategy.html
index 9210248..8e36b21 100644
--- a/frontend/strategy.html
+++ b/frontend/strategy.html
@@ -352,9 +352,14 @@
}
}
const uniqBE = bes.filter((v,i)=> i===0 || Math.abs(v - bes[i-1]) > 1e-6);
- // extremes within sample
+ // extremes — include the grid samples AND the exact kink points (strikes)
+ // plus the sample bounds, since piecewise payoffs peak/trough at strikes
let maxY = -Infinity, minY = Infinity;
for (const y of ys) { if (y > maxY) maxY = y; if (y < minY) minY = y; }
+ for (const k of new Set([...legs.map(l=>l.strike), lo0, hi0])) {
+ const y = plAt(legs, net, k, this.minDTE);
+ if (y > maxY) maxY = y; if (y < minY) minY = y;
+ }
// unbounded detection from the far-right slope of the expiration curve
// (downside is always bounded since the underlying can't go below 0)
const slopeR = (ys[Nd] - ys[Nd-1]) / (xs[Nd] - xs[Nd-1]);
@@ -447,10 +452,15 @@
const lo = Math.max(0.01, spot - half), hi = spot + half;
const expAt = this.minDTE;
const N = 161;
+ // x-grid = evenly spaced points + the exact strike kinks (so payoff
+ // vertices — butterfly peak, condor body, etc. — render sharply)
+ const xGrid = [];
+ for (let i = 0; i <= N; i++) xGrid.push(lo + (hi-lo)*i/N);
+ for (const k of strikes) if (k > lo && k < hi) xGrid.push(k);
+ xGrid.sort((a,b)=>a-b);
const expData = [], tnData = [];
let yMin = Infinity, yMax = -Infinity;
- for (let i = 0; i <= N; i++) {
- const x = lo + (hi-lo)*i/N;
+ for (const x of xGrid) {
const ye = plAt(legs, net, x, expAt);
const yt = plAt(legs, net, x, this.dteOffset);
expData.push([x, +ye.toFixed(2)]);
@@ -467,7 +477,7 @@
// breakevens for light vertical lines
const bes = [];
- for (let i = 1; i <= N; i++) {
+ for (let i = 1; i < expData.length; i++) {
const y0 = expData[i-1][1], y1 = expData[i][1];
if ((y0 < 0 && y1 > 0) || (y0 > 0 && y1 < 0)) {
const x = expData[i-1][0] + (0 - y0) * (expData[i][0]-expData[i-1][0]) / (y1 - y0);