static public void AmericanPutOptionTest() { double sigma = 0.8; double E = 100; double T = 0.25; double dt = 2e-4; double r = 0.1; int N = 800; double[] sol = new double[N]; double[] x = new double[N]; linspace(x, 0, 500, N); //construct BlackScholes equaiton BlkSch[] bs = new BlkSch[1]; BoundaryCondition[] bc = new BoundaryCondition[2]; bc[0] = new BoundaryCondition(); bc[1] = new BoundaryCondition(); bc[0].bc_type = BoundaryCondition.BC_Type.Dirichlet; bc[0].bc_values[0] = Math.Max(E - x[0], 0); //bc[1].bc_type = BoundaryCondition.BC_Type.Dirichlet; bc[1].bc_type = BoundaryCondition.BC_Type.LargeS; bc[1].bc_values[0] = Math.Max(E - x.Last(), 0); for (int i = 0; i < sol.Length; ++i) { sol[i] = Math.Max(E - x[i], 0); } Action <double[][], int> constrain = new Action <double[][], int>((V, i) => { V[0][i] = Math.Max(V[0][i], Math.Max(E - x[i], 0)); }); bs[0] = new BlkSch(sol, x, sigma * sigma / 2, r, -r, null, bc); BS_PSOR bs_solver = new BS_PSOR(bs, constrain, BS_PSOR.Scheme.CrankNicolson); double t = T; while (t > 0) { Console.WriteLine("t = {0}", t); if (t - dt < 0) { dt = t; } t = t - dt; bs_solver.advance(dt); bs_solver.printSolution("AmPut.txt", sol); Console.WriteLine("price(100) = {0}", bs_solver.getPrice(bs[0], 100)); } }
static public void ConvertibleBondTest() { double sigma = 0.2; double rc = 0.02; double r = 0.05; double rg = 0.02; double[] coupon_dates = new double[10]; linspace(coupon_dates, 0.5, 5, 10); double Bc_clean = 110; double Bp_clean = 105; double Bc = Bc_clean; double Bp = Bp_clean; double[] CallInterval = new double[2] { 1, 5 }; double[] PutInterval = new double[2] { 2, 3 }; double F = 101; double Coupon = 4; double kappa = 1.0; double T = 5; int N = 4000; double[] CB = new double[N]; double[] COCB = new double[N]; double[] x = new double[N]; linspace(x, 0, 5 * F, N); BlkSch[] bs = new BlkSch[2]; //construct Black Scholes equation for CB BoundaryCondition[] CB_bc = new BoundaryCondition[2]; CB_bc[0] = new BoundaryCondition(); CB_bc[1] = new BoundaryCondition(); CB_bc[0].bc_type = BoundaryCondition.BC_Type.ZeroS; CB_bc[1].bc_type = BoundaryCondition.BC_Type.Dirichlet; CB_bc[1].bc_values[0] = kappa * x.Last(); double[] minus_rcB = new double[N]; bs[0] = new BlkSch(CB, x, 0.5 * sigma * sigma, rg, -r, minus_rcB, CB_bc); //construct Black Scholes equation for COCB BoundaryCondition[] COCB_bc = new BoundaryCondition[2]; COCB_bc[0] = new BoundaryCondition(); COCB_bc[1] = new BoundaryCondition(); COCB_bc[0].bc_type = BoundaryCondition.BC_Type.ZeroS; COCB_bc[1].bc_type = BoundaryCondition.BC_Type.Dirichlet; COCB_bc[1].bc_values[0] = 0; bs[1] = new BlkSch(COCB, x, 0.5 * sigma * sigma, rg, -(r + rc), null, COCB_bc); //setup constrain Action <double[][], int> constrain = new Action <double[][], int>((V, i) => { double[] cb = V[0]; double[] cocb = V[1]; //upside constrain due to callability double tmp = Math.Max(Bc, kappa * x[i]); if (cb[i] > tmp) { cb[i] = tmp; cocb[i] = 0; } //downside constrain due to puttability if (cb[i] < Bp) { cb[i] = Bp; cocb[i] = Bp; } //upside constrain due to conversion tmp = kappa * x[i]; if (cb[i] < tmp) { cb[i] = tmp; cocb[i] = 0; } }); //setup initial condition for (int i = 0; i < N; ++i) { CB[i] = (kappa * x[i] >= F + Coupon) ? kappa * x[i] : F + Coupon; COCB[i] = (kappa * x[i] >= F + Coupon) ? 0.0 : F + Coupon; } //construct solver BS_PSOR solver = new BS_PSOR(bs, constrain); //advance solution backwards from maturity to t = 0; double t = T; double dt = 1.0 / 365; int coupon_index = coupon_dates.Length - 1; string CBoutput = "CBsol.txt"; string COCBoutput = "COCBsol.txt"; System.IO.File.WriteAllText(CBoutput, string.Empty); System.IO.File.WriteAllText(COCBoutput, string.Empty); solver.printSolution(CBoutput, CB); solver.printSolution(COCBoutput, COCB); Console.WriteLine("t = {0}, dt = {1}", t, dt); while (t > 0) { if (t - dt < 0) { dt = t; } //update source term for (int i = 0; i < N; ++i) { minus_rcB[i] = -rc * COCB[i]; } //update coefficients //update call price if (PutInterval != null && t >= PutInterval[0] && t <= PutInterval[1]) { Bp = Bp_clean + AccI(t, coupon_dates, Coupon); } else { Bp = 0; } //update put price if (CallInterval != null && t >= CallInterval[0] && t <= CallInterval[1]) { Bc = Bc_clean + AccI(t, coupon_dates, Coupon); } else { Bc = double.MaxValue; } //advance solution solver.advance(dt); //compute Coupon between [t-dt, t]; while (coupon_index >= 0 && coupon_dates[coupon_index] >= t - dt && coupon_dates[coupon_index] <= t) { Console.WriteLine("Paying coupon {0} at {1}", Coupon, coupon_dates[coupon_index]); for (int i = 0; i < CB.Length; ++i) { CB[i] += Coupon; COCB[i] += Coupon; } coupon_index--; } t = t - dt; Console.WriteLine("t = {0}, dt = {1}", t, dt); Console.WriteLine("price(100) = {0}", solver.getPrice(bs[0], 100)); //solver.printSolution(CBoutput, CB); //solver.printSolution(COCBoutput, COCB); } }