Sharpen P/L extrema & curve at strike kinks
Evaluate the expiration P/L at the exact strike prices (and sample bounds), not just on the coarse grid — so max profit / max loss are exact for piecewise payoffs (butterfly peak now $800 not ~$679) and the chart renders sharp vertices for butterflies, condors, etc. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -352,9 +352,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const uniqBE = bes.filter((v,i)=> i===0 || Math.abs(v - bes[i-1]) > 1e-6);
|
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;
|
let maxY = -Infinity, minY = Infinity;
|
||||||
for (const y of ys) { if (y > maxY) maxY = y; if (y < minY) minY = y; }
|
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
|
// unbounded detection from the far-right slope of the expiration curve
|
||||||
// (downside is always bounded since the underlying can't go below 0)
|
// (downside is always bounded since the underlying can't go below 0)
|
||||||
const slopeR = (ys[Nd] - ys[Nd-1]) / (xs[Nd] - xs[Nd-1]);
|
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 lo = Math.max(0.01, spot - half), hi = spot + half;
|
||||||
const expAt = this.minDTE;
|
const expAt = this.minDTE;
|
||||||
const N = 161;
|
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 = [];
|
const expData = [], tnData = [];
|
||||||
let yMin = Infinity, yMax = -Infinity;
|
let yMin = Infinity, yMax = -Infinity;
|
||||||
for (let i = 0; i <= N; i++) {
|
for (const x of xGrid) {
|
||||||
const x = lo + (hi-lo)*i/N;
|
|
||||||
const ye = plAt(legs, net, x, expAt);
|
const ye = plAt(legs, net, x, expAt);
|
||||||
const yt = plAt(legs, net, x, this.dteOffset);
|
const yt = plAt(legs, net, x, this.dteOffset);
|
||||||
expData.push([x, +ye.toFixed(2)]);
|
expData.push([x, +ye.toFixed(2)]);
|
||||||
@@ -467,7 +477,7 @@
|
|||||||
|
|
||||||
// breakevens for light vertical lines
|
// breakevens for light vertical lines
|
||||||
const bes = [];
|
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];
|
const y0 = expData[i-1][1], y1 = expData[i][1];
|
||||||
if ((y0 < 0 && y1 > 0) || (y0 > 0 && y1 < 0)) {
|
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);
|
const x = expData[i-1][0] + (0 - y0) * (expData[i][0]-expData[i-1][0]) / (y1 - y0);
|
||||||
|
|||||||
Reference in New Issue
Block a user