import csv import datetime import json import logging import os import os.path import psycopg2 import re import sys import yaml sys.path.append('.') from csv import reader from db import dbconn logger = logging.getLogger(__name__) pattern1 = re.compile("REINVEST\[\w+::REINV_TBA(\d)\]\[DEAL,(\d+)\]=.*$") # reinv end date pattern2 = re.compile("(STANDARD_VAR\[)\w+(::#REINV_END,\d+\]=)(\d.*$)") # reinvprices 1 pattern3 = re.compile("STANDARD_VAR\[\w+::#PRICE100_TBA(\d),(\d+)\]=") pattern5 = re.compile("STANDARD_VAR\[\w+::#REINVPCT_TBA(\d),(\d+)\]=") pattern7 = re.compile("KINGS3") pattern8 = re.compile("(#COLLATREINV_REINV_PCT_EXT\[)\w+(::\*\]\[DEAL,\d+\])=100") pattern9 = re.compile("(?PSEVERITY\[\w+,\d+\]=)mkt\(70\)") # we use 84 so that it's both dividable by 2 and 3 global_reinvfloatpercentage = 84 global_reinvfixedpercentage = 16 def get_reinv_assets(conn, dealname, workdate): sqlstr = 'SELECT * FROM et_historicaldealinfo(%s, %s) WHERE ReinvFlag IS TRUE' d = {} with conn.cursor() as c: c.execute(sqlstr, (dealname, workdate)) for line in c: d[line['issuername']] = (line['fixedorfloat'], line['liborfloor']) conn.commit() return d def get_recovery(conn, dealname, workdate, defaultrecovery=50): """ compute average price of defaulted assets """ sqlstr = "select sum(coalesce(price, %s) * currentbalance)/sum(currentbalance) " + \ "from et_aggdealinfo_historical(%s, %s) where defaultedflag is True" with conn.cursor() as c: try: c.execute(sqlstr, (defaultrecovery, dealname, workdate)) recovery, = c.fetchone() except (TypeError, psycopg2.DataError): recovery = defaultrecovery finally: if recovery is None: recovery = defaultrecovery conn.commit() return float(recovery) def get_reinvenddate(conn, dealname, workdate): sqlstr = 'SELECT reinv_end_date FROM historical_clo_universe(%s, %s)' with conn.cursor() as c: c.execute(sqlstr, (dealname, workdate)) reinvenddate, = c.fetchone() conn.commit() if reinvenddate: return reinvenddate.strftime("%Y%m%d") else: raise Exception("missing reinvestment end date") def generate_scenarios(workdate, dealname, conn): prometheus = os.path.join(os.environ['BASE_DIR'], "Scenarios", "prometheus.sss") n_scenarios = 100 basedir = os.path.join(os.environ['BASE_DIR'], "Scenarios", "Intex curves_" + workdate) defaultedprice = get_recovery(conn, dealname, workdate) replace = "\g{0:.3f}".format(defaultedprice) try: with open(os.path.join(basedir, "csv", dealname + ".config")) as fh: config = yaml.load(fh) except IOError: logger.error("{0}: config file doesn't exist".format(dealname)) return reinvflag = config['reinvflag'] if reinvflag: reinvenddate = get_reinvenddate(conn, dealname, workdate) reinv_assets = get_reinv_assets(conn, dealname, workdate) n_float_assets = len([v for v in reinv_assets.values() if v[0] == 'FLOAT']) n_fixed_assets = len([v for v in reinv_assets.values() if v[0] == 'FIXED']) rollingmat = config['rollingmat'] if n_fixed_assets == 0: reinvfixedpercentage = 0 else: reinvfixedpercentage = global_reinvfixedpercentage / n_fixed_assets if n_float_assets > 0: reinvfloatpercentage = (100 - n_fixed_assets * reinvfixedpercentage)/n_float_assets try: with open(os.path.join(basedir, "csv", dealname + "-reinvprices.csv"), "r") as fh: dr = csv.DictReader(fh) reinvprices = {f: [] for f in dr.fieldnames} for line in dr: for f in dr.fieldnames: try: val = float(line[f]) except ValueError: logger.error("Incorrect value in reinvprices for {}".format(dealname)) else: reinvprices[f].append("{0:.3f}".format(val)) except IOError: reinvflag = False sssfile = os.path.join(basedir, "sss", dealname + ".sss") if not os.path.exists(os.path.join(basedir, "sss")): os.makedirs(os.path.join(basedir, "sss")) cdrscenarios = os.path.join(basedir, "csv", dealname + "-cdr.csv") recoveryscenarios = os.path.join(basedir, "csv", dealname + "-recovery.csv") fhsss = open(sssfile, "w") fhcdr = open(cdrscenarios, "r") fhrecovery = open(recoveryscenarios, "r") csvcdr = reader(fhcdr) csvrecovery = reader(fhrecovery) cdrline = next(csvcdr) cdrline = "\t".join(["{0:.3f}".format(float(cdr)) for cdr in cdrline]) recoveryline = next(csvrecovery) recoveryline = "\t".join(["{0:.3f}".format(float(recovery)) for recovery in recoveryline]) i=1 with open(prometheus) as fh: for line in fh: line = line.rstrip() if "DEAL_NAME" in line: newline = "DEAL_NAME=" + dealname.upper() + "\r\n" fhsss.write(newline) continue if not reinvflag and pattern8.match(line): line = re.sub(pattern8, r"\1{0}\2=0".format(dealname.upper()), line) fhsss.write(line + "\r\n") continue if not reinvflag and "DO_REINV" in line: fhsss.write("DO_REINV=0" + "\r\n") continue m = pattern1.match(line) if reinvflag and m: reinv_number, scen_number = m.groups() key = "REINV_TBA" + reinv_number if key in reinv_assets: fixedorfloat, liborfloor = reinv_assets[key] if fixedorfloat == "FLOAT": coupon = 4 elif fixedorfloat == "FIXED": coupon = 7 line = "REINVEST[{0}::REINV_TBA{1}][DEAL,{2}]=".format(dealname.upper(), reinv_number, scen_number) line += "COUP_SPR={0}|AMORT=Bullet|USE_REINVEST_PIP=1|MAT_DATE={1}|".format(coupon, rollingmat) if liborfloor is not None: line += "|ARM_LIFE_FLOOR={}".format(liborfloor) fhsss.write(line + "\r\n") continue if reinvflag and pattern2.match(line): line = re.sub(pattern2, r"\1{0}\2{1}", line).format(dealname.upper(), reinvenddate) fhsss.write(line + "\r\n") continue m = pattern3.match(line) if reinvflag and m: reinv_number, scen_number = m.groups() if dealname=="litpt3" and reinv_number=="1": line = "STANDARD_VAR[LITPT3::#REINVLOANP100,{0}]".format(scen_number) + \ "={0}".format(" ".join(reinvprices["REINV_TBA1"])) fhsss.write(line + "\r\n") continue reinv_name = "REINV_TBA" + reinv_number if reinv_name in reinvprices: line = \ "STANDARD_VAR[{0}::#PRICE100_TBA{1},{2}]={3}".format(dealname.upper(), reinv_number, scen_number, " ".join(reinvprices[reinv_name])) fhsss.write(line + "\r\n") continue m = pattern5.match(line) if reinvflag and m: reinv_number, scen_number = m.groups() reinv_name = "REINV_TBA" + reinv_number if reinv_number == "1": if dealname == "litpt3": line = "STANDARD_VAR[LITPT3::#LOANREINVPCT,{0}]=100".format(scen_number) fhsss.write(line + "\r\n") continue if dealname == "flags4": line = "STANDARD_VAR[FLAGS4::#PCT100_TBA1,{0}]=100".format(scen_number) fhsss.write(line + "\r\n") continue if reinv_name in reinv_assets: if reinv_assets[reinv_name][0] == 'FIXED': line = "STANDARD_VAR[{0}::#REINVPCT_TBA{1},{2}]={3}".format(dealname.upper(), reinv_number, scen_number, reinvfixedpercentage) elif reinv_assets[reinv_name][0] == 'FLOAT': line = "STANDARD_VAR[{0}::#REINVPCT_TBA{1},{2}]={3}".format(dealname.upper(), reinv_number, scen_number, reinvfloatpercentage) fhsss.write(line + "\r\n") continue if pattern7.search(line): line = re.sub(pattern7, dealname.upper(), line) fhsss.write(line + "\r\n") continue if "LOSS_RATE[DEAL,{0}]".format(i) in line: newcdrline = "LOSS_RATE[DEAL,{0}]=".format(i) + cdrline fhsss.write(newcdrline + "\r\n") continue if "LOSS_SEVERITY[DEAL,{0}]".format(i) in line: newrecoveryline = "LOSS_SEVERITY[DEAL,{0}]=".format(i) + recoveryline fhsss.write(newrecoveryline + "\r\n") i = i + 1 if i <= n_scenarios: cdrline = next(csvcdr) cdrline = "\t".join(["{0:.3f}".format(float(cdr)) for cdr in cdrline]) + "\r\n" recoveryline = next(csvrecovery) recoveryline = "\t".join(["{0:.3f}".format(float(recovery)) \ for recovery in recoveryline]) + "\r\n" continue if "LOSS_NONPERF_SEVERITY" in line: line = re.sub(pattern9, replace, line) fhsss.write(line + "\r\n") fhsss.close() fhrecovery.close() fhcdr.close() logger.info("generated scenarios for: {0}".format(dealname)) if __name__ == "__main__": if len(sys.argv) > 1: workdate = sys.argv[1] else: workdate = str(datetime.date.today()) if len(sys.argv) > 2: dealnames = sys.argv[2:] else: dealnames = [d.split(".")[0] for d in os.listdir(os.path.join(os.environ['BASE_DIR'], "Scenarios", "Intex curves_" + workdate, "csv")) if "RData" in d] ET = dbconn('etdb') for dealname in dealnames: generate_scenarios(workdate, dealname, ET) ET.close()