You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
3.6 KiB
TypeScript

import { useSetAtom, atom as $ } from "jotai";
import { useMemo } from "react";
// from [https://stackoverflow.com/a/14873282]
function erf(x) {
// save the sign of x
var sign = (x >= 0) ? 1 : -1;
x = Math.abs(x);
// constants
var a1 = 0.254829592;
var a2 = -0.284496736;
var a3 = 1.421413741;
var a4 = -1.453152027;
var a5 = 1.061405429;
var p = 0.3275911;
// A&S formula 7.1.26
var t = 1.0/(1.0 + p*x);
var y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
return sign * y; // erf(-x) = -erf(x);
}
// produced by ChatGPT
export function calculateImpliedVolatility({optionPrice, underlyingPrice, strikePrice, timeToExpiration, riskFreeRate=0.03, optionType='call', maxIterations = 100, tolerance = 0.0001}) {
let iv = 0.5; // Initial guess for implied volatility
let epsilon = 1e-6; // Small value to avoid division by zero
for (let i = 0; i < maxIterations; i++) {
const optionPriceEstimate = calculateOptionPrice(underlyingPrice, strikePrice, timeToExpiration, iv, riskFreeRate, optionType);
const vega = calculateVega(underlyingPrice, strikePrice, timeToExpiration, iv, riskFreeRate);
const diff = optionPrice - optionPriceEstimate;
if (Math.abs(diff) < tolerance) {
return iv;
}
iv = iv + (diff / (vega || epsilon)); // Avoid division by zero
}
return NaN; // If max iterations are reached, return NaN (no convergence)
}
function calculateOptionPrice(S, K, T, impliedVolatility, r, optionType) {
const d1 = (Math.log(S / K) + (r + (impliedVolatility ** 2) / 2) * T) / (impliedVolatility * Math.sqrt(T));
const d2 = d1 - impliedVolatility * Math.sqrt(T);
if (optionType === 'call') {
return S * Math.exp(-r * T) * cumulativeDistributionFunction(d1) - K * Math.exp(-r * T) * cumulativeDistributionFunction(d2);
} else if (optionType === 'put') {
return K * Math.exp(-r * T) * cumulativeDistributionFunction(-d2) - S * Math.exp(-r * T) * cumulativeDistributionFunction(-d1);
} else {
throw new Error('Invalid option type. Use "call" or "put".');
}
}
function calculateVega(S, K, T, impliedVolatility, r) {
const d1 = (Math.log(S / K) + (r + (impliedVolatility ** 2) / 2) * T) / (impliedVolatility * Math.sqrt(T));
return S * Math.sqrt(T) * probabilityDensityFunction(d1);
}
function cumulativeDistributionFunction(x) {
return 0.5 * (1 + erf(x / Math.sqrt(2)));
}
function probabilityDensityFunction(x) {
return Math.exp(-0.5 * x ** 2) / Math.sqrt(2 * Math.PI);
}
// Example usage
/*
const optionPrice = 5.25; // Example option price
const underlyingPrice = 50; // Example underlying stock price
const strikePrice = 50; // Example strike price
const timeToExpiration = 0.25; // Example time to expiration (in years)
const riskFreeRate = 0.03; // Example risk-free interest rate
const optionType = 'call'; // Example option type ('call' or 'put')
const impliedVolatility = calculateImpliedVolatility({optionPrice, underlyingPrice, strikePrice, timeToExpiration, riskFreeRate, optionType});
console.log('Implied Volatility:', impliedVolatility);
*/
export function useLocalAtom(initialValue, deps){
return useMemo(()=>$(initialValue), deps);
}
/**
* Define a "command": a function that mutates state. It's passed `get` and `set` functions to access Jotai atoms, in addition to any other parameters.
* The function is memoized and is returned in the form of a Jotai "set" atom. It's called like any other function.
* @param fn The function to memoize
* @param deps Dependency array; when to re-memoize the function
* @returns
*/
export function useCommand(fn, deps){
return useSetAtom(useMemo(()=>$(null, fn),deps));
}