update calculator

This commit is contained in:
MuslemRahimi 2025-04-08 15:17:13 +02:00
parent 441902dca7
commit 1a68d83792

View File

@ -125,25 +125,55 @@
selectedAction = "Sell"; selectedAction = "Sell";
break; break;
default: default:
console.warn("Unknown strategy:", strategy); break;
selectedOptionType = null;
selectedAction = null;
} }
if (["Bull Call Spread"]?.includes(selectedStrategy)) { if (["Bull Call Spread"].includes(selectedStrategy)) {
// Find the lower strike first (for the Buy leg)
const lowerStrike = selectedStrike;
// Find a higher strike in the available strikeList for the Sell leg
// First, calculate the target strike (40% higher)
const targetHigherStrike = lowerStrike * 1.4;
// Find the closest available strike price that is higher than the lower strike
let higherStrike;
if (strikeList && strikeList.length > 0) {
// Filter strikes that are higher than the lower strike
const higherStrikes = strikeList?.filter(
(strike) => strike > lowerStrike,
);
if (higherStrikes.length > 0) {
// Find the strike closest to our target from the available higher strikes
higherStrike = higherStrikes?.reduce((closest, strike) => {
return Math.abs(strike - targetHigherStrike) <
Math.abs(closest - targetHigherStrike)
? strike
: closest;
}, higherStrikes[0]);
} else {
// If no higher strikes available, use the highest available strike
higherStrike = Math.max(...strikeList);
}
} else {
// Fallback if strikeList is empty
higherStrike = lowerStrike * 1.4;
}
userStrategy = [ userStrategy = [
{ {
strike: selectedStrike, strike: lowerStrike,
optionType: "Call", optionType: "Call",
date: selectedDate, date: selectedDate,
optionPrice: selectedOptionPrice, optionPrice: 0, // This will be updated when loadData() is called
quantity: 1, quantity: 1,
action: "Buy", action: "Buy",
}, },
{ {
strike: selectedStrike, strike: higherStrike,
optionType: "Call", optionType: "Call",
optionPrice: 0, // This will be updated when loadData() is called
date: selectedDate, date: selectedDate,
optionPrice: selectedOptionPrice,
quantity: 1, quantity: 1,
action: "Sell", action: "Sell",
}, },
@ -160,7 +190,8 @@
}, },
]; ];
} }
userStrategy = [...userStrategy];
await loadData();
shouldUpdate = true; shouldUpdate = true;
} }
@ -248,6 +279,7 @@
// Calculate break-even and metrics for single-leg strategies // Calculate break-even and metrics for single-leg strategies
calculateMetrics(); calculateMetrics();
calculateBreakevenPrice(dataPoints); calculateBreakevenPrice(dataPoints);
console.log(userStrategy);
// Build the chart options // Build the chart options
const options = { const options = {
credits: { enabled: false }, credits: { enabled: false },
@ -385,69 +417,105 @@
function calculateMetrics() { function calculateMetrics() {
const multiplier = 100; const multiplier = 100;
let totalPremium = 0; // Determine if the strategy is a vertical spread for calls:
let overallMaxProfit = 0; const buyCalls = userStrategy.filter(
let overallMaxLoss = 0; (leg) => leg.action === "Buy" && leg.optionType === "Call",
let unlimitedProfit = false; );
let unlimitedLoss = false; const sellCalls = userStrategy.filter(
(leg) => leg.action === "Sell" && leg.optionType === "Call",
);
// Loop through each leg in the strategy. // If we have both buy and sell call legs, assume a vertical spread.
for (let i = 0; i < userStrategy.length; i++) { if (buyCalls.length > 0 && sellCalls.length > 0) {
const leg = userStrategy[i]; // For simplicity, assume one pairing per vertical spread.
const quantity = leg?.quantity || 1; // Default to 1 if quantity is missing. // 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) =>
prev.strike > curr.strike ? prev : curr,
);
// Multiply the premium by the contract multiplier and quantity. // Calculate net premium (net debit)
totalPremium += leg.optionPrice * multiplier * quantity; // Note: Adjust if quantities differ.
const scenarioKey = `${leg?.action} ${leg?.optionType}`; const netDebit =
let legProfit = 0; lowerBuyCall.optionPrice * multiplier * (lowerBuyCall.quantity || 1) -
let legLoss = 0; higherSellCall.optionPrice *
multiplier *
(higherSellCall.quantity || 1);
// Determine metrics for each leg based on its scenario. // Maximum profit is the difference in strikes times the multiplier minus the net debit.
if (scenarioKey === "Buy Call") { const strikeDiff =
// Long call: unlimited profit, limited loss (the premium paid). (higherSellCall.strike - lowerBuyCall.strike) * multiplier;
legProfit = Infinity; const maxProfit = strikeDiff - netDebit;
legLoss = leg.optionPrice * multiplier * quantity; const maxLoss = netDebit;
unlimitedProfit = true;
} else if (scenarioKey === "Sell Call") { metrics = {
// Short call: limited profit (premium received), unlimited loss. maxProfit: `$${formatCurrency(maxProfit)}`,
legProfit = leg.optionPrice * multiplier * quantity; maxLoss: `$${formatCurrency(maxLoss)}`,
legLoss = -Infinity; };
unlimitedLoss = true; } else {
} else if (scenarioKey === "Buy Put") { // Otherwise, use the individual leg logic.
// Long put: profit is (strike * multiplier minus premium) and loss is the premium paid. let overallMaxProfit = 0;
legProfit = let overallMaxLoss = 0;
(leg.strike * multiplier - leg.optionPrice * multiplier) * quantity; let unlimitedProfit = false;
legLoss = leg.optionPrice * multiplier * quantity; let unlimitedLoss = false;
} else if (scenarioKey === "Sell Put") { let totalPremium = 0;
// Short put: profit is the premium received;
// Maximum loss is the difference between strike * multiplier and the premium, scaled by quantity. // Loop through each leg in the strategy.
legProfit = leg.optionPrice * multiplier * quantity; for (let i = 0; i < userStrategy.length; i++) {
legLoss = const leg = userStrategy[i];
(leg.strike * multiplier - leg.optionPrice * multiplier) * quantity; const quantity = leg?.quantity || 1;
} else { totalPremium += leg.optionPrice * multiplier * quantity;
console.error("Metrics not defined for scenario:", scenarioKey);
legProfit = 0; const scenarioKey = `${leg?.action} ${leg?.optionType}`;
legLoss = 0; 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;
}
} }
// Sum only the finite numbers. metrics = {
if (isFinite(legProfit)) { maxProfit: unlimitedProfit
overallMaxProfit += legProfit; ? "Unlimited"
} : `$${formatCurrency(overallMaxProfit)}`,
if (isFinite(legLoss)) { maxLoss: unlimitedLoss
overallMaxLoss += legLoss; ? "Unlimited"
} : `$${formatCurrency(overallMaxLoss)}`,
};
} }
// Format the aggregated metrics.
metrics = {
maxProfit: unlimitedProfit
? "Unlimited"
: `$${formatCurrency(overallMaxProfit)}`,
maxLoss: unlimitedLoss
? "Unlimited"
: `$${formatCurrency(overallMaxLoss)}`,
};
} }
function calculateBreakevenPrice(dataPoints) { function calculateBreakevenPrice(dataPoints) {
@ -542,13 +610,16 @@
item.strikeList = strikeList; item.strikeList = strikeList;
// Find closest strike to current stock price // Find closest strike to current stock price
if (!strikeList?.includes(item?.strike) && strikeList?.length > 0) { if (
selectedStrike = strikeList.reduce((closest, strike) => { !item.strikeList?.includes(item?.strike) &&
item.strikeList?.length > 0
) {
selectedStrike = item?.strikeList?.reduce((closest, strike) => {
return Math.abs(strike - currentStockPrice) < return Math.abs(strike - currentStockPrice) <
Math.abs(closest - currentStockPrice) Math.abs(closest - currentStockPrice)
? strike ? strike
: closest; : closest;
}, strikeList[0]); }, item.strikeList[0]);
item.strike = selectedStrike; item.strike = selectedStrike;
} }