/* 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; }
/* 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 */ }
int main(void) { size_t i, niter = 300*1000; clock_t start, stop; double sum = 0.0; start = clock(); for(i = 0; i < niter; i++) { sum += cbnd((double)i, 0.1, 0.0); } stop = clock(); printf("%'lu clocks for %zu calls\n", (unsigned long)(stop - start), niter); return 0; }
/* 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(); } }
/* The cumulative bivariate normal distribution function */ double cbnd(double a, double b, double Rho) { double result, t, a1, b1; assert(is_sane(a)); assert(is_sane(b)); assert(is_sane(Rho)); t = sqrt(2.0 * (1.0 - pow2(Rho))); a1 = a / t; b1 = b / t; if(a <= 0.0 && b <= 0.0 && Rho <= 0.0) { double sum = 0.0; const double rho20 = Rho * 2.0; int i; for(i = 0; i < 5; i++) { sum += XX[i][0] * exp(a1 * (y2[i] - a1) + (b1 * (y2[0] - b1) ) + (rho20 * (y[i] - a1) * (y[0] - b1))); sum += XX[i][1] * exp(a1 * (y2[i] - a1) + (b1 * (y2[1] - b1) ) + (rho20 * (y[i] - a1) * (y[1] - b1))); sum += XX[i][2] * exp(a1 * (y2[i] - a1) + (b1 * (y2[2] - b1) ) + (rho20 * (y[i] - a1) * (y[2] - b1))); sum += XX[i][3] * exp(a1 * (y2[i] - a1) + (b1 * (y2[3] - b1) ) + (rho20 * (y[i] - a1) * (y[3] - b1))); sum += XX[i][4] * exp(a1 * (y2[i] - a1) + (b1 * (y2[4] - b1) ) + (rho20 * (y[i] - a1) * (y[4] - b1))); } result = sqrt(1.0 - pow2(Rho)) / pi * sum; } else if(a <= 0.0 && b >= 0.0 && Rho >= 0.0) { result = cnd(a) - cbnd(a, -b, -Rho); } else if(a >= 0.0 && b <= 0.0 && Rho >= 0.0) { result = cnd(b) - cbnd(-a, b, -Rho); } else if(a >= 0.0 && b >= 0.0 && Rho <= 0.0) { result = cnd(a) + cnd(b) - 1.0 + cbnd(-a, -b, Rho); } else if( (a * b * Rho) > 0.0) { double rho1, rho2, Delta; const double sp2a = sqrt(pow2(a) - (Rho * 2.0 * a * b) + pow2(b)); #if 0 /*assert(is_sane(sp2a));*/ if(!is_sane(sp2a) || sp2a == 0.0) { fprintf(stderr, "This shouldn't happen :-(\n"); fprintf(stderr, "----------------------------\n"); fprintf(stderr, "sp2a == %f\n", sp2a); fprintf(stderr, "pow2(a) == %.10g, Rho == %.10g a==%.10g b==%.10g pow2(b) == %.10g\n", pow2(a), Rho, a, b, pow2(b)); fprintf(stderr, "The arg is %.10f\n", pow2(a) - (Rho * 2.0 * a * b) + pow2(b)); fprintf(stderr, "The middle part is %.10g\n", (Rho * 2.0 * a * b) ); fprintf(stderr, "----------------------------\n"); abort(); } #endif rho1 = (Rho * a - b) * sign(a) / sp2a; #if 0 if(!is_sane(rho1)) { fprintf(stderr, "Rho==%g a == %g b == %g sign(a) == %g sp2a==%g\n", Rho, a, b, sign(a), sp2a); } assert(is_sane(rho1)); #endif rho2 = (Rho * b - a) * sign(b) / sp2a; assert(is_sane(rho2)); Delta = (1.0 - sign(a) * sign(b)) / 4.0; assert(is_sane(Delta)); result = cbnd(a, 0.0, rho1) + cbnd(b, 0.0, rho2) - Delta; } else { /* Hmm, illegal input? Abort to be on the safe side */ assert(0); abort(); } return result; }
int main(void) { /* Testing cbnd() */ assert_equal(cbnd( 0.0, 0.0, 0.0), 0.250000); assert_equal(cbnd( 0.0, 0.0, -0.5), 0.1666667); assert_equal(cbnd( 0.0, 0.0, 0.5), 0.333333); assert_equal(cbnd( 0.0, -0.5, 0.0), 0.154269); assert_equal(cbnd( 0.0, -0.5, -0.5), 0.081660); assert_equal(cbnd( 0.0, -0.5, 0.5), 0.226878); assert_equal(cbnd( 0.0, 0.5, 0.0), 0.345731); assert_equal(cbnd( 0.0, 0.5, -0.5), 0.273122); assert_equal(cbnd( 0.0, 0.5, 0.5), 0.418340); assert_equal(cbnd(-0.5, 0.0, 0.0), 0.154269); assert_equal(cbnd(-0.5, 0.0, -0.5), 0.081660); assert_equal(cbnd(-0.5, 0.0, 0.5), 0.226878); assert_equal(cbnd(-0.5, -0.5, 0.0), 0.095195); assert_equal(cbnd(-0.5, -0.5, -0.5), 0.036298); assert_equal(cbnd(-0.5, -0.5, 0.5), 0.163319); assert_equal(cbnd(-0.5, 0.5, 0.0), 0.213342); assert_equal(cbnd(-0.5, 0.5, -0.5), 0.145218); assert_equal(cbnd(-0.5, 0.5, 0.5), 0.272239); assert_equal(cbnd( 0.5, 0.0, 0.0), 0.345731); assert_equal(cbnd( 0.5, 0.0, -0.5), 0.273122); assert_equal(cbnd( 0.5, 0.0, 0.5), 0.418340); assert_equal(cbnd( 0.5, -0.5, 0.0), 0.213342); assert_equal(cbnd( 0.5, -0.5, -0.5), 0.145218); assert_equal(cbnd( 0.5, -0.5, 0.5), 0.272239); assert_equal(cbnd( 0.5, 0.5, 0.0), 0.478120); assert_equal(cbnd( 0.5, 0.5, -0.5), 0.419223); assert_equal(cbnd( 0.5, 0.5, 0.5), 0.546244); return 0; }
/* Exchange options on exchange options */ double ExchangeExchangeOption( int TypeFlag, double S1, double S2, double q, double t1, double T2, double r, double b1, double b2, double v1, double v2, double Rho) { double result, I, d1, d2, d3, d4, Y1, y2, y3, y4; double st1T2, v, I1; int id; /* TODO: No asserts 20070310 */ st1T2 = sqrt(t1 / T2); v = sqrt(pow2(v1) + pow2(v2) - 2.0 * Rho * v1 * v2); I1 = S1 * exp((b1 - r) * (T2 - t1)) / (S2 * exp((b2 - r) * (T2 - t1))); if (TypeFlag == 1 || TypeFlag == 2) id = 1; else id = 2; I = CriticalPrice(id, I1, t1, T2, v, q); d1 = (log(S1 / (I * S2)) + (b1 - b2 + pow2(v) / 2.0) * t1) / (v * sqrt(t1)); d2 = d1 - v * sqrt(t1); d3 = (log((I * S2) / S1) + (b2 - b1 + pow2(v) / 2.0) * t1) / (v * sqrt(t1)); d4 = d3 - v * sqrt(t1); Y1 = (log(S1 / S2) + (b1 - b2 + pow2(v) / 2.0) * T2) / (v * sqrt(T2)); y2 = Y1 - v * sqrt(T2); y3 = (log(S2 / S1) + (b2 - b1 + pow2(v) / 2.0) * T2) / (v * sqrt(T2)); y4 = y3 - v * sqrt(T2); if (TypeFlag == 1) { result = -S2 * exp((b2 - r) * T2) * cbnd(d2, y2, st1T2) + S1 * exp((b1 - r) * T2) * cbnd(d1, Y1, st1T2) - q * S2 * exp((b2 - r) * t1) * cnd(d2); } else if( TypeFlag == 2) { result = S2 * exp((b2 - r) * T2) * cbnd(d3, y2, -st1T2) - S1 * exp((b1 - r) * T2) * cbnd(d4, Y1, -st1T2) + q * S2 * exp((b2 - r) * t1) * cnd(d3); } else if( TypeFlag == 3) { result = S2 * exp((b2 - r) * T2) * cbnd(d3, y3, st1T2) - S1 * exp((b1 - r) * T2) * cbnd(d4, y4, st1T2) - q * S2 * exp((b2 - r) * t1) * cnd(d3); } else if( TypeFlag == 4) { result = -S2 * exp((b2 - r) * T2) * cbnd(d2, y3, -st1T2) + S1 * exp((b1 - r) * T2) * cbnd(d1, y4, -st1T2) + q * S2 * exp((b2 - r) * t1) * cnd(d2); } else abort(); 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; }