reworked calcs lib
This commit is contained in:
parent
da75979574
commit
0397796cfb
11 changed files with 2648 additions and 2359 deletions
810
libs/utils/src/calculations/options-pricing.ts.disabled
Normal file
810
libs/utils/src/calculations/options-pricing.ts.disabled
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
/**
|
||||
* Options Pricing Models
|
||||
* Implementation of various options pricing models and Greeks calculations
|
||||
*/
|
||||
|
||||
export interface OptionParameters {
|
||||
spotPrice: number;
|
||||
strikePrice: number;
|
||||
timeToExpiry: number; // in years
|
||||
riskFreeRate: number;
|
||||
volatility: number;
|
||||
dividendYield?: number;
|
||||
}
|
||||
|
||||
export interface OptionPricing {
|
||||
callPrice: number;
|
||||
putPrice: number;
|
||||
intrinsicValueCall: number;
|
||||
intrinsicValuePut: number;
|
||||
timeValueCall: number;
|
||||
timeValuePut: number;
|
||||
}
|
||||
|
||||
export interface GreeksCalculation {
|
||||
delta: number;
|
||||
gamma: number;
|
||||
theta: number;
|
||||
vega: number;
|
||||
rho: number;
|
||||
}
|
||||
|
||||
export interface ImpliedVolatilityResult {
|
||||
impliedVolatility: number;
|
||||
iterations: number;
|
||||
converged: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Black-Scholes option pricing model
|
||||
*/
|
||||
export function blackScholes(params: OptionParameters): OptionPricing {
|
||||
const {
|
||||
spotPrice,
|
||||
strikePrice,
|
||||
timeToExpiry,
|
||||
riskFreeRate,
|
||||
volatility,
|
||||
dividendYield = 0,
|
||||
} = params;
|
||||
|
||||
if (timeToExpiry <= 0) {
|
||||
const intrinsicValueCall = Math.max(spotPrice - strikePrice, 0);
|
||||
const intrinsicValuePut = Math.max(strikePrice - spotPrice, 0);
|
||||
|
||||
return {
|
||||
callPrice: intrinsicValueCall,
|
||||
putPrice: intrinsicValuePut,
|
||||
intrinsicValueCall,
|
||||
intrinsicValuePut,
|
||||
timeValueCall: 0,
|
||||
timeValuePut: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const d1 =
|
||||
(Math.log(spotPrice / strikePrice) +
|
||||
(riskFreeRate - dividendYield + 0.5 * volatility * volatility) * timeToExpiry) /
|
||||
(volatility * Math.sqrt(timeToExpiry));
|
||||
const d2 = d1 - volatility * Math.sqrt(timeToExpiry);
|
||||
|
||||
const nd1 = normalCDF(d1);
|
||||
const nd2 = normalCDF(d2);
|
||||
const nMinusd1 = normalCDF(-d1);
|
||||
const nMinusd2 = normalCDF(-d2);
|
||||
|
||||
const callPrice =
|
||||
spotPrice * Math.exp(-dividendYield * timeToExpiry) * nd1 -
|
||||
strikePrice * Math.exp(-riskFreeRate * timeToExpiry) * nd2;
|
||||
|
||||
const putPrice =
|
||||
strikePrice * Math.exp(-riskFreeRate * timeToExpiry) * nMinusd2 -
|
||||
spotPrice * Math.exp(-dividendYield * timeToExpiry) * nMinusd1;
|
||||
|
||||
const intrinsicValueCall = Math.max(spotPrice - strikePrice, 0);
|
||||
const intrinsicValuePut = Math.max(strikePrice - spotPrice, 0);
|
||||
|
||||
const timeValueCall = callPrice - intrinsicValueCall;
|
||||
const timeValuePut = putPrice - intrinsicValuePut;
|
||||
|
||||
return {
|
||||
callPrice,
|
||||
putPrice,
|
||||
intrinsicValueCall,
|
||||
intrinsicValuePut,
|
||||
timeValueCall,
|
||||
timeValuePut,
|
||||
};
|
||||
}
|
||||
|
||||
export function impliedVolatility(
|
||||
price: number,
|
||||
S: number,
|
||||
K: number,
|
||||
T: number,
|
||||
r: number,
|
||||
isCall = true
|
||||
): number {
|
||||
// …Newton–Raphson on σ to match blackScholesPrice
|
||||
let sigma = 0.2; // Initial guess for volatility
|
||||
const tolerance = 1e-6;
|
||||
const maxIterations = 100;
|
||||
let iteration = 0;
|
||||
let priceDiff = 1; // Initialize to a non-zero value
|
||||
while (Math.abs(priceDiff) > tolerance && iteration < maxIterations) {
|
||||
const params: OptionParameters = {
|
||||
spotPrice: S,
|
||||
strikePrice: K,
|
||||
timeToExpiry: T,
|
||||
riskFreeRate: r,
|
||||
volatility: sigma,
|
||||
};
|
||||
|
||||
const calculatedPrice = isCall ? blackScholes(params).callPrice : blackScholes(params).putPrice;
|
||||
priceDiff = calculatedPrice - price;
|
||||
|
||||
// Calculate Vega
|
||||
const greeks = calculateGreeks(params, isCall ? 'call' : 'put');
|
||||
const vega = greeks.vega * 100; // Convert from percentage to absolute
|
||||
|
||||
if (vega === 0) {
|
||||
break; // Avoid division by zero
|
||||
}
|
||||
|
||||
sigma -= priceDiff / vega; // Update volatility estimate
|
||||
iteration++;
|
||||
}
|
||||
if (iteration === maxIterations) {
|
||||
console.warn('Implied volatility calculation did not converge');
|
||||
}
|
||||
|
||||
if (sigma < 0) {
|
||||
console.warn('Calculated implied volatility is negative, returning 0');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sigma > 10) {
|
||||
console.warn('Calculated implied volatility is too high, returning 10');
|
||||
return 10; // Cap at a reasonable maximum
|
||||
}
|
||||
if (isNaN(sigma)) {
|
||||
console.warn('Calculated implied volatility is NaN, returning 0');
|
||||
return 0;
|
||||
}
|
||||
return sigma;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate option Greeks using Black-Scholes model
|
||||
*/
|
||||
export function calculateGreeks(
|
||||
params: OptionParameters,
|
||||
optionType: 'call' | 'put' = 'call'
|
||||
): GreeksCalculation {
|
||||
const {
|
||||
spotPrice,
|
||||
strikePrice,
|
||||
timeToExpiry,
|
||||
riskFreeRate,
|
||||
volatility,
|
||||
dividendYield = 0,
|
||||
} = params;
|
||||
|
||||
if (timeToExpiry <= 0) {
|
||||
return {
|
||||
delta:
|
||||
optionType === 'call'
|
||||
? spotPrice > strikePrice
|
||||
? 1
|
||||
: 0
|
||||
: spotPrice < strikePrice
|
||||
? -1
|
||||
: 0,
|
||||
gamma: 0,
|
||||
theta: 0,
|
||||
vega: 0,
|
||||
rho: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const d1 =
|
||||
(Math.log(spotPrice / strikePrice) +
|
||||
(riskFreeRate - dividendYield + 0.5 * volatility * volatility) * timeToExpiry) /
|
||||
(volatility * Math.sqrt(timeToExpiry));
|
||||
const d2 = d1 - volatility * Math.sqrt(timeToExpiry);
|
||||
|
||||
const nd1 = normalCDF(d1);
|
||||
const nd2 = normalCDF(d2);
|
||||
const npd1 = normalPDF(d1);
|
||||
|
||||
// Delta
|
||||
const callDelta = Math.exp(-dividendYield * timeToExpiry) * nd1;
|
||||
const putDelta = Math.exp(-dividendYield * timeToExpiry) * (nd1 - 1);
|
||||
const delta = optionType === 'call' ? callDelta : putDelta;
|
||||
|
||||
// Gamma (same for calls and puts)
|
||||
const gamma =
|
||||
(Math.exp(-dividendYield * timeToExpiry) * npd1) /
|
||||
(spotPrice * volatility * Math.sqrt(timeToExpiry));
|
||||
|
||||
// Theta
|
||||
const term1 =
|
||||
-(spotPrice * npd1 * volatility * Math.exp(-dividendYield * timeToExpiry)) /
|
||||
(2 * Math.sqrt(timeToExpiry));
|
||||
const term2Call = riskFreeRate * strikePrice * Math.exp(-riskFreeRate * timeToExpiry) * nd2;
|
||||
const term2Put =
|
||||
-riskFreeRate * strikePrice * Math.exp(-riskFreeRate * timeToExpiry) * normalCDF(-d2);
|
||||
const term3 =
|
||||
dividendYield *
|
||||
spotPrice *
|
||||
Math.exp(-dividendYield * timeToExpiry) *
|
||||
(optionType === 'call' ? nd1 : normalCDF(-d1));
|
||||
|
||||
const theta =
|
||||
optionType === 'call' ? (term1 - term2Call + term3) / 365 : (term1 + term2Put + term3) / 365;
|
||||
|
||||
// Vega (same for calls and puts)
|
||||
const vega =
|
||||
(spotPrice * Math.exp(-dividendYield * timeToExpiry) * npd1 * Math.sqrt(timeToExpiry)) / 100;
|
||||
|
||||
// Rho
|
||||
const callRho = (strikePrice * timeToExpiry * Math.exp(-riskFreeRate * timeToExpiry) * nd2) / 100;
|
||||
const putRho =
|
||||
(-strikePrice * timeToExpiry * Math.exp(-riskFreeRate * timeToExpiry) * normalCDF(-d2)) / 100;
|
||||
const rho = optionType === 'call' ? callRho : putRho;
|
||||
|
||||
return {
|
||||
delta,
|
||||
gamma,
|
||||
theta,
|
||||
vega,
|
||||
rho,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate implied volatility using Newton-Raphson method
|
||||
*/
|
||||
export function calculateImpliedVolatility(
|
||||
marketPrice: number,
|
||||
spotPrice: number,
|
||||
strikePrice: number,
|
||||
timeToExpiry: number,
|
||||
riskFreeRate: number,
|
||||
optionType: 'call' | 'put' = 'call',
|
||||
dividendYield: number = 0,
|
||||
initialGuess: number = 0.2,
|
||||
tolerance: number = 1e-6,
|
||||
maxIterations: number = 100
|
||||
): ImpliedVolatilityResult {
|
||||
let volatility = initialGuess;
|
||||
let iterations = 0;
|
||||
let converged = false;
|
||||
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
iterations = i + 1;
|
||||
|
||||
const params: OptionParameters = {
|
||||
spotPrice,
|
||||
strikePrice,
|
||||
timeToExpiry,
|
||||
riskFreeRate,
|
||||
volatility,
|
||||
dividendYield,
|
||||
};
|
||||
|
||||
const pricing = blackScholes(params);
|
||||
const theoreticalPrice = optionType === 'call' ? pricing.callPrice : pricing.putPrice;
|
||||
|
||||
const priceDiff = theoreticalPrice - marketPrice;
|
||||
|
||||
if (Math.abs(priceDiff) < tolerance) {
|
||||
converged = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate vega for Newton-Raphson
|
||||
const greeks = calculateGreeks(params, optionType);
|
||||
const vega = greeks.vega * 100; // Convert back from percentage
|
||||
|
||||
if (Math.abs(vega) < 1e-10) {
|
||||
break; // Avoid division by zero
|
||||
}
|
||||
|
||||
volatility = volatility - priceDiff / vega;
|
||||
|
||||
// Keep volatility within reasonable bounds
|
||||
volatility = Math.max(0.001, Math.min(volatility, 10));
|
||||
}
|
||||
|
||||
return {
|
||||
impliedVolatility: volatility,
|
||||
iterations,
|
||||
converged,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Binomial option pricing model
|
||||
*/
|
||||
export function binomialOptionPricing(
|
||||
params: OptionParameters,
|
||||
optionType: 'call' | 'put' = 'call',
|
||||
americanStyle: boolean = false,
|
||||
steps: number = 100
|
||||
): OptionPricing {
|
||||
const {
|
||||
spotPrice,
|
||||
strikePrice,
|
||||
timeToExpiry,
|
||||
riskFreeRate,
|
||||
volatility,
|
||||
dividendYield = 0,
|
||||
} = params;
|
||||
|
||||
const dt = timeToExpiry / steps;
|
||||
const u = Math.exp(volatility * Math.sqrt(dt));
|
||||
const d = 1 / u;
|
||||
const p = (Math.exp((riskFreeRate - dividendYield) * dt) - d) / (u - d);
|
||||
const discount = Math.exp(-riskFreeRate * dt);
|
||||
|
||||
// Create price tree
|
||||
const stockPrices: number[][] = [];
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
stockPrices[i] = [];
|
||||
for (let j = 0; j <= i; j++) {
|
||||
stockPrices[i][j] = spotPrice * Math.pow(u, i - j) * Math.pow(d, j);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate option values at expiration
|
||||
const optionValues: number[][] = [];
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
optionValues[i] = [];
|
||||
}
|
||||
|
||||
for (let j = 0; j <= steps; j++) {
|
||||
if (optionType === 'call') {
|
||||
optionValues[steps][j] = Math.max(stockPrices[steps][j] - strikePrice, 0);
|
||||
} else {
|
||||
optionValues[steps][j] = Math.max(strikePrice - stockPrices[steps][j], 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Work backwards through the tree
|
||||
for (let i = steps - 1; i >= 0; i--) {
|
||||
for (let j = 0; j <= i; j++) {
|
||||
// European option value
|
||||
const holdValue =
|
||||
discount * (p * optionValues[i + 1][j] + (1 - p) * optionValues[i + 1][j + 1]);
|
||||
|
||||
if (americanStyle) {
|
||||
// American option - can exercise early
|
||||
const exerciseValue =
|
||||
optionType === 'call'
|
||||
? Math.max(stockPrices[i][j] - strikePrice, 0)
|
||||
: Math.max(strikePrice - stockPrices[i][j], 0);
|
||||
|
||||
optionValues[i][j] = Math.max(holdValue, exerciseValue);
|
||||
} else {
|
||||
optionValues[i][j] = holdValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const price = optionValues[0][0];
|
||||
const intrinsicValue =
|
||||
optionType === 'call'
|
||||
? Math.max(spotPrice - strikePrice, 0)
|
||||
: Math.max(strikePrice - spotPrice, 0);
|
||||
const timeValue = price - intrinsicValue;
|
||||
|
||||
if (optionType === 'call') {
|
||||
return {
|
||||
callPrice: price,
|
||||
putPrice: 0, // Not calculated
|
||||
intrinsicValueCall: intrinsicValue,
|
||||
intrinsicValuePut: 0,
|
||||
timeValueCall: timeValue,
|
||||
timeValuePut: 0,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
callPrice: 0, // Not calculated
|
||||
putPrice: price,
|
||||
intrinsicValueCall: 0,
|
||||
intrinsicValuePut: intrinsicValue,
|
||||
timeValueCall: 0,
|
||||
timeValuePut: timeValue,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monte Carlo option pricing
|
||||
*/
|
||||
export function monteCarloOptionPricing(
|
||||
params: OptionParameters,
|
||||
optionType: 'call' | 'put' = 'call',
|
||||
numSimulations: number = 100000
|
||||
): OptionPricing {
|
||||
const {
|
||||
spotPrice,
|
||||
strikePrice,
|
||||
timeToExpiry,
|
||||
riskFreeRate,
|
||||
volatility,
|
||||
dividendYield = 0,
|
||||
} = params;
|
||||
|
||||
let totalPayoff = 0;
|
||||
|
||||
for (let i = 0; i < numSimulations; i++) {
|
||||
// Generate random price path
|
||||
const z = boxMullerTransform();
|
||||
const finalPrice =
|
||||
spotPrice *
|
||||
Math.exp(
|
||||
(riskFreeRate - dividendYield - 0.5 * volatility * volatility) * timeToExpiry +
|
||||
volatility * Math.sqrt(timeToExpiry) * z
|
||||
);
|
||||
|
||||
// Calculate payoff
|
||||
const payoff =
|
||||
optionType === 'call'
|
||||
? Math.max(finalPrice - strikePrice, 0)
|
||||
: Math.max(strikePrice - finalPrice, 0);
|
||||
|
||||
totalPayoff += payoff;
|
||||
}
|
||||
|
||||
const averagePayoff = totalPayoff / numSimulations;
|
||||
const price = averagePayoff * Math.exp(-riskFreeRate * timeToExpiry);
|
||||
|
||||
const intrinsicValue =
|
||||
optionType === 'call'
|
||||
? Math.max(spotPrice - strikePrice, 0)
|
||||
: Math.max(strikePrice - spotPrice, 0);
|
||||
const timeValue = price - intrinsicValue;
|
||||
|
||||
if (optionType === 'call') {
|
||||
return {
|
||||
callPrice: price,
|
||||
putPrice: 0,
|
||||
intrinsicValueCall: intrinsicValue,
|
||||
intrinsicValuePut: 0,
|
||||
timeValueCall: timeValue,
|
||||
timeValuePut: 0,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
callPrice: 0,
|
||||
putPrice: price,
|
||||
intrinsicValueCall: 0,
|
||||
intrinsicValuePut: intrinsicValue,
|
||||
timeValueCall: 0,
|
||||
timeValuePut: timeValue,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate option portfolio risk metrics
|
||||
*/
|
||||
export function calculateOptionPortfolioRisk(
|
||||
positions: Array<{
|
||||
optionType: 'call' | 'put';
|
||||
quantity: number;
|
||||
params: OptionParameters;
|
||||
}>
|
||||
): {
|
||||
totalDelta: number;
|
||||
totalGamma: number;
|
||||
totalTheta: number;
|
||||
totalVega: number;
|
||||
totalRho: number;
|
||||
portfolioValue: number;
|
||||
} {
|
||||
let totalDelta = 0;
|
||||
let totalGamma = 0;
|
||||
let totalTheta = 0;
|
||||
let totalVega = 0;
|
||||
let totalRho = 0;
|
||||
let portfolioValue = 0;
|
||||
|
||||
for (const position of positions) {
|
||||
const greeks = calculateGreeks(position.params, position.optionType);
|
||||
const pricing = blackScholes(position.params);
|
||||
const optionPrice = position.optionType === 'call' ? pricing.callPrice : pricing.putPrice;
|
||||
|
||||
totalDelta += greeks.delta * position.quantity;
|
||||
totalGamma += greeks.gamma * position.quantity;
|
||||
totalTheta += greeks.theta * position.quantity;
|
||||
totalVega += greeks.vega * position.quantity;
|
||||
totalRho += greeks.rho * position.quantity;
|
||||
portfolioValue += optionPrice * position.quantity;
|
||||
}
|
||||
|
||||
return {
|
||||
totalDelta,
|
||||
totalGamma,
|
||||
totalTheta,
|
||||
totalVega,
|
||||
totalRho,
|
||||
portfolioValue,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Volatility surface interpolation
|
||||
*/
|
||||
export function interpolateVolatilitySurface(
|
||||
strikes: number[],
|
||||
expiries: number[],
|
||||
volatilities: number[][],
|
||||
targetStrike: number,
|
||||
targetExpiry: number
|
||||
): number {
|
||||
// Simplified bilinear interpolation
|
||||
// In production, use more sophisticated interpolation methods
|
||||
|
||||
// Find surrounding points
|
||||
let strikeIndex = 0;
|
||||
let expiryIndex = 0;
|
||||
|
||||
for (let i = 0; i < strikes.length - 1; i++) {
|
||||
if (targetStrike >= strikes[i] && targetStrike <= strikes[i + 1]) {
|
||||
strikeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < expiries.length - 1; i++) {
|
||||
if (targetExpiry >= expiries[i] && targetExpiry <= expiries[i + 1]) {
|
||||
expiryIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Bilinear interpolation
|
||||
const x1 = strikes[strikeIndex];
|
||||
const x2 = strikes[strikeIndex + 1];
|
||||
const y1 = expiries[expiryIndex];
|
||||
const y2 = expiries[expiryIndex + 1];
|
||||
|
||||
const q11 = volatilities[expiryIndex][strikeIndex];
|
||||
const q12 = volatilities[expiryIndex + 1][strikeIndex];
|
||||
const q21 = volatilities[expiryIndex][strikeIndex + 1];
|
||||
const q22 = volatilities[expiryIndex + 1][strikeIndex + 1];
|
||||
|
||||
const wx = (targetStrike - x1) / (x2 - x1);
|
||||
const wy = (targetExpiry - y1) / (y2 - y1);
|
||||
|
||||
return q11 * (1 - wx) * (1 - wy) + q21 * wx * (1 - wy) + q12 * (1 - wx) * wy + q22 * wx * wy;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
/**
|
||||
* Normal cumulative distribution function
|
||||
*/
|
||||
function normalCDF(x: number): number {
|
||||
return 0.5 * (1 + erf(x / Math.sqrt(2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal probability density function
|
||||
*/
|
||||
function normalPDF(x: number): number {
|
||||
return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error function approximation
|
||||
*/
|
||||
function erf(x: number): number {
|
||||
// Abramowitz and Stegun approximation
|
||||
const a1 = 0.254829592;
|
||||
const a2 = -0.284496736;
|
||||
const a3 = 1.421413741;
|
||||
const a4 = -1.453152027;
|
||||
const a5 = 1.061405429;
|
||||
const p = 0.3275911;
|
||||
|
||||
const sign = x >= 0 ? 1 : -1;
|
||||
x = Math.abs(x);
|
||||
|
||||
const t = 1.0 / (1.0 + p * x);
|
||||
const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
|
||||
|
||||
return sign * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Box-Muller transformation for normal random numbers
|
||||
*/
|
||||
function boxMullerTransform(): number {
|
||||
let u1 = Math.random();
|
||||
const u2 = Math.random();
|
||||
|
||||
// Ensure u1 is not zero
|
||||
while (u1 === 0) {
|
||||
u1 = Math.random();
|
||||
}
|
||||
|
||||
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prices a straddle option strategy
|
||||
*/
|
||||
export function straddle(params: OptionParameters): {
|
||||
callPrice: number;
|
||||
putPrice: number;
|
||||
strategyCost: number;
|
||||
} {
|
||||
const callOption = blackScholes(params);
|
||||
const putOption = blackScholes(params);
|
||||
const strategyCost = callOption.callPrice + putOption.putPrice;
|
||||
|
||||
return {
|
||||
callPrice: callOption.callPrice,
|
||||
putPrice: putOption.putPrice,
|
||||
strategyCost: strategyCost,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prices a strangle option strategy
|
||||
*/
|
||||
export function strangle(
|
||||
callParams: OptionParameters,
|
||||
putParams: OptionParameters
|
||||
): { callPrice: number; putPrice: number; strategyCost: number } {
|
||||
const callOption = blackScholes(callParams);
|
||||
const putOption = blackScholes(putParams);
|
||||
const strategyCost = callOption.callPrice + putOption.putPrice;
|
||||
|
||||
return {
|
||||
callPrice: callOption.callPrice,
|
||||
putPrice: putOption.putPrice,
|
||||
strategyCost: strategyCost,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prices a butterfly option strategy
|
||||
*/
|
||||
export function butterfly(
|
||||
lowerStrikeParams: OptionParameters,
|
||||
middleStrikeParams: OptionParameters,
|
||||
upperStrikeParams: OptionParameters
|
||||
): {
|
||||
lowerCallPrice: number;
|
||||
middleCallPrice: number;
|
||||
upperCallPrice: number;
|
||||
strategyCost: number;
|
||||
} {
|
||||
const lowerCall = blackScholes(lowerStrikeParams);
|
||||
const middleCall = blackScholes(middleStrikeParams);
|
||||
const upperCall = blackScholes(upperStrikeParams);
|
||||
|
||||
const strategyCost = lowerCall.callPrice - 2 * middleCall.callPrice + upperCall.callPrice;
|
||||
|
||||
return {
|
||||
lowerCallPrice: lowerCall.callPrice,
|
||||
middleCallPrice: middleCall.callPrice,
|
||||
upperCallPrice: upperCall.callPrice,
|
||||
strategyCost: strategyCost,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prices a condor option strategy
|
||||
*/
|
||||
export function condor(
|
||||
lowerStrikeParams: OptionParameters,
|
||||
middleLowerStrikeParams: OptionParameters,
|
||||
middleUpperStrikeParams: OptionParameters,
|
||||
upperStrikeParams: OptionParameters
|
||||
): {
|
||||
lowerCallPrice: number;
|
||||
middleLowerCallPrice: number;
|
||||
middleUpperCallPrice: number;
|
||||
upperCallPrice: number;
|
||||
strategyCost: number;
|
||||
} {
|
||||
const lowerCall = blackScholes(lowerStrikeParams);
|
||||
const middleLowerCall = blackScholes(middleLowerStrikeParams);
|
||||
const middleUpperCall = blackScholes(middleUpperStrikeParams);
|
||||
const upperCall = blackScholes(upperStrikeParams);
|
||||
|
||||
const strategyCost =
|
||||
lowerCall.callPrice -
|
||||
middleLowerCall.callPrice -
|
||||
middleUpperCall.callPrice +
|
||||
upperCall.callPrice;
|
||||
|
||||
return {
|
||||
lowerCallPrice: lowerCall.callPrice,
|
||||
middleLowerCallPrice: middleLowerCall.callPrice,
|
||||
middleUpperCallPrice: middleUpperCall.callPrice,
|
||||
upperCallPrice: upperCall.callPrice,
|
||||
strategyCost: strategyCost,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates combined Greeks for an option strategy
|
||||
*/
|
||||
export function calculateStrategyGreeks(
|
||||
positions: Array<{
|
||||
optionType: 'call' | 'put';
|
||||
quantity: number;
|
||||
params: OptionParameters;
|
||||
}>
|
||||
): GreeksCalculation {
|
||||
let totalDelta = 0;
|
||||
let totalGamma = 0;
|
||||
let totalTheta = 0;
|
||||
let totalVega = 0;
|
||||
let totalRho = 0;
|
||||
|
||||
for (const position of positions) {
|
||||
const greeks = calculateGreeks(position.params, position.optionType);
|
||||
|
||||
totalDelta += greeks.delta * position.quantity;
|
||||
totalGamma += greeks.gamma * position.quantity;
|
||||
totalTheta += greeks.theta * position.quantity;
|
||||
totalVega += greeks.vega * position.quantity;
|
||||
totalRho += greeks.rho * position.quantity;
|
||||
}
|
||||
|
||||
return {
|
||||
delta: totalDelta,
|
||||
gamma: totalGamma,
|
||||
theta: totalTheta,
|
||||
vega: totalVega,
|
||||
rho: totalRho,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Black-Scholes option pricing model with greeks
|
||||
*/
|
||||
export function blackScholesWithGreeks(
|
||||
params: OptionParameters,
|
||||
optionType: 'call' | 'put' = 'call'
|
||||
): { pricing: OptionPricing; greeks: GreeksCalculation } {
|
||||
const pricing = blackScholes(params);
|
||||
const greeks = calculateGreeks(params, optionType);
|
||||
return { pricing, greeks };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the breakeven point for a call option at expiration
|
||||
*/
|
||||
export function callBreakeven(strikePrice: number, callPrice: number): number {
|
||||
return strikePrice + callPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the breakeven point for a put option at expiration
|
||||
*/
|
||||
export function putBreakeven(strikePrice: number, putPrice: number): number {
|
||||
return strikePrice - putPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the probability of profit for a call option at expiration
|
||||
*/
|
||||
export function callProbabilityOfProfit(
|
||||
spotPrice: number,
|
||||
strikePrice: number,
|
||||
timeToExpiry: number,
|
||||
riskFreeRate: number,
|
||||
volatility: number
|
||||
): number {
|
||||
const d1 =
|
||||
(Math.log(spotPrice / strikePrice) +
|
||||
(riskFreeRate + 0.5 * volatility * volatility) * timeToExpiry) /
|
||||
(volatility * Math.sqrt(timeToExpiry));
|
||||
return normalCDF(d1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the probability of profit for a put option at expiration
|
||||
*/
|
||||
export function putProbabilityOfProfit(
|
||||
spotPrice: number,
|
||||
strikePrice: number,
|
||||
timeToExpiry: number,
|
||||
riskFreeRate: number,
|
||||
volatility: number
|
||||
): number {
|
||||
const d1 =
|
||||
(Math.log(spotPrice / strikePrice) +
|
||||
(riskFreeRate + 0.5 * volatility * volatility) * timeToExpiry) /
|
||||
(volatility * Math.sqrt(timeToExpiry));
|
||||
return 1 - normalCDF(d1);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue