/// <summary> /// Returns a random number from a Student's t distribution. /// </summary> public double NextStudentsT(double degreesOfFreedom) { double U = NextDouble(); double S = Distributions.StudentsTCumulativeDistributionFunctionInverse(U, degreesOfFreedom); return(S); }
/// <summary> /// Returns a random number from a normal distribution with the given mean and standard deviation. /// </summary> public double NextNormal(double mean, double standardDeviation) { double U = NextDouble(); double N = Distributions.NormalCumulativeDistributionFunctionInverse(U, mean, standardDeviation); return(N); }
/// <summary> /// Black, Scholes, Merton formula (1973) for European options with dividend. Vega = d[price]/d[vol]. /// </summary> /// <returns>option vega</returns> public static double BlackScholesVega(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { //Put and call values are the same. I've left putCallFlag in parameters so that all the Black Scholes formulas have the same argument list. double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double n1 = Distributions.StandardNormalProbabilityDensityFunction(d1); return(underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) * n1 * sqrtT); }
/// <summary> /// Black, Scholes, Merton formula (1973) for European options with dividend. Delta = d[price]/d[underlyingPrice]. /// </summary> /// <returns>option delta</returns> public static double BlackScholesDelta(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double N1 = Distributions.StandardNormalCumulativeDistributionFunction(d1); if (putCallFlag == PutCallFlag.Call) { return(Math.Exp(-dividendYield * yearsToExpiry) * N1); } return(Math.Exp(-dividendYield * yearsToExpiry) * (N1 - 1.0)); }
/// <summary> /// Assumes returns are normally distributed. /// <param name="returnArray">Historical returns from which VaR is to be calculated. The last value, with the highest index is assumed to be the most recent data point.</param> /// <param name="windowLength">Length of the VaR window. The number of historical returns that will be used to calculate the VaR.</param> /// <param name="confidenceLevel">VaR confidence level. 95% and 99% are typical values. If confidenceLevel is 95% then we expect 95% of days to be better (have a more positive return) than the VaR.</param> public static double NormalValueAtRisk(double[] returnArray, int windowLength, double confidenceLevel) { double[] da = new double[windowLength]; for (int i = 0; i < windowLength; i++) { da[i] = returnArray[returnArray.Length - windowLength + i]; } double mean = Moments.Mean(da); double standardDeviation = Moments.StandardDeviation(da); double var = Distributions.NormalCumulativeDistributionFunctionInverse(1 - confidenceLevel, mean, standardDeviation); return(-var); //By convention VaR is quoted as a positive value, even though it corresponds to a loss. }
/// <summary> /// Black, Scholes, Merton formula (1973) for European options with dividend. Rho = d[price]/d[riskFreeRate]. /// </summary> /// <returns>option rho</returns> public static double BlackScholesRho(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { double sqrtT = Math.Sqrt(yearsToExpiry); double d2 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield - 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); if (putCallFlag == PutCallFlag.Call) { double N2 = Distributions.StandardNormalCumulativeDistributionFunction(d2); return(yearsToExpiry * strike * Math.Exp(-riskFreeRate * yearsToExpiry) * N2); } double Nn2 = Distributions.StandardNormalCumulativeDistributionFunction(-d2); return(-yearsToExpiry *strike *Math.Exp(-riskFreeRate *yearsToExpiry) * Nn2); }
/// <summary> /// Black, Scholes, Merton formula (1973) for European options with dividend. Theta = -d[price]/d[yearsToExpiry]. /// </summary> /// <returns>option theta</returns> public static double BlackScholesTheta(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double d2 = d1 - vol * sqrtT; double a = -underlyingPrice *Math.Exp(-dividendYield *yearsToExpiry) * Distributions.StandardNormalProbabilityDensityFunction(d1) * vol / (2.0 * sqrtT); double b = dividendYield * underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry); if (putCallFlag == PutCallFlag.Call) { return(a + b * Distributions.StandardNormalCumulativeDistributionFunction(d1) - riskFreeRate * strike * Math.Exp(-riskFreeRate * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(d2)); } return(a - b * Distributions.StandardNormalCumulativeDistributionFunction(-d1) + riskFreeRate * strike * Math.Exp(-riskFreeRate * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(-d2)); }
private void CalculateParametersAndStatistic(bool calculateStatistics) { Matrix XprimeXinv = X.Transpose().Multiply(X).PseudoInverse(); Matrix betaValues = XprimeXinv.Multiply(X.Transpose()).Multiply(Y); int t = Y.NRows; int n = betaValues.NRows; if (calculateStatistics == false) { Betas = new ParameterEstimate[n]; for (int i = 0; i < n; i++) { Betas[i] = new ParameterEstimate(betaValues[i, 0]); } return; } Matrix expectedValueY = X.Multiply(betaValues); Matrix Z = expectedValueY.DeviationsFromMean(); ESS = Z.Transpose().Multiply(Z)[0, 0]; Residuals = expectedValueY.Subtract(Y); RSS = Residuals.Transpose().Multiply(Residuals)[0, 0]; TrackingError = Math.Sqrt(RSS / (t - 1)); rSquared = 1 - RSS / TSS; AdjustedRSquared = 1 - (1 - rSquared) * (t - 1) / (t - n); double varianceOfErrorTerm = Residuals.Transpose().Multiply(Residuals)[0, 0] / (t - n); Betas = new ParameterEstimate[n]; for (int i = 0; i < n; i++) { double sigma = Math.Sqrt(varianceOfErrorTerm * XprimeXinv[i, i]); double tStat = betaValues[i, 0] == 0 ? 0 : Math.Abs(betaValues[i, 0] - 0) / sigma; double pValue = 1.0 - Distributions.StudentsTCumulativeDensityFunction(tStat, t - n); Betas[i] = new ParameterEstimate(betaValues[i, 0], sigma, tStat, pValue); } fStatistic = (rSquared / (n - 1)) / ((1 - rSquared) / (t - n)); if (n > 1) { fValue = 1.0 - Distributions.FCumulativeDensityFunction(fStatistic, n - 1, t - n); } }
/// <summary> /// Used in Bjerksund and Stensland formula /// </summary> private static double BjerksundStenslandPhi(double s, double t, double gamma, double h, double i, double r, double b, double v) { double lambda = (-r + gamma * b + 0.5 * gamma * (gamma - 1) * v * v) * t; double d1a = Math.Log(s / h); double d1b = (b + (gamma - 0.5) * v * v) * t; double d1 = -(d1a + d1b); //-(Math.Log(s / h) + (b + (gamma - 0.5) * v * v) * t) ; double d2 = (v * Math.Sqrt(t)); double d = d1 / d2; //-(Math.Log(s / h) + (b + (gamma - 0.5) * v * v) * t) / (v * Math.Sqrt(t)); double kappa = 2 * b / (v * v) + 2 * gamma - 1; double n1 = Distributions.StandardNormalCumulativeDistributionFunction(d); double n2 = Distributions.StandardNormalCumulativeDistributionFunction(d - 2.0 * Math.Log(i / s) / (v * Math.Sqrt(t))); double p1 = Math.Exp(lambda); double p2 = Math.Pow(s, gamma); double p3 = n2 == 0 ? n1 : n1 - Math.Pow(i / s, kappa) * n2; double phi = p1 * p2 * p3; return(phi); }
/// <summary> /// Gereralized Black, Scholes, Merton formula (1973) for European options with dividend /// </summary> /// <returns>price of the option</returns> public static double BlackScholesPrice(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { //setting dividendYield = 0 gives the classic Black Scholes model //setting dividendYield = foreign risk-free rate gives a model for European currency options, see Garman and Kohlhagen (1983) double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double d2 = d1 - vol * sqrtT; if (putCallFlag == PutCallFlag.Call) { double N1 = Distributions.StandardNormalCumulativeDistributionFunction(d1); double N2 = Distributions.StandardNormalCumulativeDistributionFunction(d2); return(N1 * underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) - N2 * strike * Math.Exp(-riskFreeRate * yearsToExpiry)); } double Nn1 = Distributions.StandardNormalCumulativeDistributionFunction(-d1); double Nn2 = Distributions.StandardNormalCumulativeDistributionFunction(-d2); return(Nn2 * strike * Math.Exp(-riskFreeRate * yearsToExpiry) - Nn1 * underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry)); }
/// <summary> /// Used for BlackScholesImpliedVol /// </summary> /// <returns>price of option</returns> private static double BlackScholesPriceAndVega(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag, out double vega) { double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double d2 = d1 - vol * sqrtT; if (putCallFlag == PutCallFlag.Call) { double N1 = Distributions.StandardNormalCumulativeDistributionFunction(d1); double N2 = Distributions.StandardNormalCumulativeDistributionFunction(d2); double nn1 = Distributions.StandardNormalProbabilityDensityFunction(d1); vega = underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) * nn1 * sqrtT; return(N1 * underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) - N2 * strike * Math.Exp(-riskFreeRate * yearsToExpiry)); } double Nn1 = Distributions.StandardNormalCumulativeDistributionFunction(-d1); double Nn2 = Distributions.StandardNormalCumulativeDistributionFunction(-d2); double n1 = Distributions.StandardNormalProbabilityDensityFunction(d1); vega = underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) * n1 * sqrtT; return(Nn2 * strike * Math.Exp(-riskFreeRate * yearsToExpiry) - Nn1 * underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry)); }
/// <summary> /// Black, Scholes, Merton formula (1973) for European options with dividend. Convexity = second derivative of price with respect to interest rate. /// Not considered a standard Greek, but can be useful for portfolios with fixed income instruments and equity options. /// </summary> /// <returns>option interest rate convexity</returns> public static double BlackScholesRateConvexity(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { double sqrtT = Math.Sqrt(yearsToExpiry); double d2 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield - 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double N2 = Distributions.StandardNormalCumulativeDistributionFunction(d2); double n2 = Distributions.StandardNormalProbabilityDensityFunction(d2); double rho, convexity; if (putCallFlag == PutCallFlag.Call) { rho = yearsToExpiry * strike * Math.Exp(-riskFreeRate * yearsToExpiry) * N2; convexity = rho * ((n2 * sqrtT) / (N2 * vol) - yearsToExpiry); } else { rho = -yearsToExpiry *strike *Math.Exp(-riskFreeRate *yearsToExpiry) * (1 - N2); convexity = -rho * ((n2 * sqrtT) / ((1 - N2) * vol) + yearsToExpiry); } return(convexity); }
/// <summary> /// Black, Scholes, Merton formula (1973) for European option. /// Returns the price, and outputs the Greeks. /// Not as elegant looking, but faster than calculating each separately. /// </summary> public static double BlackScholesPriceAndGreeks(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag, out double delta, out double gamma, out double vega, out double theta, out double rho, out double convexity) { //setting dividendYield = 0 gives the classic Black Scholes model //setting dividendYield = foreign risk-free rate gives a model for European currency options, see Garman and Kohlhagen (1983) double sqrtT = Math.Sqrt(yearsToExpiry); double d1 = (Math.Log(underlyingPrice / strike) + (riskFreeRate - dividendYield + 0.5 * vol * vol) * yearsToExpiry) / (vol * sqrtT); double d2 = d1 - vol * sqrtT; double N1 = Distributions.StandardNormalCumulativeDistributionFunction(d1); double N2 = Distributions.StandardNormalCumulativeDistributionFunction(d2); double n1 = Distributions.StandardNormalProbabilityDensityFunction(d1); double n2 = Distributions.StandardNormalProbabilityDensityFunction(d2); double eNegRiskFreeRateTimesYearsToExpiry = Math.Exp(-riskFreeRate * yearsToExpiry); double eNegDivYieldYearsToExpiry = Math.Exp(-dividendYield * yearsToExpiry); double price; if (putCallFlag == PutCallFlag.Call) { price = N1 * underlyingPrice * eNegDivYieldYearsToExpiry - N2 * strike * eNegRiskFreeRateTimesYearsToExpiry; } else { price = (1 - N2) * strike * eNegRiskFreeRateTimesYearsToExpiry - (1 - N1) * underlyingPrice * eNegDivYieldYearsToExpiry; } if (putCallFlag == PutCallFlag.Call) { delta = eNegDivYieldYearsToExpiry * N1; } else { delta = eNegDivYieldYearsToExpiry * (N1 - 1.0); } gamma = eNegDivYieldYearsToExpiry * n1 / (underlyingPrice * vol * sqrtT); vega = underlyingPrice * Math.Exp(-dividendYield * yearsToExpiry) * n1 * sqrtT; if (putCallFlag == PutCallFlag.Call) { rho = yearsToExpiry * strike * eNegRiskFreeRateTimesYearsToExpiry * N2; } else { rho = -yearsToExpiry * strike * eNegRiskFreeRateTimesYearsToExpiry * (1 - N2); } double a = -underlyingPrice * eNegDivYieldYearsToExpiry * n1 * vol / (2.0 * sqrtT); double b = dividendYield * underlyingPrice * eNegDivYieldYearsToExpiry; if (putCallFlag == PutCallFlag.Call) { theta = a + b * N1 - riskFreeRate * strike * eNegRiskFreeRateTimesYearsToExpiry * N2; } else { theta = a - b * (1 - N1) + riskFreeRate * strike * eNegRiskFreeRateTimesYearsToExpiry * (1 - N2); } if (putCallFlag == PutCallFlag.Call) { convexity = rho * ((n2 * sqrtT) / (N2 * vol) - yearsToExpiry); } else { convexity = -rho * ((n2 * sqrtT) / ((1 - N2) * vol) + yearsToExpiry); } return(price); }
/// <summary> /// Barone Adesi and Whaley price approximation for an American option /// </summary> /// <returns>price of the option</returns> public static double BaroneAdesiWhaley(double strike, double underlyingPrice, double yearsToExpiry, double vol, double riskFreeRate, double dividendYield, PutCallFlag putCallFlag) { if (dividendYield <= 0 && putCallFlag == PutCallFlag.Call) { return(BlackScholesPrice(strike, underlyingPrice, yearsToExpiry, vol, riskFreeRate, dividendYield, putCallFlag)); } const double accuracy = 0.00001; const double maxIterations = 500; double volSqrd = vol * vol; double sqrtT = Math.Sqrt(yearsToExpiry); double m = 2.0 * riskFreeRate / volSqrd; double n = 2.0 * (riskFreeRate - dividendYield) / volSqrd; double k = 1.0 - Math.Exp(-riskFreeRate * yearsToExpiry); double a = Math.Sqrt(Math.Pow((n - 1), 2.0) + (4.0 * m / k)); double q; if (putCallFlag == PutCallFlag.Call) { q = 0.5 * (1 - n + a); } else { q = 0.5 * (1 - n - a); } //calculate seed value double undPriceSeed; if (putCallFlag == PutCallFlag.Call) { double b = -(n - 1.0) + Math.Sqrt(Math.Pow((n - 1), 2.0) + 4.0 * m); double undPriceStarInf = strike / (1.0 - 2.0 / b); double h = -((riskFreeRate - dividendYield) * yearsToExpiry + 2.0 * vol * sqrtT) * (strike / (undPriceStarInf - strike)); undPriceSeed = strike + (undPriceStarInf - strike) * (1.0 - Math.Exp(h)); } else { double b = -(n - 1.0) - Math.Sqrt(Math.Pow((n - 1), 2.0) + 4.0 * m); double undPriceStarInf = strike / (1.0 - 2.0 / b); double h = ((riskFreeRate - dividendYield) * yearsToExpiry - 2.0 * vol * sqrtT) * (strike / (strike - undPriceStarInf)); undPriceSeed = undPriceStarInf + (strike - undPriceStarInf) * Math.Exp(h); } //Newton-Raphson int nIterations = 0; double undPriceI = undPriceSeed; double g = 1.0; double gPrime = 1.0; while ((Math.Abs(g) > accuracy) && (Math.Abs(gPrime) > accuracy) && (nIterations++ < maxIterations) && (undPriceI > 0.0)) { double bsPrice = BlackScholesPrice(strike, undPriceI, yearsToExpiry, vol, riskFreeRate, dividendYield, putCallFlag); double d1 = (Math.Log(undPriceI / strike) + (riskFreeRate - dividendYield + 0.5 * volSqrd) * yearsToExpiry) / (vol * sqrtT); if (putCallFlag == PutCallFlag.Call) { g = (1.0 - 1.0 / q) * undPriceI - strike - bsPrice + (1.0 / q) * undPriceI * Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(d1); gPrime = (1.0 - 1.0 / q) * (1.0 - Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(d1)) + (1.0 / q) * Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalProbabilityDensityFunction(d1) * (1.0 / (vol * sqrtT)); } else { g = strike - undPriceI - bsPrice + (undPriceI / q) * (1.0 - Math.Exp(-dividendYield * yearsToExpiry)); gPrime = (1.0 / q - 1.0) * (1.0 - Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(-d1)) + (1.0 / q) * Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalProbabilityDensityFunction(-d1) * (1.0 / (vol * sqrtT)); } undPriceI = undPriceI - (g / gPrime); } double undPriceStar; if (Math.Abs(g) > accuracy) { //undPriceStar = undPriceSeed; //could continue with this value, currently throws exception throw new Exception("Newton-Raphson did not converge"); } undPriceStar = undPriceI; double price; double bsPrice2 = BlackScholesPrice(strike, underlyingPrice, yearsToExpiry, vol, riskFreeRate, dividendYield, putCallFlag); if (putCallFlag == PutCallFlag.Call && underlyingPrice >= undPriceStar) { price = underlyingPrice - strike; } else if (putCallFlag == PutCallFlag.Put && underlyingPrice <= undPriceStar) { price = strike - underlyingPrice; } else { double d1 = (Math.Log(undPriceStar / strike) + (riskFreeRate - dividendYield + 0.5 * volSqrd) * yearsToExpiry) / (vol * sqrtT); if (putCallFlag == PutCallFlag.Put) { d1 *= -1.0; } double A = (1.0 - Math.Exp(-dividendYield * yearsToExpiry) * Distributions.StandardNormalCumulativeDistributionFunction(d1)) * (undPriceStar / q); if (putCallFlag == PutCallFlag.Put) { A *= -1.0; } price = bsPrice2 + A * Math.Pow((underlyingPrice / undPriceStar), q); } return(Math.Max(price, bsPrice2)); // know value will never be less than BS value }