public static double inv_g(double beta, double y) { if (beta == 0.0 || y >= 1.0) { return(Math.Log(y)); } var expo = -(beta * beta - 2.0 * beta + y) / (beta * (beta - 1.0)); if (expo > 700.0) { //! This happens only for small beta //! Need solution here - default to newton iteration? //! For the time being we use an expansion var l1 = expo + Math.Log((1 - beta) / beta); // no risk here, beta is small var l12 = l1 * l1; var l13 = l12 * l1; var l14 = l12 * l12; var l15 = l14 * l12; var l2 = Math.Log(l1); var l22 = l2 * l2; var l23 = l22 * l2; var l24 = l23 * l2; var wwb = l1 - l2 + l2 / l1 + l2 * (-2.0 + l2) / (2.0 * l12) + l2 * (6.0 - 9.0 * l2 + 2.0 * l22) / (6.0 * l13) + l2 * (-12.0 + 36.0 * l2 - 22.0 * l22 + 3.0 * l23) / (12.0 * l14) + l2 * (60.0 - 300.0 * l2 + 350.0 * l22 - 125.0 * l23 + 12.0 * l24) / (60.0 * l15); return(beta * wwb - wwb + beta - 2.0 + y / beta); } var aw = Math.Exp(expo) * (1.0 - beta) / beta; var wb = SpecialFunction.wapr(aw); return(beta * wb - wb + beta - 2.0 + y / beta); }
public static double beta_black_pdf(double beta, double mu, double sigma, double x) { var gm1 = inv_g(beta, x); var den = dg(beta, gm1); return(SpecialFunction.pdf_normal((gm1 - mu) / sigma) / den / sigma); }
public static double inv_m1_approx_smart(double beta, double f, double sigma) { // Compute some relevant bounds var log_001 = -4.605170186; var lo = log_001 * (1 - beta) - 0.5 * sigma * sigma; var hi = Math.Min(37 - 0.5 * sigma * sigma, 0.5 * sigma * sigma); //! < 10 // Evaluate the function on those points // Using the actual function to too costly var zz = 0.0; var vlo = m1_beta_black(beta, lo, sigma); var dhi_dmu = 0.0; var vhi = m1_beta_black_b(beta, hi, sigma, ref zz, ref dhi_dmu, ref zz, 1.0); // Treat bounds if (f < vlo) { return(inv_g(beta, f)); // no convexity } if (f > vhi) { return(inv_g(beta, f) - 0.5 * sigma * sigma); // full convexity } //! Middle region, fit approximately // a + b x + c exp(d x) var d = dhi_dmu / vhi; var c = vhi / Math.Exp(dhi_dmu * hi / vhi); var b = -(vlo - c * Math.Exp(d * lo)) / (hi - lo); var a = -b * hi; //! The invert in closed form var aw = Math.Exp(-d * (a - f) / b) * c * d / b; return(-(b * SpecialFunction.wapr(aw) + a * d - d * f) / (b * d)); }
public static double ibachelier(OptionType optType, double F, double K, double T, double premium, bool safe = false) { if (T <= 0.0) { throw FS.FailWith($"T must be postive, got {T}"); } var epsilon = optType.ToEpsilon(); var intrinsic = Math.Max(epsilon * (F - K), 0.0); if (premium <= intrinsic) { if (DoubleUtils.ApproximatelyEqual(premium, intrinsic, DoubleUtils.DoubleEpsilon)) { return(0.0); } if (safe) { return(0.0); } throw FS.FailWith($"premium must be larger than intrinsic. Got premium = {premium}, vs intrinsic = {intrinsic}"); //! } if (F == K) { return(premium * Math.Sqrt(2.0 * Math.PI / T)); } var sqrtT = Math.Sqrt(T); var phiStar = -Math.Abs((premium - intrinsic) / (K - F)); var xBar = 0.0; if (phiStar < -0.001882039271) { var g = 1.0 / (phiStar - 0.5); var g2 = g * g; var zeta = (0.032114372355 - g2 * (0.016969777977 - g2 * (2.6207332461e-3 - 9.6066952861e-5 * g2))) / (1.0 - g2 * (0.6635646938 - g2 * (0.14528712196 - 0.010472855461 * g2))); xBar = g * (1.0 / Math.Sqrt(2.0 * Math.PI) + zeta * g2); } else { var h = Math.Sqrt(-Math.Log(-phiStar)); xBar = (9.4883409779 - h * (9.6320903635 - h * (0.58556997323 + 2.1464093351 * h))) / (1 - h * (0.65174820867 + h * (1.5120247828 + 6.6437847132e-5 * h))); } var xBar2 = xBar * xBar; var nxBar = SpecialFunction.pdf_normal(xBar); var phiTildaXBar = SpecialFunction.cdf_normal(xBar) + nxBar / xBar; var q = (phiTildaXBar - phiStar) / nxBar; var xStar = xBar + (3.0 * q * xBar2 * (2.0 - q * xBar * (2.0 + xBar2))) / (6.0 + q * xBar * (-12.0 + xBar * (6.0 * q + xBar * (-6.0 + q * xBar * (3.0 + xBar2))))); return(Math.Abs((K - F) / (xStar * sqrtT))); }
public static double beta_black_quick_delta_to_strike(double beta, double F, double T, double sigma, double qd) { if (qd < -1.0 || qd > 1.0) { throw FS.FailWith($"delta must be in (-1, 1), got {qd}"); } var qdUsed = qd < 0.0 ? 1.0 + qd : qd; return(g(beta, inv_g(beta, F) - SpecialFunction.cdfinv_normal(qdUsed) * sigma * Math.Sqrt(T))); }
public static double beta_black_delta_to_strike(double beta, double F, double T, double sigma, double delta) { if (delta < -1.0 || delta > 1.0) { throw FS.FailWith($"delta must be in (-1, 1), got {delta}"); } var mu = inv_m1_beta_black(beta, F, sigma * Math.Sqrt(T)); var usedDelta = delta < 0.0 ? -delta : 1.0 - delta; return(g(beta, SpecialFunction.cdfinv_normal(usedDelta) * sigma * Math.Sqrt(T) + mu)); }
public static double beta_black_strike_to_quick_delta(double beta, double F, double T, double sigma, double strike) { var qd = SpecialFunction.cdf_normal((inv_g(beta, F) - inv_g(beta, strike)) / (sigma * Math.Sqrt(T))); if (qd <= -0.5) { return(1.0 + qd); } if (qd > 0.5) { return(qd - 1.0); } return(qd); }
public static double inv_m1_beta_black_approx_2(double beta, double f, double sigma) { //! Numerical var expArg = -1.0 * (SpecialFunction.pow2(beta) - 2.0 * beta + f) / ((-1.0 + beta) * beta); if (expArg > 37.0) { return(inv_g(beta, f)); } var aw = -0.5000000000 * (2.0 * SpecialFunction.pow2(beta) + SpecialFunction.pow2(sigma) - 4.0 * beta + 2.0) * Math.Exp(expArg) / ((-1.0 + beta) * beta); var wb = SpecialFunction.wapr(aw); return(beta * wb - wb + beta - 2.0 + f / beta); }
public static double black76(OptionType optType, double F, double K, double sdev) { var num = optType.ToEpsilon(); if (sdev < 0.0) { throw FS.FailWith($"sdev must be positive. Got {sdev}"); } if (sdev == 0.0 || K <= 0.0) { return(Math.Max(num * (F - K), 0.0)); } else { var num2 = (Math.Log(F / K) + 0.5 * sdev * sdev) / sdev; var num3 = num2 - sdev; return(num * (F * SpecialFunction.cdf_normal(num * num2) - K * SpecialFunction.cdf_normal(num * num3))); } }
public static double m1_beta_black(double beta, double mu, double sigma) { var betaMinusOne = beta - 1.0; var betaMinusOneSq = betaMinusOne * betaMinusOne; var sigma2 = sigma * sigma; var sqrt2 = Math.Sqrt(2.0); var sqrtPi = Math.Sqrt(Math.PI); var t = -mu / sigma; var t2 = t * t; var one_over_two_betaMinusOneSq = 0.5 / betaMinusOneSq; var e4 = Math.Exp(-0.5 * t2); var a1 = t + sigma / betaMinusOne; var group1 = -SpecialFunction.exp_x_N_y(-beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq, a1); var a2 = sigma - t; var group2 = SpecialFunction.exp_x_N_y(mu + 0.5 * sigma2, a2); var f0 = -1.0 / (2.0 * sqrtPi); return(f0 * sqrt2 * e4 * beta * sigma - betaMinusOneSq * group1 + group2 - beta * (mu - beta + 2) * (SpecialFunction.cdf_normal(-t) - 1.0)); }
public static double beta_black_trunc(double beta, double mu, double sigma, double x) { var betaMinusOne = beta - 1.0; var betaMinusOneSq = betaMinusOne * betaMinusOne; var sigma2 = sigma * sigma; var sqrt2 = Math.Sqrt(2.0); var sqrtPi = Math.Sqrt(Math.PI); var t = -mu / sigma; var one_over_two_betaMinusOneSq = 0.5 / betaMinusOneSq; var f0 = -1.0 / (2.0 * sqrtPi); if (x < t) { var x2 = x * x; var e2 = Math.Exp(-0.5 * x2); var a4 = x + sigma / betaMinusOne; var group4 = SpecialFunction.exp_x_N_y(-beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq, a4); return(f0 * sqrt2 * e2 * beta * sigma + +betaMinusOneSq * group4 + beta * SpecialFunction.cdf_normal(x) * (mu - beta + 2.0)); } else { var t2 = t * t; var e4 = Math.Exp(-0.5 * t2); var a1 = t + sigma / betaMinusOne; var group1 = -SpecialFunction.exp_x_N_y(-beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq, a1); var a2 = sigma - t; var a3 = x - sigma; var group2 = -SpecialFunction.exp_x_N_y(mu + 0.5 * sigma2, -a2); var group3 = SpecialFunction.exp_x_N_y(mu + 0.5 * sigma2, a3); return(f0 * sqrt2 * e4 * beta * sigma - betaMinusOneSq * group1 + (group2 + group3) - beta * (mu - beta + 2) * (SpecialFunction.cdf_normal(-t) - 1.0)); } }
public static double beta_black(double beta, OptionType optType, double F, double K, double T, double sigma) { if (T < 0.0) { throw FS.FailWith($"T < 0 not allowed, {T}"); } if (sigma < 0.0) { throw FS.FailWith($"sigma < 0 not allowed {sigma}"); } var stdev = sigma * Math.Sqrt(T); if (stdev == 0.0) { return(Math.Max(optType.ToEpsilon() * (F - K), 0.0)); } var mu = inv_m1_beta_black(beta, F, stdev); var d = (inv_g(beta, K) - mu) / stdev; var t1 = K * SpecialFunction.cdf_normal(d); var t2 = beta_black_trunc(beta, mu, stdev, d); var put = t1 - t2; var intrinsic = Math.Max(optType.ToEpsilon() * (F - K), 0.0); if (optType == OptionType.Put) { return(Math.Max(put, intrinsic)); } else if (optType == OptionType.Call) { return(Math.Max(put + F - K, intrinsic)); } else { throw FS.E_CASE(optType); } }
public static double m1_beta_black_b(double beta, double mu, double sigma, ref double beta_b, ref double mu_b, ref double sigma_b, double res_b) { var betaMinusOne = beta - 1.0; var betaMinusOneSq = betaMinusOne * betaMinusOne; var sigma2 = sigma * sigma; var sqrt2 = Math.Sqrt(2.0); var sqrtPi = Math.Sqrt(Math.PI); var t = -mu / sigma; var t2 = t * t; var one_over_two_betaMinusOneSq = 0.5 / betaMinusOneSq; var e4 = Math.Exp(-0.5 * t2); var a1 = t + sigma / betaMinusOne; var b1 = -beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq; var dmgroup1_db1 = 0.0; var dmgroup1_da1 = 0.0; var group1 = -SpecialFunction.exp_x_N_y_b(b1, a1, ref dmgroup1_db1, ref dmgroup1_da1, 1.0); var a2 = sigma - t; var muOneHalfS2 = mu + 0.5 * sigma2; var dgroup2_dmuOneHalfS2 = 0.0; var dgroup2_da2 = 0.0; var group2 = SpecialFunction.exp_x_N_y_b(muOneHalfS2, a2, ref dgroup2_dmuOneHalfS2, ref dgroup2_da2, 1.0); var f0 = -1.0 / (2.0 * sqrtPi); var dnmt_dmt = 0.0; var nmt = SpecialFunction.cdf_normal_b(-t, ref dnmt_dmt, 1.0); var res = f0 * sqrt2 * e4 * beta * sigma - betaMinusOneSq * group1 + group2 - beta * (mu - beta + 2) * (nmt - 1.0); var e4_b = res_b * f0 * sqrt2 * beta * sigma; beta_b += res_b * f0 * sqrt2 * e4 * sigma; beta_b += -res_b * (mu - 2.0 * beta + 2.0) * (nmt - 1.0); sigma_b += res_b * f0 * sqrt2 * e4 * beta; var betaMinusOneSq_b = -res_b * group1; var group1_b = -res_b * betaMinusOneSq; var group2_b = res_b; mu_b += -res_b * beta * (nmt - 1.0); var nmt_b = -res_b * beta * (mu - beta + 2); var t_b = -nmt_b * dnmt_dmt; var a2_b = group2_b * dgroup2_da2; var muOneHalfS2_b = group2_b * dgroup2_dmuOneHalfS2; mu_b += muOneHalfS2_b; sigma_b += muOneHalfS2_b * sigma; sigma_b += a2_b; t_b += -a2_b; var b1_b = -group1_b * dmgroup1_db1; var a1_b = -group1_b * dmgroup1_da1; beta_b += -b1_b * mu / betaMinusOneSq; mu_b += b1_b * (-beta / betaMinusOneSq + 2.0 * one_over_two_betaMinusOneSq); sigma_b += b1_b * 2.0 * sigma * one_over_two_betaMinusOneSq; betaMinusOneSq_b += b1_b * beta * mu / betaMinusOneSq / betaMinusOneSq; var one_over_two_betaMinusOneSq_b = b1_b * (sigma2 + 2.0 * mu); t_b += a1_b; var betaMinusOne_b = -a1_b * sigma / betaMinusOne / betaMinusOne; sigma_b += a1_b / betaMinusOne; var t2_b = -0.5 * e4_b * e4; betaMinusOneSq_b += -one_over_two_betaMinusOneSq_b * 0.5 / betaMinusOneSq / betaMinusOneSq; t_b += t2_b * 2.0 * t; mu_b += -t_b / sigma; sigma_b += t_b * mu / sigma / sigma; betaMinusOne_b += betaMinusOneSq_b * 2.0 * betaMinusOne; beta_b += betaMinusOne_b; return(res); }
public static double beta_black_cdf(double beta, double mu, double sigma, double x) { return(SpecialFunction.cdf_normal((inv_g(beta, x) - mu) / sigma)); }
public static double beta_black_cdfinv(double beta, double mu, double sigma, double p) { return(g(beta, SpecialFunction.cdfinv_normal(p) * sigma + mu)); }
public static double beta_black_trunc_b(double beta, double mu, double sigma, double x, ref double beta_b, ref double mu_b, ref double sigma_b, ref double x_b, double res_b) { var betaMinusOne = beta - 1.0; var betaMinusOneSq = betaMinusOne * betaMinusOne; var sigma2 = sigma * sigma; var sqrt2 = Math.Sqrt(2.0); var sqrtPi = Math.Sqrt(Math.PI); var t = -mu / sigma; var one_over_two_betaMinusOneSq = 0.5 / betaMinusOneSq; var f0 = -1.0 / (2.0 * sqrtPi); var res = 0.0; var betaMinusOne_b = 0.0; var betaMinusOneSq_b = 0.0; var one_over_two_betaMinusOneSq_b = 0.0; var x2_b = 0.0; var t_b = 0.0; if (x < t) { var x2 = x * x; var ar2 = -0.5 * x2; var e2 = Math.Exp(ar2); var a4 = x + sigma / betaMinusOne; var ar4 = -beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq; var dgroup4_dar4 = 0.0; var dgroup4_da4 = 0.0; var group4 = SpecialFunction.exp_x_N_y_b(ar4, a4, ref dgroup4_dar4, ref dgroup4_da4, 1.0); var dNx_dx = 0.0; var Nx = SpecialFunction.cdf_normal_b(x, ref dNx_dx, 1.0); res = f0 * sqrt2 * e2 * beta * sigma + betaMinusOneSq * group4 + beta * Nx * (mu - beta + 2.0); var e2_b = res_b * f0 * sqrt2 * beta * sigma; beta_b += res_b * (f0 * sqrt2 * e2 * sigma + Nx * (mu - 2.0 * beta + 2.0)); sigma_b += res_b * f0 * sqrt2 * e2 * beta; betaMinusOneSq_b += res_b * group4; var group4_b = res_b * betaMinusOneSq; var Nx_b = res_b * beta * (mu - beta + 2.0); mu_b += res_b * beta * Nx; x_b += Nx_b * dNx_dx; var ar4_b = group4_b * dgroup4_dar4; var a4_b = group4_b * dgroup4_da4; beta_b += ar4_b * (-mu / betaMinusOneSq); mu_b += ar4_b * (-beta / betaMinusOneSq + 2.0 * one_over_two_betaMinusOneSq); betaMinusOneSq_b += ar4_b * (beta * mu / betaMinusOneSq / betaMinusOneSq); sigma_b += ar4_b * 2.0 * sigma * one_over_two_betaMinusOneSq; one_over_two_betaMinusOneSq_b += ar4_b * (sigma2 + 2.0 * mu); x_b += a4_b; sigma_b += a4_b / betaMinusOne; betaMinusOne_b += a4_b * (-sigma / betaMinusOne / betaMinusOne); var ar2_b = e2_b * e2; x2_b += -0.5 * ar2_b; x_b += x2_b * 2.0 * x; } else { var t2 = t * t; var ar4 = -0.5 * t2; var e4 = Math.Exp(ar4); var a1 = t + sigma / betaMinusOne; var ar1 = -beta * mu / betaMinusOneSq + (sigma2 + 2.0 * mu) * one_over_two_betaMinusOneSq; var dmgroup1_dar1 = 0.0; var dmgroup1_da1 = 0.0; var group1 = -SpecialFunction.exp_x_N_y_b(ar1, a1, ref dmgroup1_dar1, ref dmgroup1_da1, 1.0); var a2 = t - sigma; var a3 = x - sigma; var ar2 = mu + 0.5 * sigma2; var dmgroup2_dar2 = 0.0; var dmgroup2_da2 = 0.0; var group2 = -SpecialFunction.exp_x_N_y_b(ar2, a2, ref dmgroup2_dar2, ref dmgroup2_da2, 1.0); var dgroup3_dar2 = 0.0; var dgroup3_da3 = 0.0; var group3 = SpecialFunction.exp_x_N_y_b(ar2, a3, ref dgroup3_dar2, ref dgroup3_da3, 1.0); var dnmt_dmt = 0.0; var nmt = SpecialFunction.cdf_normal_b(-t, ref dnmt_dmt, 1.0); res = f0 * sqrt2 * e4 * beta * sigma - betaMinusOneSq * group1 + group2 + group3 - beta * (mu - beta + 2) * (nmt - 1.0); var e4_b = res_b * f0 * sqrt2 * beta * sigma; beta_b += res_b * (f0 * sqrt2 * e4 * sigma - (mu - 2.0 * beta + 2.0) * (nmt - 1.0)); sigma_b += res_b * f0 * sqrt2 * e4 * beta; betaMinusOneSq_b += -res_b * group1; var group1_b = -res_b * betaMinusOneSq; var group2_b = res_b; var group3_b = res_b; mu_b += -res_b * beta * (nmt - 1.0); var nmt_b = res_b * -beta * (mu - beta + 2); t_b += -nmt_b * dnmt_dmt; var ar2_b = group3_b * dgroup3_dar2; var a3_b = group3_b * dgroup3_da3; var a2_b = -group2_b * dmgroup2_da2; ar2_b += -group2_b * dmgroup2_dar2; mu_b += ar2_b; sigma_b += ar2_b * sigma; x_b += a3_b; sigma_b += -a3_b; sigma_b += -a2_b; t_b += a2_b; var ar1_b = -group1_b * dmgroup1_dar1; var a1_b = -group1_b * dmgroup1_da1; beta_b += ar1_b * (-mu / betaMinusOneSq); mu_b += ar1_b * (-beta / betaMinusOneSq + 2.0 * one_over_two_betaMinusOneSq); betaMinusOneSq_b += ar1_b * (beta * mu / betaMinusOneSq / betaMinusOneSq); sigma_b += ar1_b * 2.0 * sigma * one_over_two_betaMinusOneSq; one_over_two_betaMinusOneSq_b += ar1_b * (sigma2 + 2.0 * mu); t_b += a1_b; sigma_b += a1_b / betaMinusOne; betaMinusOne_b += -a1_b * sigma / betaMinusOne / betaMinusOne; var ar4_b = e4_b * e4; var t2_b = -0.5 * ar4_b; t_b += t2_b * 2.0 * t; } betaMinusOneSq_b += -one_over_two_betaMinusOneSq_b * 0.5 / betaMinusOneSq / betaMinusOneSq; mu_b += -t_b / sigma; sigma_b += t_b * mu / sigma / sigma; betaMinusOne_b += betaMinusOneSq_b * 2.0 * betaMinusOne; beta_b += betaMinusOne_b; return(res); }
public static double beta_black_b(double beta, OptionType optType, double F, double K, double T, double sigma , ref double beta_b, ref double F_b, ref double K_b, ref double T_b, ref double sigma_b, double res_b) { if (T < 0.0) { throw FS.FailWith($"T < 0 not allowed, {T}"); } if (sigma < 0.0) { throw FS.FailWith($"sigma < 0 not allowed {sigma}"); } var sqrtT = Math.Sqrt(T); var stdev = sigma * sqrtT; if (stdev == 0.0) { return(Math.Max(optType.ToEpsilon() * (F - K), 0.0)); } var dmu_dbeta = 0.0; var dmu_dF = 0.0; var dmu_dstdev = 0.0; var mu = inv_m1_beta_black_b(beta, F, stdev, ref dmu_dbeta, ref dmu_dF, ref dmu_dstdev, 1.0); var digb_dbeta = 0.0; var digb_dK = 0.0; var igb = inv_g_b(beta, K, ref digb_dbeta, ref digb_dK, 1.0); var d = (igb - mu) / stdev; var dNd_dd = 0.0; var Nd = SpecialFunction.cdf_normal_b(d, ref dNd_dd, 1.0); var t1 = K * Nd; var dt2_dbeta = 0.0; var dt2_dmu = 0.0; var dt2_dstdev = 0.0; var dt2_dd = 0.0; var t2 = beta_black_trunc_b(beta, mu, stdev, d, ref dt2_dbeta, ref dt2_dmu, ref dt2_dstdev, ref dt2_dd, 1.0); var res = t1 - t2; // now rewind var t1_b = res_b; var t2_b = -res_b; beta_b += t2_b * dt2_dbeta; var mu_b = t2_b * dt2_dmu; var stdev_b = t2_b * dt2_dstdev; var d_b = t2_b * dt2_dd; K_b += t1_b * Nd; var Nd_b = t1_b * K; d_b += Nd_b * dNd_dd; var igb_b = d_b / stdev; mu_b += -d_b / stdev; stdev_b += -d_b * (igb - mu) / stdev / stdev; beta_b += igb_b * digb_dbeta; K_b += igb_b * digb_dK; beta_b += mu_b * dmu_dbeta; F_b += mu_b * dmu_dF; stdev_b += mu_b * dmu_dstdev; sigma_b += stdev_b * sqrtT; T_b += stdev_b * sigma / (2.0 * sqrtT); if (optType == OptionType.Put) { return(res); } else if (optType == OptionType.Call) { F_b += res_b; K_b += -res_b; return(res + F - K); } else { throw FS.E_CASE(optType); } }