Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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));
        }
Exemple #4
0
        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)));
        }
Exemple #5
0
        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)));
        }
Exemple #6
0
        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));
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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);
        }
Exemple #9
0
        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)));
            }
        }
Exemple #10
0
        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));
        }
Exemple #11
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));
            }
        }
Exemple #12
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);
            }
        }
Exemple #13
0
        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);
        }
Exemple #14
0
 public static double beta_black_cdf(double beta, double mu, double sigma, double x)
 {
     return(SpecialFunction.cdf_normal((inv_g(beta, x) - mu) / sigma));
 }
Exemple #15
0
 public static double beta_black_cdfinv(double beta, double mu, double sigma, double p)
 {
     return(g(beta, SpecialFunction.cdfinv_normal(p) * sigma + mu));
 }
Exemple #16
0
        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);
        }
Exemple #17
0
        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);
            }
        }