/* Vasicek: options on zero coupon bonds, page 151-152.*/
double VasicekBondOption(
	int fCall,
	double F,
	double X,
	double tau,
	double T,
	double r,
	double Theta,
	double kappa,
	double v) 
{
	double PtT, Pt_tau, vp, H, result;

	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_volatility(v);

    X = X / F;
    PtT = VasicekBondPrice(0.0, T, r, Theta, kappa, v);
    Pt_tau = VasicekBondPrice(0.0, tau, r, Theta, kappa, v);
    vp	= sqrt(pow2(v) * (1.0 - exp(-2.0 * kappa * T)) / (2.0 * kappa)) 
		* (1.0 - exp(-kappa * (tau - T))) / kappa;
   
    H = 1.0 / vp * log(Pt_tau / (PtT * X)) + vp / 2.0;
    
    if(fCall)
        result = F * (Pt_tau * cnd(H) - X * PtT * cnd(H - vp));
    else
        result = F * (X * PtT * cnd(-H + vp) - Pt_tau * cnd(-H));
    
    return result;
}
Exemple #2
0
/* Rho for the generalized Black and Scholes formula */
double rho(int fCall, double S, double X, double T, double r, double b, double v) 
{
	double result;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	if(b == 0.0)
		result = -T * gbs(fCall, S, X, T, r, b, v);
    else {
		const double vst = v * sqrt(T);
		const double d1 = (log(S / X) + (b + pow2(v) / 2.0) * T) / (vst);
		const double d2 = d1 - vst;

		if(fCall) 
			result = T * X * exp(-r * T) * cnd(d2);
		else 
			result = -T * X * exp(-r * T) * cnd(-d2);
	}

	return result;
}
extern double BSbisection(int fCall, double S, double X, double T, double r, double b, double price)
{

	double vLow = VOLATILITY_MIN;
	double vHigh = VOLATILITY_MAX, cLow, cHigh;

	double val, diff, vi = 0.0;
	for(;;) {

		cLow = BSAmericanApprox(fCall, S, X, T, r, b, vLow);
		cHigh = BSAmericanApprox(fCall, S, X, T, r, b, vHigh);
		if(price < cLow)
			return 0.0; 
		else if(price > cHigh)
			return VOLATILITY_MAX;

		vi = vLow + (price - cLow) * (vHigh - vLow) / (cHigh - cLow);
		if(vi < VOLATILITY_MIN)
			vi = VOLATILITY_MIN;
		else if(vi > VOLATILITY_MAX)	
			vi = VOLATILITY_MAX;

		assert_valid_volatility(vi);
		val = BSAmericanApprox(fCall, S, X, T, r, b, vi);
		diff = price - val;
		if(fabs(diff) <= epsilon)
			break;
		else if(val < price)
			vLow = vi;
		else
			vHigh = vi;
	}

	return vi;
}
/* Vasicek: value zero coupon bond */
extern double VasicekBondPrice(
	double t1,
	double T,
	double r,
	double Theta,
	double kappa,
	double v) 
{
	double BtT, AtT, PtT;

	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_volatility(v);

    BtT = (1 - exp(-kappa * (T - t1))) / kappa;
    AtT = exp(
		(BtT - T + t1) * (pow2(kappa) * Theta - pow2(v) / 2.0) / pow2(kappa) 
		- pow2(v) * pow2(BtT) / (4.0 * kappa)
	);

    PtT = AtT * exp(-BtT * r);

	assert(is_sane(PtT));
	return PtT;
}
/* Merton (1973) Options on stock indices, page 4. */
double merton73(
	int fCall,
	double S,
	double X,
	double T,
	double r,
	double q,
	double v) 
{
	double d1, d2, result;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_volatility(v);

    d1 = (log(S / X) + (r - q + pow2(v) / 2.0) * T) / (v * sqrt(T));
    d2 = d1 - v * sqrt(T);

    if(fCall)
        result = S * exp(-q * T) * cnd(d1) - X * exp(-r * T) * cnd(d2);
    else 
        result = X * exp(-r * T) * cnd(-d2) - S * exp(-q * T) * cnd(-d1);

	assert(is_sane(result));
	return result;
}
Exemple #6
0
/* Theta for the generalized Black and Scholes formula */
double theta(int fCall, double S, double X, double T, double r, double b, double v)
{
    double st, d1, d2, sbrt, result;

    assert_valid_price(S);
    assert_valid_strike(X);
    assert_valid_time(T);
    assert_valid_interest_rate(r);
    assert_valid_cost_of_carry(b);
    assert_valid_volatility(v);

    st = sqrt(T);
    d1 = (log(S / X) + (b + pow2(v) / 2.0) * T) / (v * st);
    d2 = d1 - v * st;
    sbrt= S * exp((b - r) * T);

    if(fCall) {
        result = -sbrt * normdist(d1) * v / (2.0 * st)
                 - (b - r) * sbrt * cnd(d1)
                 - r * X * exp(-r * T) * cnd(d2);
    }
    else {
        result = -sbrt * normdist(d1) * v / (2.0 * st)
                 + (b - r) * sbrt * cnd(-d1)
                 + r * X * exp(-r * T) * cnd(-d2);
    }

    return result;
}
/* Calculation of critical price options on options */
static double CriticalValueOptionsOnOptions(
	int fCall,
	double X1,
	double X2,
	double T, 
	double r,
	double b,
	double v) 
{
	double Si, ci, di;

	assert_valid_strike(X1);
	assert_valid_strike(X2);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

    Si = X1;
    ci = gbs(fCall, Si, X1, T, r, b, v);
    di = delta(fCall, Si, X1, T, r, b, v);

    /* Newton-Raphson algorithm */
    while(fabs(ci - X2) > epsilon) {
        Si = Si - (ci - X2) / di;
        ci = gbs(fCall, Si, X1, T, r, b, v);
        di = delta(fCall, Si, X1, T, r, b, v);
    }

    return Si;
}
/* Two asset correlation options */
double TwoAssetCorrelation(
	int fCall,
	double S1,
	double S2,
	double X1,
	double X2,
	double T,
	double b1,
	double b2,
	double r,
	double v1,
	double v2,
	double Rho)
{
	double sT, d1, d2, result;

	assert_valid_price(S1);
	assert_valid_price(S2);
	assert_valid_strike(X1);
	assert_valid_strike(X2);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b1);
	assert_valid_cost_of_carry(b2);
	assert_valid_volatility(v1);
	assert_valid_volatility(v2);

	sT = sqrt(T);
    d1 = (log(S1 / X1) + (b1 - pow2(v1) / 2.0) * T) / (v1 * sT);
    d2 = (log(S2 / X2) + (b2 - pow2(v2) / 2.0) * T) / (v2 * sT);
    
    if (fCall) {
        result 
			= S2 * exp((b2 - r) * T) * cbnd(d2 + v2 * sT, d1 + Rho * v2 * sT, Rho) 
			- X2 * exp(-r * T)       * cbnd(d2, d1, Rho);
    }
	else {
		result 
			= X2 * exp(-r * T)       * cbnd(-d2, -d1, Rho) 
			- S2 * exp((b2 - r) * T) * cbnd(-d2 - v2 * sT, -d1 - Rho * v2 * sT, Rho);
    }

	assert(is_sane(result));
	return result;
}
extern double carry_put(double S, double X, double T, double r, double b, double v) 
{
	double d1;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	d1 = (log(S / X) + (b + pow2(v) / 2.0) * T) / (v * sqrt(T));
	return -T * S * exp((b - r) * T) * cnd(-d1);
}
extern double blackscholes_put(double S, double X, double T, double r, double v) 
{
	double vst, d1;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_volatility(v);

	vst = v * sqrt(T);
    d1 = (log(S / X) + (r + pow2(v) / 2) * T) / (vst);
	return X * exp(-r * T) * cnd(-(d1 - vst)) - S * cnd(-d1);
}
/* Arithmetic average rate option */
double TurnbullWakemanAsian(
	int fCall,
	double S,
	double SA,
	double X,
	double T,
	double T2,
	double tau,
	double r,
	double b,
	double v) 
{
	double m1, m2, bA, vA, t1, result;
	double b2, vv, bvv, b2vv, t22;

	assert_valid_price(S);
	assert_valid_price(SA);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_time(T2);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	b2 = b * 2.0;
	vv= v * v;
	bvv = b+vv;
	b2vv = b2 + vv;
	t22 = pow(T2-tau, 2.0);

    m1 = (exp(b * T2) - exp(b * tau)) / (b * (T2 - tau));
    m2 	= 2.0 * exp(b2vv * T2)  / (bvv * b2vv * t22) 
		+ 2.0 * exp(b2vv * tau) / (b * t22) 
		* (1.0 / b2vv - exp(b * (T2 - tau)) / bvv);
    
    bA = log(m1) / T2;
    vA = sqrt(log(m2) / T2 - 2.0 * bA);
    t1 = T - T2;
    
    if (t1 > 0) {
        X = T / T2 * X - t1 / T2 * SA;
        result = gbs(fCall, S, X, T2, r, bA, vA) * T2 / T;
	}
    else
        result = gbs(fCall, S, X, T2, r, bA, vA);

	return result;
}
Exemple #12
0
/* Complex chooser options */
double ComplexChooser(
    double S,
    double Xc,
    double Xp,
    double T,
    double Tc,
    double Tp,
    double r,
    double b,
    double v)
{
    assert_valid_price(S);
    assert_valid_strike(Xc);
    assert_valid_strike(Xp);
    assert_valid_time(Tc);
    assert_valid_time(Tp);
    assert_valid_interest_rate(r);
    assert_valid_cost_of_carry(b);
    assert_valid_volatility(v);

    {   /* No C99 */

        const double vsT = v * sqrt(T);
        const double vsTc = v * sqrt(Tc);
        const double vsTp = v * sqrt(Tp);
        const double bv2 = b + pow2(v)/2.0;

        const double I = CriticalValueChooser(S, Xc, Xp, T, Tc, Tp, r, b, v);
        const double d1 = (log(S / I) + bv2 * T) / vsT;
        const double d2 = d1 - vsT;
        const double Y1 = (log(S / Xc) + bv2 * Tc) / vsTc;
        const double y2 = (log(S / Xp) + bv2 * Tp) / vsTp;
        const double rho1 = sqrt(T / Tc);
        const double rho2 = sqrt(T / Tp);

        const double result
            =  S * exp((b - r) * Tc) * cbnd(d1, Y1, rho1)
               - Xc * exp(-r * Tc)      * cbnd(d2, Y1 - vsTc, rho1)
               -  S * exp((b - r) * Tp) * cbnd(-d1, -y2, rho2)
               + Xp * exp(-r * Tp)      * cbnd(-d2, -y2 + vsTp, rho2);

        return result;
    } /* No C99 */
}
/* Black and Scholes (1973) Stock options */
double blackscholes(int fCall, double S, double X, double T, double r, double v) 
{
	double vst, d1, d2;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_volatility(v);

	assert(r >= 0.0);			/* Interest rate >= 0.0 */
	assert(v > 0.0 && v <= 100.0);	/* Volatility between 0 and 100 */
    
	vst = v * sqrt(T);
    d1 = (log(S / X) + (r + pow2(v) / 2.0) * T) / (vst);
    d2 = d1 - vst;
    if(fCall) 
        return S * cnd(d1) - X * exp(-r * T) * cnd(d2);
    else 
        return X * exp(-r * T) * cnd(-d2) - S * cnd(-d1);
}
/* Three dimensional binomial tree */
extern double ThreeDimensionalBinomial(
	int type,
	int EuropeanOption,
	int fCall,
	double S1,
	double S2,
	double Q1,
	double Q2,
	double X1,
	double X2,
	double T,
	double r,
	double b1,
	double b2,
	double v1,
	double v2,
	double rho,
	int n)
{
	double result, dt, sdt, mu1, mu2, u, d;
	int i, j, m;
	double optval[n + 1][n + 1];

	assert_valid_price(S1);
	assert_valid_price(S2);
	assert_valid_strike(X1);
	assert_valid_strike(X2);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b1);
	assert_valid_cost_of_carry(b2);
	assert_valid_volatility(v1);
	assert_valid_volatility(v2);

	assert(n > 0);
	assert(type >= TDB_OPTIONTYPE_MIN);
	assert(type <= TDB_OPTIONTYPE_MAX);


	dt = T / n;
	sdt = sqrt(dt);
	mu1 = b1 - pow2(v1) / 2.0;
	mu2 = b2 - pow2(v2) / 2.0;
	u = exp(mu1 * dt + v1 * sdt);
	d = exp(mu1 * dt - v1 * sdt);

	for(j = 0; j <= n; j++) {
		const double Y1 = (2.0 * j - n) * sdt;
		const double NodeValueS1 = S1 * pow(u, (double)j) * pow(d, (double)(n - j));

		for(i = 0; i <= n; i++) {
			double NodeValueS2;
			const double y2 = rho * Y1 + sqrt(1.0 - pow2(rho)) * (2.0 * i - n) * sdt;
			(void)y2; /* Read TODO file for more info */

			NodeValueS2 
				= S2 * exp(mu2 * n * dt) 
				* exp(v2 * (rho * Y1 + sqrt(1 - pow2(rho)) 
				* (2.0 * i - n) * sdt));

			optval[j][i] = PayoffFunction(type, fCall, NodeValueS1, NodeValueS2, Q1, Q2, X1, X2);
		} 
	}


	for(m = n - 1; m >= 0; m--) {
		for(j = 0; j <= m; j++) {
			const double Y1 = (2.0 * j - m) * sdt;
			const double NodeValueS1 = S1 * pow(u, (double)j) * pow(d, (double)(m - j));

			for(i = 0; i <= m; i++) {
				const double y2 = rho * Y1 + sqrt(1.0 - pow2(rho)) * (2.0 * i - m) * sdt;
				const double NodeValueS2 = S2 * exp(mu2 * m * dt) * exp(v2 * y2);
				optval[j][i] 
					= 0.25 * (
						  optval[j][i] 
						+ optval[j + 1][i] 
						+ optval[j][i + 1]  
						+ optval[j + 1][i + 1]
					) * exp(-r * dt);

				if(!EuropeanOption) {
					optval[j][i] = fmax(optval[j][i], PayoffFunction(type, fCall, NodeValueS1, NodeValueS2, Q1, Q2, X1, X2));
				}
			} 
		}

	}

	result = optval[0][0];
	assert(is_sane(result));
	return result;
}
/* Options on options */
double OptionsOnOptions(
	int typeflag,
	double S,
	double X1,
	double X2,
	double t1, 
	double T2,
	double r,
	double b,
	double v) 
{
	double result, bv2, vst1, vsT2, I, Rho, _y1, y2, z1, z2;
	int fCall;

	assert_valid_price(S);
	assert_valid_strike(X1);
	assert_valid_strike(X2);
	assert_valid_time(t1);
	assert_valid_time(T2);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	bv2 = b + pow2(v) / 2.0;
	vst1 = v * sqrt(t1);
	vsT2 = v * sqrt(T2);

	assert(typeflag == OOO_CALL_ON_CALL || typeflag == OOO_CALL_ON_PUT || typeflag == OOO_PUT_ON_CALL || typeflag == OOO_PUT_ON_PUT);
    
	if(typeflag == OOO_CALL_ON_CALL || typeflag == OOO_PUT_ON_CALL)
        fCall = 1;
    else
        fCall = 0;
    
    I = CriticalValueOptionsOnOptions(fCall, X1, X2, T2 - t1, r, b, v);
    
    Rho = sqrt(t1 / T2);
    _y1 = (log(S / I) + bv2 * t1) / vst1;
    y2 = _y1 - vst1;
    z1 = (log(S / X1) + bv2 * T2) / vsT2;
    z2 = z1 - vsT2;

	if(typeflag == OOO_CALL_ON_CALL) {
        result 
			=  S * exp((b - r) * T2) * cbnd(z1, _y1, Rho) 
			- X1 * exp(-r * T2)      * cbnd(z2, y2, Rho) 
			- X2 * exp(-r * t1)      * cnd(y2);
    }
	else if(typeflag == OOO_PUT_ON_CALL) {
        result 
			= X1 * exp(-r * T2)      * cbnd(z2, -y2, -Rho) 
			-  S * exp((b - r) * T2) * cbnd(z1, -_y1, -Rho) 
			+ X2 * exp(-r * t1)      * cnd(-y2);
    }
	else if(typeflag == OOO_CALL_ON_PUT) {
        result 
			= X1 * exp(-r * T2)      * cbnd(-z2, -y2, Rho) 
			-  S * exp((b - r) * T2) * cbnd(-z1, -_y1, Rho) 
			- X2 * exp(-r * t1)      * cnd(-y2);
    }
	else if(typeflag == OOO_PUT_ON_PUT) {
        result 
			=  S * exp((b - r) * T2) * cbnd(-z1, _y1, -Rho) 
			- X1 * exp(-r * T2)      * cbnd(-z2, y2, -Rho) 
			+ X2 * exp(-r * t1)      * cnd(y2);
    }
	else {
		/* Abort to be on the safe side */
		abort();
	}

	assert(is_sane(result));
	return result;
}
/* Trinomial tree */
extern double TrinomialTree(
	int EuropeanOption,
	int fCall,
	double S,
	double X,
	double T,
	double r,
	double b,
	double v,
	double n) /* NOTE: n should probably be made an int . boa 20070316 */
{
	int i, j, z;
	double dt, u, vsdt2, d, pu, pd, pm, Df, result;

	double *pval;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	if( (pval = calloc(2*n + 1, sizeof *pval)) == NULL) {
		abort();
	}

	if(fCall)
		z = 1;
	else
		z = -1;

	dt = T / n;
	u = exp(v * sqrt(2.0 * dt));
	vsdt2 = v * sqrt(dt / 2.0);
	d = exp(-v * sqrt(2.0 * dt));
	pu = pow2((exp(b * dt / 2.0) - exp(-vsdt2)) / (exp(vsdt2) - exp(-vsdt2)));
	pd = pow2((exp(vsdt2) - exp(b * dt / 2.0)) / (exp(vsdt2) - exp(-vsdt2)));
	pm = 1.0 - pu - pd;
	Df = exp(-r * dt);

	for(i = 0; i <= 2 * n; i++) {
		pval[i] = fmax(0.0, z * (S * pow(u, fmax(i - n, 0.0)) * pow(d, fmax(n * 2.0 - n - i, 0.0)) - X));
	}

	for(j = (int)n - 1; j >= 0; j--) {
		for(i = 0; i <= j * 2; i++) {
			if(EuropeanOption) {
				pval[i] = (pu * pval[i + 2] + pm * pval[i + 1] + pd * pval[i]) * Df;
			}
			else {
				pval[i] = fmax(
					(z * (S * pow(u, fmax((double)i - j, 0.0)) * pow(d, fmax((double)j * 2.0 - j - i, 0.0)) - X)),
					(pu * pval[i + 2] + pm * pval[i + 1] + pd * pval [i]) * Df);
			}
		} 
	}

	result = pval[0];
	free(pval);
	assert(is_sane(result));
	return result;
}
/* Two asset barrier options */
double TwoAssetBarrier(
	int typeflag,
	double S1,
	double S2,
	double X,
	double H,
	double T,
	double r,
	double b1,
	double b2,
	double v1,
	double v2,
	double Rho) 
{
	double sT, v2sT, logHS2, mu1, mu2, d1, d2, d3, d4, e1, e2, e3, e4, w;
    double eta;    /* Binary variable: 1 for call options and -1 for put options */
    double phi;	/* Binary variable: 1 for up options and -1 for down options */

	assert_valid_price(S1);
	assert_valid_price(S2);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b1);
	assert_valid_cost_of_carry(b2);
	assert_valid_volatility(v1);
	assert_valid_volatility(v2);
    
    
	sT = sqrt(T);
	v2sT = v2 * sT;
	logHS2 = log(H / S2);
    mu1 = b1 - pow2(v1) / 2.0;
    mu2 = b2 - pow2(v2) / 2.0;
    
