def fivepointtostrikevol(fwd, atm, rr25, fly25, rr10, fly10, mat): ''' create volslice with atm vol transform strikes with a quadratic ''' strikevol = fivepointsmile(fwd, atm, rr25, fly25, rr10, fly10, mat) strikevolbase = fivepointsmile(fwd, atm, 0, 0, 0, 0, mat) strikevolmap = interpolate.interp1d(np.log(strikevolbase[:, 0]), np.log(strikevol[:, 0]), fill_value="extrapolate", kind=2) lbound = BS.blackstrikefordelta(atm, strikevol[0, 0], mat, -1, 0.01) ubound = BS.blackstrikefordelta(atm, strikevol[-1, 0], mat, 1, 0.01) stvol = np.ones([1000, 2]) stvol[:, 0] = np.linspace(lbound, ubound, 1000) stvol[:, 1] = atm vol = logvolslice(stvol, fwd, mat) baseprob = vol.cumdf.strikeprob prob = baseprob.copy() prob[:, 0] = [np.exp(strikevolmap(np.log(stri))) for stri in baseprob[:, 0]] prob[:, 1] = baseprob[:, 1] plt.plot(prob[:, 0], prob[:, 1], label="smile") plt.plot(baseprob[:, 0], baseprob[:, 1], label="nonsmile") plt.title("smile") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.show() strikevol = cdf(prob).getstrikevol(prob[:, 0], mat) return strikevol
def __init__(self, strikevol, fwd, mat): """ strike vol : 2d np array ascending order strike, vol """ self.strikevol = strikevol self.fwd = fwd self.mat = mat self.strikevolinter = interpolate.interp1d(strikevol[:, 0], strikevol[:, 1], kind=2, fill_value="extrapolate") nofstrikes = self.strikevol.shape[0] strprob = np.ones((nofstrikes - 1, 2)) * 0.5 for i in range(0, nofstrikes - 1): v0 = self.strikevol[i][1] v1 = self.strikevol[i + 1][1] k0 = self.strikevol[i][0] k1 = self.strikevol[i + 1][0] #cp = -1 if k0 < self.fwd else 1 cp = 1 if fwd < k0 else -1 d0 = BS.black(v0, self.fwd, k0, self.mat, cp) d1 = BS.black(v1, self.fwd, k1, self.mat, cp) strprob[i, 0] = (k1 + k0) / 2 strprob[i, 1] = (cp + 1) / 2 + (d1 - d0) / (k1 - k0) self.cumdf = cdf(strprob)
def calibslice(lvol): lvolf = np.maximum(0, lvol) interped = intp.interp1d(calibki, lvolf, kind=1, fill_value="extrapolate") lvolj = interped(R[i - 1]) volj = volji * lvolj #print(lvol) R[i] = R[i - 1] * dffor[i - 1] / dfdom[i - 1] * np.exp( -volj * volj * t / 2 + paths[i - 1] * volj * math.sqrt(t)) driftadj = np.mean(R[i]) / fwd R[i] = R[i] / driftadj volsim = [ BS.volfromsim(R[i], fwd, strk, i * t) for strk in calibki ] #err = np.linalg.norm(np.log(volsim) - np.log(volcalibi)) err = np.linalg.norm(volsim - volcalibi) #if i == 1 : # print(err, volsim, volcalibi) return err cdf0 = vu.cdf(BS.mccdf(R[i])) err = np.linalg.norm( np.log(cdf0.probinterpinv(cumppt)) - np.log(cdf1.probinterpinv(cumppt))) print(err) return err * err
def getstrikevol(self, strikes, mat): simul = self.getsimulation(500000) fwd = simul.mean() print("Fwd", fwd) fwd = 15.0 strikevol = np.zeros((strikes.shape[0], 2)) strikevol[:, 0] = strikes strikevol[:, 1] = np.array( [BS.volfromsim(simul, fwd, stri, mat) for stri in strikes]) return strikevol
def testquadsmile(): fwd = 15.0 T = 10 a = 0.5 b = 0.0001 c = 1.05 stvol = np.ones([1000, 2]) stvol[:, 0] = np.exp(np.linspace(np.log(0.5), np.log(100), 1000)) stvol[:, 1] = 0.2 stvol[:, 1] = 0.2 * du.quad(np.log(stvol[:, 0] / fwd) / 0.2 / T, a, b, c) vol = logvolslice(stvol, fwd, T) cdfvol = vol.cumdf.strikeprob plt.plot(cdfvol[:, 0], cdfvol[:, 1]) plt.title("cdf") plt.show() pdfvol = vol.cumdf.getpdf() plt.plot(pdfvol[:, 0], pdfvol[:, 1]) plt.title("pdf") plt.show() strvol = vol.cumdf.getstrikevol(stvol[:, 0], vol.mat) plt.plot(stvol[:, 0], stvol[:, 1], label="input quadvoL") plt.plot(strvol[:, 0], strvol[:, 1], label="vol from cdf") plt.title("vol from cdf") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.show() xran = np.random.normal(size=10000) vols = 0.2 sim = fwd * np.exp(-vols * vols * T / 2 + vols * xran * math.sqrt(T)) lbound = vol.cumdf.getstrike(0.01) ubound = vol.cumdf.getstrike(0.99) print(lbound, ubound) x = np.linspace(ubound, lbound, 30) y = [BS.volfromsim(sim, sim.mean(), stri, T) for stri in x] plt.plot(x, y) plt.title("vol from cdf") plt.show() print(vol.deltastrike(0.25, 1), vol.deltastrike(0.25, -1)) print(vol.getrr(0.25), vol.getfly(0.25)) print(vol.deltastrike(0.1, 1), vol.deltastrike(0.1, -1)) print(vol.getrr(0.1), vol.getfly(0.1)) strikevolsmile = fivepointtostrikevol(15, 0.20, -0.06, 0.01, -0.12, 0.03, 5) plt.plot(strikevolsmile[:, 0], strikevolsmile[:, 1]) plt.title("interpolated smile") plt.show() return vol
def fivepointsmile(fwd, atm, rr25, fly25, rr10, fly10, mat): c25 = 0.5 * (rr25 + 2 * fly25 + 2 * atm) p25 = c25 - rr25 c10 = 0.5 * (rr10 + 2 * fly10 + 2 * atm) p10 = c10 - rr10 kc25 = BS.blackstrikefordelta(c25, fwd, mat, 1, 0.25) kc10 = BS.blackstrikefordelta(c10, fwd, mat, 1, 0.1) kp25 = BS.blackstrikefordelta(p25, fwd, mat, -1, 0.25) kp10 = BS.blackstrikefordelta(p10, fwd, mat, -1, 0.1) strikevol = np.ones((5, 2)) strikevol[0, 0] = kp10 strikevol[0, 1] = p10 strikevol[1, 0] = kp25 strikevol[1, 1] = p25 strikevol[2, 0] = fwd strikevol[2, 1] = atm strikevol[3, 0] = kc25 strikevol[3, 1] = c25 strikevol[4, 0] = kc10 strikevol[4, 1] = c10 return strikevol
def bounddelta(strike): f = self.fwd v = self.getvolforstrike(strike) mat = self.mat err = (BS.blackdelta(v, f, strike, mat, cp) - delta) return err * err
def __init__(self, spot, sigma, paths, time, rdom=None, rfor=None, volcalib=None, marginadjust=False): self.sigma = sigma self.numpaths = paths.shape[1] self.timeslices = paths.shape[0] self.sigma = sigma self.time = time R = np.zeros([self.timeslices + 1, self.numpaths]) dti = self.time[1:] - self.time[:-1] if rdom is None: rdom = np.ones((self.timeslices, self.numpaths)) * 0.05 rfor = np.ones((self.timeslices, self.numpaths)) * 0.05 dffor = 1.0 / (1 + rfor[:-1] * dti.reshape((-1, 1))) dfdom = 1.0 / (1 + rdom[:-1] * dti.reshape((-1, 1))) R[0, :] = spot cumppt = np.linspace(0.1, 0.9, 5) bounds = [(0, None), (0, None), (0, None), (0, None), (0, None)] for i in range(1, self.timeslices + 1): print("calib timeslice", i) t = dti[i - 1] dfr = (1 + t * np.mean(rfor[i - 1])) / (1 + t * np.mean(rdom[i - 1])) fwd = np.mean(R[i - 1]) / dfr volji = np.maximum(0, sigma[i - 1]) cdf1 = volcalib[i - 1].cumdf calibki = cdf1.getstrike(cumppt) * dfr volcalibi = volcalib[i - 1].getvolforstrike(calibki) def calibslice(lvol): lvolf = np.maximum(0, lvol) interped = intp.interp1d(calibki, lvolf, kind=1, fill_value="extrapolate") lvolj = interped(R[i - 1]) volj = volji * lvolj #print(lvol) R[i] = R[i - 1] * dffor[i - 1] / dfdom[i - 1] * np.exp( -volj * volj * t / 2 + paths[i - 1] * volj * math.sqrt(t)) driftadj = np.mean(R[i]) / fwd R[i] = R[i] / driftadj volsim = [ BS.volfromsim(R[i], fwd, strk, i * t) for strk in calibki ] #err = np.linalg.norm(np.log(volsim) - np.log(volcalibi)) err = np.linalg.norm(volsim - volcalibi) #if i == 1 : # print(err, volsim, volcalibi) return err cdf0 = vu.cdf(BS.mccdf(R[i])) err = np.linalg.norm( np.log(cdf0.probinterpinv(cumppt)) - np.log(cdf1.probinterpinv(cumppt))) print(err) return err * err # do drift adjustment #res = opt.minimize(calibslice,np.ones(cumppt.shape[0]), method="Nelder-Mead",options={"maxiter":100}) #res = opt.minimize(calibslice,np.ones(cumppt.shape[0]), method="Nelder-Mead",tol=1e-2) #res = opt.minimize(calibslice,np.ones(cumppt.shape[0]), bounds = bounds) #print(res.x) #print(res.message, res.x) #calibslice(res.x) #driftadj = np.mean(R[i])/fwd #R[i] = R[i]/driftadj if volcalib is not None: res = opt.minimize(calibslice, np.ones(cumppt.shape[0]), bounds=bounds) print(res.x) #print(res.message, res.x) calibslice(res.x) x = np.linspace(0.05, 0.95, 20) cdf0 = vu.cdf(BS.mccdf(R[i])) p = cdf0.getcumprob(R[i]) adjRi = cdf1.getstrike(p) if i == 1 or marginadjust: R[i] = adjRi pass cdf2 = vu.cdf(BS.mccdf(R[i])) #plt.plot(cdf0.getstrike(x),x, label = "original") plt.plot(cdf1.getstrike(x), x, label="target") plt.plot(cdf2.getstrike(x), x, label="adjusted") plt.title("generated cdf vs target") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.show() else: calibslice(np.ones(cumppt.shape[0])) driftadj = np.mean(R[i]) / fwd R[i] = R[i] / driftadj self.paths = R
def main(): #np.random.seed(100910) numpaths = 20000 N = 5 rho = 0.75 T = 5.0 t = T / N spot = 18.95 TI = np.linspace(0, T, N + 1) # 0 :fx, 1:fxvol, 2:dom, 3:for rho01 = 0 rho02 = 0.002 rho03 = -0.27 rho12 = 0 rho13 = 0 rho23 = 0.16 cov = np.array([[1, rho01, rho02, rho03], [rho01, 1, rho12, rho13], [rho02, rho12, 1, rho23], [rho03, rho13, rho23, 1]]) paths = corpath.getpaths(cov, numpaths, N) #dom domts = np.ones(N + 1) * 0.001 meanrev = np.ones([N]) * 0.01 sigma = np.ones([N]) * 0.005 * 20 rdom = irprocess.OrUhl(domts, meanrev, sigma, paths[2], TI) #for forts = du.funa(0.205, 0.15, 0.25, N + 1) meanrev = np.ones([N]) * 0.05 sigma = np.ones([N]) * 0.020 rfor = irprocess.OrUhl(forts, meanrev, sigma, paths[3], TI) atm = intp.interp1d([0.25, 5], [0.208, 0.28]) rr25 = intp.interp1d([0.25, 5], [-0.06, -0.1025]) rr10 = intp.interp1d([0.25, 5], [-0.12, -0.2]) fly25 = intp.interp1d([0.25, 5], [0.009, 0.0154]) fly10 = intp.interp1d([0.25, 5], [0.045, 0.075]) fwd = spot volcalib = [] for i in range(0, N): ti = (i + 1) * t fwd = fwd * (1 + domts[i] * t) / (1 + forts[i] * t) #print( fwd,atm(ti),rr25(ti),fly25(ti),rr10(ti),fly10(ti),ti) voluninterp = vu.fivepointsmile(fwd, atm(ti), rr25(ti), fly25(ti), rr10(ti), fly10(ti), ti) volinterp = vu.strikevolinterp(voluninterp) volcalib.append(vu.logvolslice(volinterp, fwd, ti)) #Stoch vol process meanrev = np.ones([N]) * 0.2 vvol = np.ones([N]) * 0.05 basevol = np.ones([N + 1]) * 0.135 vol = volprocess.Logoruhl(basevol, meanrev, vvol, paths[1], TI) sigma = vol.paths asset = Stochvol(spot, sigma, paths[0], TI, rdom.paths, rfor.paths, volcalib) #plot 10 random sample paths R = asset.paths a = np.random.permutation(R.shape[1]) for i in range(0, 50): plt.plot(TI, rdom.paths[:, a[i]]) plt.title("Dom Rate Process") plt.show() for i in range(0, 50): plt.plot(TI, rfor.paths[:, a[i]]) plt.title("For Rate Process") plt.show() for i in range(0, 50): plt.plot(TI, vol.paths[:, a[i]]) plt.title("Vol Process") plt.show() for i in range(0, 50): plt.plot(TI, R[:, a[i]]) plt.title("Fx Process") plt.show() fxfwds = np.ones([N + 1]) * spot fxfwdspaths = np.ones([N + 1]) * spot fxvolpaths = np.ones([N + 1]) * basevol[0] for i in range(1, N + 1): fxfwds[i] = fxfwds[i - 1] * (1 + t * np.mean(rdom.paths[i - 1])) / ( 1 + t * np.mean(rfor.paths[i - 1])) fxfwdspaths[i] = np.mean(R[i]) fxvolpaths[i] = BS.blackimply(fxfwdspaths[i], fxfwdspaths[i], t * i, 1, asset.optprice(fxfwdspaths[i], 1, i)) plt.plot(TI, fxfwds) plt.plot(TI, fxfwdspaths) plt.title("Fxfwds: fwds and on paths") plt.show() plt.plot(TI, fxvolpaths) plt.title("Vol evolving with time") plt.show() fwd = fxfwds[-1] std = fxvolpaths[-1] * fwd x = np.linspace(fwd - 3 * std, fwd + 3 * std, 15) y = [ BS.blackimply(fwd, stri, T, -1, asset.optprice(stri, -1)) for stri in x ] plt.plot(x, y, label="from mc") y1 = volcalib[-1].strikevolinter(x) plt.plot(x, y1, label="original") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.title("Terminal Smile") plt.show() print(BS.blackimply(fwd, fwd + std, T, 1, (asset.optprice(fwd + std, 1)))) print(BS.blackimply(fwd, fwd, T, 1, (asset.optprice(fwd, 1)))) print(BS.blackimply(fwd, fwd - std, T, -1, (asset.optprice(fwd - std, -1)))) return asset, vol, rdom, rfor, volcalib, paths