public static double beta_black_normal_vol(double beta, double F, double K, double T, double sigma) { var optType = OptionType.Put; // F > K ? OptionType.Put : OptionType.Call; var bbPrice = beta_black(beta, optType, F, K, T, sigma); try { return(ClosedForm.ibachelier(optType, F, K, T, bbPrice)); } catch (Exception e) { FS.WithInfo(e, $"beta ={beta}, optType = {optType}, F = {F}, K = {K}, T = {T}, sigma = {sigma}, bbPrice = {bbPrice}"); throw; } }
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 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); } }
private static double lambert_w_boost(double z) { if (z < 6.0) { throw FS.FailWith("require(z > 6)"); } else if (z < 18) { // 6 < z < 18 // Max error in interpolated form: 1.985e-19 const double offset = 1.80937194824218750e+00; double[] P = { -1.80690935424793635e+00, -3.66995929380314602e+00, -1.93842957940149781e+00, -2.94269984375794040e-01, 1.81224710627677778e-03, 2.48166798603547447e-03, 1.15806592415397245e-04, 1.43105573216815533e-06, 3.47281483428369604e-09 }; double[] Q = { 1.00000000000000000e+00, 2.57319080723908597e+00, 1.96724528442680658e+00, 5.84501352882650722e-01, 7.37152837939206240e-02, 3.97368430940416778e-03, 8.54941838187085088e-05, 6.05713225608426678e-07, 8.17517283816615732e-10 }; return(offset + boost_math_tools_evaluate_rational(P, Q, z)); } else if (z < 9897.12905874) // 2.8 < log(z) < 9.2 { // Max error in interpolated form: 1.195e-18 double Y = -1.40297317504882812e+00; double[] P = { 1.97011826279311924e+00, 1.05639945701546704e+00, 3.33434529073196304e-01, 3.34619153200386816e-02, -5.36238353781326675e-03, -2.43901294871308604e-03, -2.13762095619085404e-04, -4.85531936495542274e-06, -2.02473518491905386e-08, }; double[] Q = { 1.00000000000000000e+00, 8.60107275833921618e-01, 4.10420467985504373e-01, 1.18444884081994841e-01, 2.16966505556021046e-02, 2.24529766630769097e-03, 9.82045090226437614e-05, 1.36363515125489502e-06, 3.44200749053237945e-09, }; var log_w = Math.Log(z); return(log_w + Y + boost_math_tools_evaluate_rational(P, Q, log_w)); } else if (z < 7.896296e+13) // 9.2 < log(z) <= 32 { // Max error in interpolated form: 6.529e-18 double Y = -2.73572921752929688e+00; double[] P = { 3.30547638424076217e+00, 1.64050071277550167e+00, 4.57149576470736039e-01, 4.03821227745424840e-02, -4.99664976882514362e-04, -1.28527893803052956e-04, -2.95470325373338738e-06, -1.76662025550202762e-08, -1.98721972463709290e-11, }; double[] Q = { 1.00000000000000000e+00, 6.91472559412458759e-01, 2.48154578891676774e-01, 4.60893578284335263e-02, 3.60207838982301946e-03, 1.13001153242430471e-04, 1.33690948263488455e-06, 4.97253225968548872e-09, 3.39460723731970550e-12, }; var log_w = Math.Log(z); return(log_w + Y + boost_math_tools_evaluate_rational(P, Q, log_w)); } else if (z < 2.6881171e+43) // 32 < log(z) < 100 { // Max error in interpolated form: 2.015e-18 double Y = -4.01286315917968750e+00; double[] P = { 5.07714858354309672e+00, -3.32994414518701458e+00, -8.61170416909864451e-01, -4.01139705309486142e-02, -1.85374201771834585e-04, 1.08824145844270666e-05, 1.17216905810452396e-07, 2.97998248101385990e-10, 1.42294856434176682e-13, }; double[] Q = { 1.00000000000000000e+00, -4.85840770639861485e-01, -3.18714850604827580e-01, -3.20966129264610534e-02, -1.06276178044267895e-03, -1.33597828642644955e-05, -6.27900905346219472e-08, -9.35271498075378319e-11, -2.60648331090076845e-14, }; var log_w = Math.Log(z); return(log_w + Y + boost_math_tools_evaluate_rational(P, Q, log_w)); } else // 100 < log(z) < 710 { // Max error in interpolated form: 5.277e-18 double Y = -5.70115661621093750e+00; double[] P = { 6.42275660145116698e+00, 1.33047964073367945e+00, 6.72008923401652816e-02, 1.16444069958125895e-03, 7.06966760237470501e-06, 5.48974896149039165e-09, -7.00379652018853621e-11, -1.89247635913659556e-13, -1.55898770790170598e-16, -4.06109208815303157e-20, -2.21552699006496737e-24, }; double[] Q = { 1.00000000000000000e+00, 3.34498588416632854e-01, 2.51519862456384983e-02, 6.81223810622416254e-04, 7.94450897106903537e-06, 4.30675039872881342e-08, 1.10667669458467617e-10, 1.31012240694192289e-13, 6.53282047177727125e-17, 1.11775518708172009e-20, 3.78250395617836059e-25, }; var log_w = Math.Log(z); return(log_w + Y + boost_math_tools_evaluate_rational(P, Q, log_w)); } }
public static double cdfinv_normal(double p) /******************************************************************************/ /* * Purpose: * * R8_NORMAL_01_CDF_INVERSE inverts the standard normal CDF. * * Discussion: * * The result is accurate to about 1 part in 10^16. * * Licensing: * * This code is distributed under the GNU LGPL license. * * Modified: * * 19 March 2010 * * Author: * * Original FORTRAN77 version by Michael Wichura. * C version by John Burkardt. * * Reference: * * Michael Wichura, * The Percentage Points of the Normal Distribution, * Algorithm AS 241, * Applied Statistics, * Volume 37, Number 3, pages 477-484, 1988. * * Parameters: * * Input, double P, the value of the cumulative probability * densitity function. 0 < P < 1. If P is outside this range, an "infinite" * value is returned. * * Output, double R8_NORMAL_01_CDF_INVERSE, the normal deviate value * with the property that the probability of a standard normal deviate being * less than or equal to this value is P. */ { var const1 = 0.180625; var const2 = 1.6; double q; double r; var split1 = 0.425; var split2 = 5.0; double value; if (p <= 0.0) { if (p < 0) { throw FS.FailWith($"p is lower than 0, with {p}"); } value = -R8_HUGE;//r8_huge ( ); return(value); } if (1.0 <= p) { if (p > 1) { throw FS.FailWith($"p is higher than 1 with {p}"); } value = R8_HUGE; // r8_huge ( ); return(value); } q = p - 0.5; //if ( r8_abs ( q ) <= split1 ) if (Math.Abs(q) <= split1) { r = const1 - q * q; value = q * (((((((a[7] * r + a[6]) * r + a[5]) * r + a[4]) * r + a[3]) * r + a[2]) * r + a[1]) * r + a[0]) / (((((((b[7] * r + b[6]) * r + b[5]) * r + b[4]) * r + b[3]) * r + b[2]) * r + b[1]) * r + b[0]); } else { if (q < 0.0) { r = p; } else { r = 1.0 - p; } if (r <= 0.0) { value = -1.0; throw FS.FailWith($"Error, cdfinv with p = {p}"); //exit ( 1 );// exception?? } r = Math.Sqrt(-Math.Log(r)); if (r <= split2) { r = r - const2; value = (((((((c[7] * r + c[6]) * r + c[5]) * r + c[4]) * r + c[3]) * r + c[2]) * r + c[1]) * r + c[0]) / (((((((d[7] * r + d[6]) * r + d[5]) * r + d[4]) * r + d[3]) * r + d[2]) * r + d[1]) * r + d[0]); } else { r = r - split2; value = (((((((e[7] * r + e[6]) * r + e[5]) * r + e[4]) * r + e[3]) * r + e[2]) * r + e[1]) * r + e[0]) / (((((((f[7] * r + f[6]) * r + f[5]) * r + f[4]) * r + f[3]) * r + f[2]) * r + f[1]) * r + f[0]); } if (q < 0.0) { value = -value; } } return(value); }
public BetaBlackPack(double beta, OptionType callPut, double f, double k, double t, double sigma) { if (beta < 0 || beta >= 1.0) { throw FS.FailWith($"Beta must be in [0, 1.0), got {beta}"); } if (sigma <= 0.0) { throw FS.FailWith($"Sigma must be positive, got {sigma}"); } Beta = beta; CallPut = callPut; F = f; K = k; T = t; Sigma = sigma; //! Nota bene: Use AD for first order derivatives and FD on AD for second order // // Mu = BetaBlack.inv_m1_beta_black(beta, f, sigma * Math.Sqrt(t)); double beta_b = 0.0; double f_b = 0.0; double k_b = 0.0; double t_b = 0.0; double sigma_b = 0.0; Price = BetaBlack.beta_black_b(beta, callPut, f, k, t, sigma, ref beta_b, ref f_b, ref k_b, ref t_b, ref sigma_b, 1.0); dPV_dS = f_b; dPV_dK = k_b; dPV_dT = t_b; dPV_dVol = sigma_b; dPV_dBeta = beta_b; var eps = Math.Sqrt(DoubleUtils.DoubleEpsilon); { var dF = eps * (1.0 + Math.Abs(f)); double betaUp_b = 0.0; double fUp_b = 0.0; double kUp_b = 0.0; double tUp_b = 0.0; double sigmaUp_b = 0.0; double pUp = BetaBlack.beta_black_b(beta, callPut, f + dF, k, t, sigma, ref betaUp_b, ref fUp_b, ref kUp_b, ref tUp_b, ref sigmaUp_b, 1.0); double betaDown_b = 0.0; double fDown_b = 0.0; double kDown_b = 0.0; double tDown_b = 0.0; double sigmaDown_b = 0.0; double pDown = BetaBlack.beta_black_b(beta, callPut, f - dF, k, t, sigma, ref betaDown_b, ref fDown_b, ref kDown_b, ref tDown_b, ref sigmaDown_b, 1.0); d2PV_dFdBeta = (betaUp_b - betaDown_b) / (2.0 * dF); d2PV_dS2 = (fUp_b - fDown_b) / (2.0 * dF); d2PV_dKdS = (kUp_b - kDown_b) / (2.0 * dF); d2PV_dSdT = (tUp_b - tDown_b) / (2.0 * dF); d2PV_dSdVol = (sigmaUp_b - sigmaDown_b) / (2.0 * dF); } { var dK = eps * (1.0 + Math.Abs(k)); double betaUp_b = 0.0; double fUp_b = 0.0; double kUp_b = 0.0; double tUp_b = 0.0; double sigmaUp_b = 0.0; double pUp = BetaBlack.beta_black_b(beta, callPut, f, k + dK, t, sigma, ref betaUp_b, ref fUp_b, ref kUp_b, ref tUp_b, ref sigmaUp_b, 1.0); double betaDown_b = 0.0; double fDown_b = 0.0; double kDown_b = 0.0; double tDown_b = 0.0; double sigmaDown_b = 0.0; double pDown = BetaBlack.beta_black_b(beta, callPut, f, k - dK, t, sigma, ref betaDown_b, ref fDown_b, ref kDown_b, ref tDown_b, ref sigmaDown_b, 1.0); d2PV_dKdBeta = (betaUp_b - betaDown_b) / (2.0 * dK); // d2PV_dKdS = (fUp_b - fDown_b) / (2.0 * dK); d2PV_dK2 = (kUp_b - kDown_b) / (2.0 * dK); d2PV_dKdT = (tUp_b - tDown_b) / (2.0 * dK); d2PV_dVoldK = (sigmaUp_b - sigmaDown_b) / (2.0 * dK); } { var dSigma = eps * (1.0 + Math.Abs(sigma)); double betaUp_b = 0.0; double fUp_b = 0.0; double kUp_b = 0.0; double tUp_b = 0.0; double sigmaUp_b = 0.0; double pUp = BetaBlack.beta_black_b(beta, callPut, f, k, t, sigma + dSigma, ref betaUp_b, ref fUp_b, ref kUp_b, ref tUp_b, ref sigmaUp_b, 1.0); double betaDown_b = 0.0; double fDown_b = 0.0; double kDown_b = 0.0; double tDown_b = 0.0; double sigmaDown_b = 0.0; double pDown = BetaBlack.beta_black_b(beta, callPut, f, k, t, sigma - dSigma, ref betaDown_b, ref fDown_b, ref kDown_b, ref tDown_b, ref sigmaDown_b, 1.0); d2PV_dVoldBeta = (betaUp_b - betaDown_b) / (2.0 * dSigma); // d2PV_dSdVol = (fUp_b - fDown_b) / (2.0 * dSigma); // d2PV_dVoldK = (kUp_b - kDown_b) / (2.0 * dSigma); d2PV_dVoldT = (tUp_b - tDown_b) / (2.0 * dSigma); d2PV_dVol2 = (sigmaUp_b - sigmaDown_b) / (2.0 * dSigma); } }
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); } }