bugfixing

This commit is contained in:
MuslemRahimi 2025-04-08 15:36:01 +02:00
parent 1a68d83792
commit c66a06f873
4 changed files with 203 additions and 92 deletions

View File

@ -74,10 +74,9 @@
let minValue = Math?.min(...rawData?.map((item) => item?.close)); let minValue = Math?.min(...rawData?.map((item) => item?.close));
let maxValue = Math?.max(...rawData?.map((item) => item?.close)); let maxValue = Math?.max(...rawData?.map((item) => item?.close));
if (displayData === "1D") { if (minValue - 0 < 1 && displayData === "1D") {
//don't delete this sometimes 1D can't find minValue //don't delete this sometimes 1D can't find minValue
minValue = data?.getStockQuote?.dayLow; minValue = data?.getStockQuote?.dayLow;
maxValue = data?.getStockQuote?.dayHigh;
} }
let padding = 0.015; let padding = 0.015;

View File

@ -72,10 +72,9 @@
let minValue = Math?.min(...rawData?.map((item) => item?.close)); let minValue = Math?.min(...rawData?.map((item) => item?.close));
let maxValue = Math?.max(...rawData?.map((item) => item?.close)); let maxValue = Math?.max(...rawData?.map((item) => item?.close));
if (displayData === "1D") { if (minValue - 0 < 1 && displayData === "1D") {
//don't delete this sometimes 1D can't find minValue //don't delete this sometimes 1D can't find minValue
minValue = data?.getStockQuote?.dayLow; minValue = data?.getStockQuote?.dayLow;
maxValue = data?.getStockQuote?.dayHigh;
} }
let padding = 0.015; let padding = 0.015;

View File

