from serenitas.utils.exchange import ExchangeMessage import pandas as pd from serenitas.utils.db2 import dbconn from psycopg.errors import UniqueViolation import datetime import logging import argparse import re from serenitas.ops.trade_dataclasses import CreditSwaptionDeal, desc_str from serenitas.ops.dataclass_mapping import Fund parser = argparse.ArgumentParser() parser.add_argument( "workdate", nargs="?", default=str(datetime.date.today()), type=datetime.date.fromisoformat, ) args = parser.parse_args() logger = logging.getLogger(__name__) columns = [ "option_recap", "index_buyer", "index_seller", "trade_date", "effective_date", "execution_time", "notional", "price", "mid_price", "strike", "expiry_date", "option_type", "exercise_clearing_house", "premium_fee", "premium_fee_pay_date", "trade_id", "venue_mic", ] # Counterparty Ticket to Database _baml_funds: dict[str, Fund] = { "LMIL-Boston Patriot Bowdoin St LLC": "BOWDST", "Iso Selene Inc.": "ISOSEL", "Serenitas Credit Gamma Master Fund LP": "SERCGMAST", "Lmcg Investments, Llc": "SERCGMAST", "Reep-Ofc One Bowdoin Square Ma Llc Et Al (Cob)": "BOWDST", } def regex_baml_numbers(text: str): return float(re.search(r"[\d,]+", text)[0].replace(",", "")) def from_baml_email(d: dict, conn): buysell = "Bank Of America" in d["Index Seller"] index, series = re.search(r"(HY|IG)\.([\d]{2})", d["OPTION RECAP"]).groups() security_desc = desc_str(index, series, "5") trade_date = datetime.datetime.strptime(d["Trade Date"], "%d-%b-%Y") price = float(d["Price"]) / 100 sql_str = ( "SELECT * FROM index_desc WHERE INDEX=%s AND series=%s " "AND lastdate >= %s AND tenor='5yr'" ) with conn.cursor() as c: c.execute(sql_str, (index, series, trade_date)) row = c.fetchone() return CreditSwaptionDeal( buysell=buysell, fund=_baml_funds[d[f"Index {'Buyer' if buysell else 'Seller'}"]], cp_code="BAMSNY", portfolio="OPTIONS", folder=index + ("PAYER" if d["Option Type"] == "Payer" else "REC"), trade_date=trade_date, settle_date=datetime.datetime.strptime(d["Premium Fee Pay Date"], "%d-%b-%Y"), expiration_date=datetime.datetime.strptime(d["Expiry Date"], "%d-%b-%Y"), notional=regex_baml_numbers(d["Notional"]), option_type=d["Option Type"].upper(), strike=d["Strike"], swap_type="CD_INDEX_OPTION", fixed_rate=row.coupon / 100, maturity=row.maturity, security_desc=security_desc, security_id=row.redindexcode, currency="USD", price=price, initial_margin_percentage=( regex_baml_numbers(d["Collateral"]) / regex_baml_numbers(d["Premium Fee"]) * price ) if d.get("Collateral") else None, ) if __name__ == "__main__": dawndb = dbconn("dawndb") em = ExchangeMessage() start = datetime.datetime.combine(args.workdate, datetime.time.min).replace( tzinfo=em._account.default_timezone ) for msg in em.get_msgs( path=["AutoBook", "BAML Swaption"], datetime_received__gte=start ): dfs = pd.read_html(msg.body) tickets = [] for ticket in dfs: if len(ticket[0]) > 5: ticket = ticket.set_index(0).to_dict()[1] if "Block" in ticket or "Unwind Price" in ticket: continue tickets.append(ticket) trade = from_baml_email(ticket, dawndb) trade.stage() df = pd.DataFrame.from_dict(tickets) df.columns = df.columns.str.lower().str.replace(" ", "_") if "collateral" in df.columns: additional_columns = ["collateral"] else: additional_columns = [] df = df[columns + additional_columns] place_holders = ",".join(["%s"] * len(df.columns)) try: with dawndb.cursor() as c: c.executemany( f"INSERT INTO baml_swaption_ticket({','.join(df.columns)})" f"VALUES ({place_holders})", df.itertuples(index=False), ) except UniqueViolation as e: logger.warning(str(e)) CreditSwaptionDeal._insert_queue.clear() else: CreditSwaptionDeal.commit()