/*
 * The book has some errors, including this function. The old, broken version
 * is the original code which matches the testdata in table 2-12. That doesn't
 * help, since the function itself is broken... So we use the new version.
 */
#ifdef USE_OLD_BROKEN_VERSION
    d1 = (log(S1 / X) + (mu1 + pow2(v1) / 2.0) * T) / (v1 * sT);
#else
    d1 = (log(S1 / X) + (mu1 + pow2(v1)) * T) / (v1 * sT);
#endif

    d2 = d1 - v1 * sT;
    d3 = d1 + 2.0 * Rho * logHS2 / v2sT;
    d4 = d2 + 2.0 * Rho * logHS2 / v2sT;
    e1 = (logHS2 - (mu2 + Rho * v1 * v2) * T) / v2sT;
    e2 = e1 + Rho * v1 * sT;
    e3 = e1 - 2.0 * logHS2 / v2sT;
    e4 = e2 - 2.0 * logHS2 / v2sT;
   
	switch(typeflag) {
		case SB_CALL_UP_OUT:
		case SB_CALL_UP_IN:
			eta = phi = 1;
			break;

		case SB_CALL_DOWN_OUT:
		case SB_CALL_DOWN_IN:
			eta = 1;
			phi = -1;
			break;
		case SB_PUT_UP_OUT:
		case SB_PUT_UP_IN:
			eta = -1; 
			phi = 1;
			break;

		case SB_PUT_DOWN_OUT:
		case SB_PUT_DOWN_IN:
			phi = eta = -1;
			break;

		default:
			assert(0);
			abort();
	}

    w	= eta * S1 * exp((b1 - r) * T) * (
			cbnd(eta * d1, phi * e1, -eta * phi * Rho) 
			- exp(2.0 * (mu2 + Rho * v1 * v2) * logHS2 / pow2(v2)) * cbnd(eta * d3, phi * e3, -eta * phi * Rho)
		) 
		- eta * exp(-r * T) * X * (cbnd(eta * d2, phi * e2, -eta * phi * Rho) 
		- exp(2.0 * mu2 * logHS2 / pow2(v2)) * cbnd(eta * d4, phi * e4, -eta * phi * Rho));

	switch(typeflag) {
		case SB_CALL_UP_OUT:
		case SB_CALL_DOWN_OUT:
		case SB_PUT_UP_OUT:
		case SB_PUT_DOWN_OUT:
			return w;

		case SB_CALL_UP_IN:
		case SB_CALL_DOWN_IN:
        return gbs_call(S1, X, T, r, b1, v1) - w;

		case SB_PUT_UP_IN:
		case SB_PUT_DOWN_IN:
			return gbs_put(S1, X, T, r, b1, v1) - w;

		default:
			abort();
	}
}
/* Standard barrier options */
double StandardBarrier(
	int typeflag,
	double S,
	double X,
	double H,
	double K,
	double T,
	double r,
	double b,
	double v)
{
	double HS, vsT, erT, mu, lambda, X1, X2, _y1, y2, Z, pse;
    int eta, phi;

	assert_valid_price(S);
	assert_valid_strike(X);
	assert_valid_time(T);
	assert_valid_interest_rate(r);
	assert_valid_cost_of_carry(b);
	assert_valid_volatility(v);

	HS = H/S;
	vsT = v * sqrt(T);;
	erT = exp(-r * T) ;
    
    mu = (b - pow2(v) / 2.0) / pow2(v);
    lambda = sqrt(pow2(mu) + 2.0 * r / pow2(v));
    X1 = log(S / X) / vsT + (1.0 + mu) * vsT;
    X2 = log(S / H) / vsT + (1.0 + mu) * vsT;
    _y1 = log(pow2(H) / (S * X)) / vsT + (1.0 + mu) * vsT;
    y2 = log(HS) / vsT + (1.0 + mu) * vsT;
    Z =  log(HS) / vsT + lambda * vsT;
    
	/* Binary variables which is either 1 or -1 */
	switch(typeflag) {
		case SB_CALL_DOWN_IN:
		case SB_CALL_DOWN_OUT:
			eta = 1;
			phi = 1;
			break;
    
		case SB_CALL_UP_IN:
		case SB_CALL_UP_OUT:
			eta = -1;
			phi = 1;
			break;
    
		case SB_PUT_DOWN_IN:
		case SB_PUT_DOWN_OUT:
			eta = 1;
			phi = -1;
			break;

		case SB_PUT_UP_IN:
		case SB_PUT_UP_OUT:
			eta = -1;
			phi = -1;
			break;

		default:
			abort();
	}

	pse = phi * S * exp((b - r) * T);

/* Inline macros to avoid calling cnd() as well as to avoid duplicating formulas. We could of course create
 * inline functions instead, but then we need to pass a lot of params...
 */
#define PX (phi * X * erT * pow(HS, (2.0 * mu)))
#define PP (pse * pow(HS, (2.0 * (mu + 1.0))))

#define F1 (pse * cnd(phi * X1) - phi * X * erT * cnd(phi * X1 - phi * vsT))
#define F2 (pse * cnd(phi * X2) - phi * X * erT * cnd(phi * X2 - phi * vsT))
#define F3 (PP * cnd(eta * _y1) - PX * cnd(eta * _y1 - eta * vsT))
#define F4 (PP * cnd(eta * y2) - PX * cnd(eta * y2 - eta * vsT))
#define F5 (K * erT * (cnd(eta * X2 - eta * vsT) - pow(HS, (2.0 * mu)) * cnd(eta * y2 - eta * vsT)))
#define F6 (K * (pow(HS, (mu + lambda)) * cnd(eta * Z) + pow(HS, (mu - lambda)) * cnd(eta * Z - 2.0 * eta * lambda * vsT)))
    
    if (X > H) {
		switch(typeflag) {
			case SB_CALL_DOWN_IN:	return F3 + F5;
			case SB_CALL_UP_IN:		return F1 + F5;
			case SB_PUT_DOWN_IN:	return F2 - F3 + F4 + F5;
			case SB_PUT_UP_IN:		return F1 - F2 + F4 + F5;
			case SB_CALL_DOWN_OUT:	return F1 - F3 + F6;
			case SB_CALL_UP_OUT:	return F6;
			case SB_PUT_DOWN_OUT:	return F1 - F2 + F3 - F4 + F6;
			case SB_PUT_UP_OUT:		return F2 - F4 + F6;
			default:				abort();
		}
    }
	else if( X < H) {
		switch(typeflag) {
			case SB_CALL_DOWN_IN:	return F1 - F2 + F4 + F5;
			case SB_CALL_UP_IN:		return F2 - F3 + F4 + F5;
			case SB_PUT_DOWN_IN:	return F1 + F5;
			case SB_PUT_UP_IN:		return F3 + F5;
			case SB_CALL_DOWN_OUT:	return F2 + F6 - F4;
			case SB_CALL_UP_OUT:	return F1 - F2 + F3 - F4 + F6;
			case SB_PUT_DOWN_OUT:	return F6;
			case SB_PUT_UP_OUT:		return F1 - F3 + F6;
			default:				abort();
		}
    }
	else {
		switch(typeflag) {
			case SB_CALL_DOWN_IN:	/* fall through */
			case SB_PUT_DOWN_IN:	return F1;
			case SB_CALL_UP_IN:		/* fall through */
			case SB_PUT_UP_IN:		
			case SB_CALL_DOWN_OUT:
			case SB_CALL_UP_OUT:
			case SB_PUT_DOWN_OUT:
			case SB_PUT_UP_OUT:		return F6;
			default:				abort();
		}
	}
}