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)); }