@ -417,105 +417,219 @@
function calculateMetrics() { function calculateMetrics() {
const multiplier = 100; const multiplier = 100;
// Determine if the strategy is a vertical spread for calls: // Get all legs in the strategy
const buyCalls = userStrategy.filter( const allLegs = [...userStrategy];
// Check if strategy is empty
if (allLegs.length === 0) {
metrics = {
maxProfit: "$0",
maxLoss: "$0",
};
return metrics;
}
// Classify legs by type
const buyCalls = allLegs.filter(
(leg) => leg.action === "Buy" && leg.optionType === "Call", (leg) => leg.action === "Buy" && leg.optionType === "Call",
); );
const sellCalls = userStrategy.filter( const sellCalls = allLegs.filter(
(leg) => leg.action === "Sell" && leg.optionType === "Call", (leg) => leg.action === "Sell" && leg.optionType === "Call",
); );
const buyPuts = allLegs.filter(
// If we have both buy and sell call legs, assume a vertical spread. (leg) => leg.action === "Buy" && leg.optionType === "Put",
if (buyCalls.length > 0 && sellCalls.length > 0) {
// For simplicity, assume one pairing per vertical spread.
// Find the highest strike among the sell calls (short leg) and the lowest strike among the buy calls (long leg).
const lowerBuyCall = buyCalls.reduce((prev, curr) =>
prev.strike < curr.strike ? prev : curr,
); );
const higherSellCall = sellCalls.reduce((prev, curr) => const sellPuts = allLegs.filter(
prev.strike > curr.strike ? prev : curr, (leg) => leg.action === "Sell" && leg.optionType === "Put",
); );
// Calculate net premium (net debit) // Calculate net premium for the entire strategy
// Note: Adjust if quantities differ. let netPremium = 0;
const netDebit = allLegs.forEach((leg) => {
lowerBuyCall.optionPrice * multiplier * (lowerBuyCall.quantity || 1) - const quantity = leg.quantity || 1;
higherSellCall.optionPrice * const premium = leg.optionPrice * multiplier * quantity;
multiplier * if (leg.action === "Buy") {
(higherSellCall.quantity || 1); netPremium -= premium; // Buying costs money (debit)
} else {
netPremium += premium; // Selling receives money (credit)
}
});
// Maximum profit is the difference in strikes times the multiplier minus the net debit. // Check for standard vertical spreads first
const isSimpleCallVertical =
buyCalls.length === 1 &&
sellCalls.length === 1 &&
buyPuts.length === 0 &&
sellPuts.length === 0;
const isSimplePutVertical =
buyPuts.length === 1 &&
sellPuts.length === 1 &&
buyCalls.length === 0 &&
sellCalls.length === 0;
// Handle standard vertical spreads
if (isSimpleCallVertical) {
// Call vertical spread
const buyCall = buyCalls[0];
const sellCall = sellCalls[0];
const buyQuantity = buyCall.quantity || 1;
const sellQuantity = sellCall.quantity || 1;
// Only use vertical spread calculation if quantities match
if (buyQuantity === sellQuantity) {
if (buyCall.strike < sellCall.strike) {
// Bull call spread (buy lower, sell higher)
const strikeDiff = const strikeDiff =
(higherSellCall.strike - lowerBuyCall.strike) * multiplier; (sellCall.strike - buyCall.strike) * multiplier * buyQuantity;
const maxProfit = strikeDiff - netDebit; const maxProfit = strikeDiff + netPremium;
const maxLoss = netDebit; const maxLoss = -netPremium; // Net debit is the max loss
metrics = { metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`, maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: `$${formatCurrency(maxLoss)}`, maxLoss: `$${formatCurrency(maxLoss)}`,
}; };
return metrics;
} else { } else {
// Otherwise, use the individual leg logic. // Bear call spread (buy higher, sell lower)
let overallMaxProfit = 0; const strikeDiff =
let overallMaxLoss = 0; (buyCall.strike - sellCall.strike) * multiplier * buyQuantity;
let unlimitedProfit = false; const maxProfit = netPremium; // Net credit is the max profit
let unlimitedLoss = false; const maxLoss = strikeDiff - netPremium; // Strike diff minus net credit
let totalPremium = 0;
// Loop through each leg in the strategy.
for (let i = 0; i < userStrategy.length; i++) {
const leg = userStrategy[i];
const quantity = leg?.quantity || 1;
totalPremium += leg.optionPrice * multiplier * quantity;
const scenarioKey = `${leg?.action} ${leg?.optionType}`;
let legProfit = 0;
let legLoss = 0;
if (scenarioKey === "Buy Call") {
// Long call: unlimited profit, limited loss (the premium paid).
legProfit = Infinity;
legLoss = leg.optionPrice * multiplier * quantity;
unlimitedProfit = true;
} else if (scenarioKey === "Sell Call") {
// Short call: limited profit (premium received), unlimited loss.
legProfit = leg.optionPrice * multiplier * quantity;
legLoss = -Infinity;
unlimitedLoss = true;
} else if (scenarioKey === "Buy Put") {
// Long put: profit is (strike * multiplier minus premium) and loss is the premium paid.
legProfit =
(leg.strike * multiplier - leg.optionPrice * multiplier) * quantity;
legLoss = leg.optionPrice * multiplier * quantity;
} else if (scenarioKey === "Sell Put") {
// Short put: profit is the premium received;
// Maximum loss is the difference between strike * multiplier and the premium, scaled by quantity.
legProfit = leg.optionPrice * multiplier * quantity;
legLoss =
(leg.strike * multiplier - leg.optionPrice * multiplier) * quantity;
} else {
console.error("Metrics not defined for scenario:", scenarioKey);
legProfit = 0;
legLoss = 0;
}
if (isFinite(legProfit)) {
overallMaxProfit += legProfit;
}
if (isFinite(legLoss)) {
overallMaxLoss += legLoss;
}
}
metrics = { metrics = {
maxProfit: unlimitedProfit maxProfit: `$${formatCurrency(maxProfit)}`,
? "Unlimited" maxLoss: `$${formatCurrency(maxLoss)}`,
: `$${formatCurrency(overallMaxProfit)}`, };
maxLoss: unlimitedLoss return metrics;
? "Unlimited" }
: `$${formatCurrency(overallMaxLoss)}`, }
} else if (isSimplePutVertical) {
// Put vertical spread
const buyPut = buyPuts[0];
const sellPut = sellPuts[0];
const buyQuantity = buyPut.quantity || 1;
const sellQuantity = sellPut.quantity || 1;
// Only use vertical spread calculation if quantities match
if (buyQuantity === sellQuantity) {
if (buyPut.strike > sellPut.strike) {
// Bear put spread (buy higher, sell lower)
const strikeDiff =
(buyPut.strike - sellPut.strike) * multiplier * buyQuantity;
const maxProfit = strikeDiff + netPremium;
const maxLoss = -netPremium; // Net debit is the max loss
metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: `$${formatCurrency(maxLoss)}`,
};
return metrics;
} else {
// Bull put spread (buy lower, sell higher)
const strikeDiff =
(sellPut.strike - buyPut.strike) * multiplier * buyQuantity;
const maxProfit = netPremium; // Net credit is the max profit
const maxLoss = strikeDiff - netPremium; // Strike diff minus net credit
metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: `$${formatCurrency(maxLoss)}`,
};
return metrics;
}
}
}
// For complex or custom strategies, calculate P/L at different price points
// Generate an array of price points to evaluate
const strikes = allLegs.map((leg) => leg.strike);
const minStrike = Math.min(...strikes);
const maxStrike = Math.max(...strikes);
// Include price points at 0, below the lowest strike, at each strike, and above the highest strike
const pricePoints = [0, minStrike * 0.5];
strikes.forEach((strike) => pricePoints.push(strike));
pricePoints.push(maxStrike * 1.5, maxStrike * 2);
// Add special price points for large moves
const hasShortCall = sellCalls.length > 0;
const hasLongPut = buyPuts.length > 0;
const hasShortPut = sellPuts.length > 0;
const hasLongCall = buyCalls.length > 0;
// Check for unlimited profit/loss scenarios
let unlimitedProfit = hasLongCall && !hasShortCall;
let unlimitedLoss = hasShortCall && !hasLongCall;
// Calculate P/L at each price point
let maxProfit = -Infinity;
let maxLoss = -Infinity;
pricePoints.forEach((price) => {
let profitLoss = netPremium; // Start with net premium
// Calculate P/L contribution from each leg at this price point
allLegs.forEach((leg) => {
const quantity = leg.quantity || 1;
const strike = leg.strike;
if (leg.optionType === "Call") {
if (price > strike) {
// Call is in the money
const intrinsicValue = (price - strike) * multiplier * quantity;
if (leg.action === "Buy") {
profitLoss += intrinsicValue;
} else {
profitLoss -= intrinsicValue;
}
}
} else if (leg.optionType === "Put") {
if (price < strike) {
// Put is in the money
const intrinsicValue = (strike - price) * multiplier * quantity;
if (leg.action === "Buy") {
profitLoss += intrinsicValue;
} else {
profitLoss -= intrinsicValue;
}
}
}
});
// Update max profit and max loss
maxProfit = Math.max(maxProfit, profitLoss);
maxLoss = Math.min(maxLoss, profitLoss);
});
maxLoss = Math.abs(maxLoss);
// Handle unlimited scenarios
if (unlimitedProfit) {
metrics = {
maxProfit: "Unlimited",
maxLoss: `$${formatCurrency(maxLoss)}`,
};
} else if (unlimitedLoss) {
metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: "Unlimited",
};
} else {
// Finite profit and loss
metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: `$${formatCurrency(maxLoss)}`,
}; };
} }
// Special case: if maxLoss is negative, it's actually a profit floor
if (maxLoss < 0) {
metrics.maxLoss = "$0"; // Can't lose money
metrics.maxProfit = `$${formatCurrency(Math.max(maxProfit, Math.abs(maxLoss)))}`;
}
return metrics;
} }
function calculateBreakevenPrice(dataPoints) { function calculateBreakevenPrice(dataPoints) {

View File

@ -76,10 +76,9 @@
let minValue = Math?.min(...rawData?.map((item) => item?.close)); let minValue = Math?.min(...rawData?.map((item) => item?.close));
let maxValue = Math?.max(...rawData?.map((item) => item?.close)); let maxValue = Math?.max(...rawData?.map((item) => item?.close));
if (displayData === "1D") { if (minValue - 0 < 1 && displayData === "1D") {
//don't delete this sometimes 1D can't find minValue //don't delete this sometimes 1D can't find minValue
minValue = data?.getStockQuote?.dayLow; minValue = data?.getStockQuote?.dayLow;
maxValue = data?.getStockQuote?.dayHigh;
} }
let padding = 0.015; let padding = 0.015;