def find_opt_rates(args: EvalArgs, actual: np.ndarray) -> dict: best_rates = {"heston": 0, "vg": 0, "ls": 0} best_fun = {"heston": 1000, "vg": 1000, "ls": 1000} if args.is_call: postfix = "call" else: postfix = "put" metric = "RMR" step = .001 upper = 1 with open(f"params/opt_rate_{postfix}.txt", "a+") as f: try: last = f.readlines()[-1] last_rate = float(re.search(r"Rate: (.+?), ", last).group(1)) start_from = int(last_rate / step) except IndexError or ValueError or AttributeError: start_from = 1 for rate in [ i * step for i in range(start_from, int(upper / step) + 1) ]: for model in ("heston", "vg", "ls"): args.r = rate args.q = rate res = tune_model(eval_args=args, bounds=par_bounds[model], model=model, local=False, metric=metric, prices=actual, polish=True, maxiter=50) if best_fun[model] > res.fun: best_rates[model] = rate best_fun[model] = res.fun msg = f"Rate: {rate}, model: {model}, metric: {metric}, pars: {array2str(res.x)}, " \ f"res.fun: {res.fun}, best: {best_rates[model]}" print(msg) f.write(msg + "\n") f.flush() f.write("\n") res = dict() vals = tuple(zip(best_rates, best_fun)) for i in range(len(best_rates.keys())): res[list(best_rates.keys())[i]] = vals[i] return res
def optimize_model(model: str, info: List[Info], data: Data, metric: str, day: int, rate: float, local: bool, use_fft: bool, **kwargs) -> opt.OptimizeResult: print(f"Optimizing {model} with {metric} on day {day}") actual_calls = data.prices[True][day] actual_puts = data.prices[False][day] pricer = GenPricer(model=model, market=EvalArgs.from_structure(data=data, info=info, rate=rate, day=day), use_fft=use_fft) optimizer = opt.minimize if local else opt.differential_evolution t0 = time() result = pricer.optimize_pars(metric=metric, actual_calls=actual_calls, actual_puts=actual_puts, bounds=par_bounds[model], optimizer=optimizer, **kwargs) with open(hf.get_log_file_name(model=model, metric=metric), 'a+') as log: hf.log_print( f"Time spent for {model}, day {day}: {timedelta(seconds=(time() - t0))}\n", out1=log) return result
def model_prices(pars: tuple, args: EvalArgs, model: str, strict=False, check=False, bounds_only=True) -> np.ndarray: return optimization.models[model](pars=pars, args=args.as_tuple(), strict=strict, check=check, bounds_only=bounds_only)
def estimate_model(pars: tuple, args: EvalArgs, model: str, metric: str, prices: np.ndarray) -> float: """ :param pars: model parameters :param args: market parameters with strikes as args[1] :param model: pricing model :param metric: quality metric :param prices: actual prices :return: value of quality metric on passed parameters """ k = args.get_strikes() if model not in models.keys(): raise Exception("Cannot use model " + model) if metric not in metrics.keys(): raise Exception("Cannot use metric " + metric) if (type(prices) is not np.ndarray) | (type(k) is not np.ndarray) | (len(prices) != len(k)): raise Exception("strikes and prices should be np.arrays with same length") return apply_metric(models[model](pars=pars, args=args.as_tuple()), prices, metric)
def tuning(): times = 5 metric = 'RMSE' optimizer = differential_evolution actual_puts = np.ndarray([]) spot = 1200 t = .76 r = .008 q = r is_call = True logfile = open(f'{root_dir}/params/time_benchmark_{datetime.now()}.log', 'w') for n in [1, 10, 25, 50, 100, 200]: strikes = np.array([i * 50 + 500 for i in range(n)]) args = (spot, strikes, t, r, q, is_call) market = EvalArgs.from_tuple((spot, strikes, t, r, q, is_call)) call_prices = FFT(model='vg', args=args).price((0.838288226409, -0.188041460262, 0.179096605713)) def bs_func(): GenPricer(model='bs', market=market, use_fft=False) \ .optimize_pars(metric=metric, optimizer=optimizer, bounds=par_bounds['bs'], actual_puts=actual_puts, actual_calls=call_prices, polish=True) t0 = time() unit = td_decorator(func=bs_func, times=times, seconds=True, log_each=True)() hf.log_print(f'BS unit time for {n} strike(s): {unit}, real time: {(time() - t0) / times}', logfile) for model in ['heston', 'vg', 'ls']: def func(): GenPricer(model=model, market=market, use_fft=True) \ .optimize_pars(metric=metric, optimizer=optimizer, bounds=par_bounds[model], actual_puts=actual_puts, actual_calls=call_prices, polish=True, disp=True, maxiter=50) t0 = time() hf.log_print(f"{model}: {td_decorator(func=func, times=times, seconds=True, log_each=True)() / unit}" f" units, real time: {time() - t0}", logfile) hf.log_print("\n", logfile) logfile.close()
def cut_by_bs_delta(data: Data, info: List[Info], rate=0.008, disp=False) -> Tuple[Data, List[Info]]: for day, is_call in [(d, c) for d in range(len(info)) for c in [True, False]]: pricer = gp.GenPricer(model='bs', market=EvalArgs.from_structure(data=data, info=info, rate=rate, day=day), use_fft=False) result: opt.OptimizeResult = pricer.optimize_pars( metric='MAE', actual_calls=data.prices[True][day], actual_puts=data.prices[False][day], bounds=config.par_bounds['bs'], optimizer=opt.differential_evolution, polish=True, disp=disp) bs_sigma = result.x[0] bs_sigma = bs_sigma deltas = bs.bs_delta(spot=info[day].spot, strikes=data.strikes[is_call][day], r=rate, q=rate, t=info[day].mat, bs_sigma=bs_sigma, is_call=is_call) data.prices[is_call][day] = data.prices[is_call][day][ np.abs(deltas) >= .1] data.strikes[is_call][day] = data.strikes[is_call][day][ np.abs(deltas) >= .1] return data, info
def main() -> None: # need to somehow work around with overflows np.seterr(all='warn') data, info = hf.read_new_data() print("Preparing data...") try: data = hf.get_prepared_data() except FileNotFoundError: data, info = dh.prepare_data(data=data, info=info) print("Done") day = 0 metric = "RMSE" is_call = None market = EvalArgs(spot=info[day].spot, k_call=data.strikes[True][day], k_put=data.strikes[False][day], tau=info[day].mat, r=.008, q=.008, call=is_call) # models = ('heston', 'vg', 'ls', 'bs') # kwargs = [{ # 'data': data, # 'rate': .008, # 'disp': False, # 'use_fft': True, # 'polish': True # }] * len(models) # all_args = zip(models, [metric] * len(models), [info] * len(models), [is_call] * len(models), kwargs) # pool = Pool() # pool.map(calibrate, all_args) tune_all_models(market=market, metric=metric)
def test_r(): # noinspection PyShadowingNames def prepare_args(a: List[str]) -> tuple: spot = float(a[0]) strike = float(a[1]) tau = float(a[2]) r = float(a[3]) q = float(a[4]) is_call = True if a[5] == 'TRUE' else False return spot, strike, tau, r, q, is_call def params2tuple(params: tuple) -> tuple: return params + tuple(None for _ in range(5 - len(params))) cases = read_r_data() names = ['model', 'p1', 'p2', 'p3', 'p4', 'p5', 'spot', 'strike', 't', 'r', 'q', 'is call', 'R answer', 'Python answer', 'diff', 'rel diff', 'is correct'] with open('sanity_log.txt', 'w') as log, open('sanity.csv', 'w') as out: out.write('; '.join(names) + "\n") for i, case in enumerate(cases): correct = True model = str(case[0]) data = case[1:-1] answer = float(case[-1]) if model.lower() == "heston": # func = he.price_heston pars = tuple(map(lambda x: float(x), data[:5])) args = prepare_args(data[5:]) elif model.lower() == "vg": # func = vg.price_vg pars = tuple(map(lambda x: float(x), data[:3])) args = prepare_args(data[3:]) elif model.lower() == "ls": # func = ls.price_ls pars = tuple(map(lambda x: float(x), data[:2])) args = prepare_args(data[2:]) else: raise Exception(f"Can't recognise model {model}") market = EvalArgs.from_tuple(args) pricer = GenPricer(model=model.lower(), market=market, use_fft=True) # calculated = float(func(pars, args)) calculated = pricer.price_call(pars) if market.is_call else pricer.price_put(pars) assert len(calculated) == 1 diff = abs(answer - calculated) if diff > 1e-2 * answer and diff > 1e-3 and not (answer < 0 and calculated == 0): correct = False log.write(f"Sanity test failed with case: {', '.join(case)}\n" f"\tR answer: {answer}\n" f"\tPython answer: {calculated}\n" f"\tDiff: {diff}\n\n") log.flush() p1, p2, p3, p4, p5 = params2tuple(params=pars) spot, strike, t, r, q, is_call = args row = f'{model};{p1};{p2};{p3};{p4};{p5};{spot};{strike};{t};{r};{q};' \ f'{is_call};{answer};{calculated};{diff};' \ f'{diff / answer if answer != 0 else "Inf"};{"+" if correct else "-"}\n' out.write(row) out.flush() if i % 1000 == 0: print(f"{i / len(cases):.{3}}")