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,24 +417,61 @@
function calculateMetrics() { function calculateMetrics() {
const multiplier = 100; const multiplier = 100;
let totalPremium = 0; // Determine if the strategy is a vertical spread for calls:
const buyCalls = userStrategy.filter(
(leg) => leg.action === "Buy" && leg.optionType === "Call",
);
const sellCalls = userStrategy.filter(
(leg) => leg.action === "Sell" && leg.optionType === "Call",
);
// If we have both buy and sell call legs, assume a vertical spread.
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) =>
prev.strike > curr.strike ? prev : curr,
);
// Calculate net premium (net debit)
// Note: Adjust if quantities differ.
const netDebit =
lowerBuyCall.optionPrice * multiplier * (lowerBuyCall.quantity || 1) -
higherSellCall.optionPrice *
multiplier *
(higherSellCall.quantity || 1);
// Maximum profit is the difference in strikes times the multiplier minus the net debit.
const strikeDiff =
(higherSellCall.strike - lowerBuyCall.strike) * multiplier;
const maxProfit = strikeDiff - netDebit;
const maxLoss = netDebit;
metrics = {
maxProfit: `$${formatCurrency(maxProfit)}`,
maxLoss: `$${formatCurrency(maxLoss)}`,
};
} else {
// Otherwise, use the individual leg logic.
let overallMaxProfit = 0; let overallMaxProfit = 0;
let overallMaxLoss = 0; let overallMaxLoss = 0;
let unlimitedProfit = false; let unlimitedProfit = false;
let unlimitedLoss = false; let unlimitedLoss = false;
let totalPremium = 0;
// Loop through each leg in the strategy. // Loop through each leg in the strategy.
for (let i = 0; i < userStrategy.length; i++) { for (let i = 0; i < userStrategy.length; i++) {
const leg = userStrategy[i]; const leg = userStrategy[i];
const quantity = leg?.quantity || 1; // Default to 1 if quantity is missing. const quantity = leg?.quantity || 1;
// Multiply the premium by the contract multiplier and quantity.
totalPremium += leg.optionPrice * multiplier * quantity; totalPremium += leg.optionPrice * multiplier * quantity;
const scenarioKey = `${leg?.action} ${leg?.optionType}`; const scenarioKey = `${leg?.action} ${leg?.optionType}`;
let legProfit = 0; let legProfit = 0;
let legLoss = 0; let legLoss = 0;
// Determine metrics for each leg based on its scenario.
if (scenarioKey === "Buy Call") { if (scenarioKey === "Buy Call") {
// Long call: unlimited profit, limited loss (the premium paid). // Long call: unlimited profit, limited loss (the premium paid).
legProfit = Infinity; legProfit = Infinity;
@ -430,7 +499,6 @@
legLoss = 0; legLoss = 0;
} }
// Sum only the finite numbers.
if (isFinite(legProfit)) { if (isFinite(legProfit)) {
overallMaxProfit += legProfit; overallMaxProfit += legProfit;
} }
@ -439,7 +507,6 @@
} }
} }
// Format the aggregated metrics.
metrics = { metrics = {
maxProfit: unlimitedProfit maxProfit: unlimitedProfit
? "Unlimited" ? "Unlimited"
@ -449,6 +516,7 @@
: `$${formatCurrency(overallMaxLoss)}`, : `$${formatCurrency(overallMaxLoss)}`,
}; };
} }
}
function calculateBreakevenPrice(dataPoints) { function calculateBreakevenPrice(dataPoints) {
breakEvenPrice = null; breakEvenPrice = null;
@ -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;
} }