public static bool zbrac(Func <double, double> f, ref double x1, ref double x2, double num2 = 1.6) { var num = 50; if (x1 == x2) { throw FS.FailWith($"Bad initial range in zbrac x1 = x2 with {x1}"); } var num3 = 0; var num4 = f(x1); var num5 = f(x2); while (num3++ < num) { if (num4 * num5 <= 0.0) { return(true); } if (Math.Abs(num4) < Math.Abs(num5)) { x1 /= num2; num4 = f(x1); } else { x2 *= num2; num5 = f(x2); } } return(false); }
public static double beta_black_break_even(double beta, double f, double t, double sigma, double dt = 1.0 / 365.0) { try { //var pack = new OptionBetaBlack(beta, OptionType.Put, f, f, t, sigma); //var gamma = pack.d2PV_dS2; //if (gamma == 0.0) throw FS.FailWith($"Cannot compute breakeven, gamma is zero."); //var res = Math.Sqrt(2.0 * pack.dPV_dT * dt / pack.d2PV_dS2); var zz = 0.0; var T_b = 0.0; var f_b = 0.0; beta_black_b(beta, OptionType.Put, f, f, t, sigma, ref zz, ref f_b, ref zz, ref T_b, ref zz, 1.0); var df = Math.Sqrt(DoubleUtils.DoubleEpsilon) * (1.0 + Math.Abs(f)); var fup_b = 0.0; beta_black_b(beta, OptionType.Put, f + df, f, t, sigma, ref zz, ref fup_b, ref zz, ref zz, ref zz, 1.0); var gamma = (fup_b - f_b) / df; if (gamma == 0.0) { throw FS.FailWith($"Cannot compute Break even, gamma is zero"); } var res = Math.Sqrt(2.0 * T_b * dt / gamma); // if (double.IsNaN(res)) throw FS.FailWith("Nan"); return(res); } catch (Exception e) { FS.WithInfo(e, $"beta ={beta}, f = {f}, t = {t}, sigma = {sigma}, dt = {dt}"); throw; } }
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 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); } }