from serenitas.utils.env import DAILY_DIR from serenitas.utils.exchange import ExchangeMessage import datetime from collateral.baml_isda import download_from_secure_id from bs4 import BeautifulSoup from urllib.parse import urlsplit, parse_qs, urlunsplit import logging import argparse from collateral.common import load_pdf from pathlib import Path import pandas as pd from collections import defaultdict import shutil from interest_statement import export_data from dateutil.relativedelta import relativedelta logger = logging.getLogger(__name__) def get_fpath(counterparty, save=False): match (counterparty, save): case (counterparty, False): return ( DAILY_DIR / "Serenitas" / "MonthlyInterest" / f"{counterparty}_reports" ) case ("CITI", True): return DAILY_DIR / f"{counterparty}_reports" / "Interest Statements" case ("BAML", True): return DAILY_DIR / "Serenitas" / f"BoA_reports" / "Interest Statements" case _: return ( DAILY_DIR / "Serenitas" / f"{counterparty}_reports" / "Interest Statements" ) def download_messages(em, counterparty, start, end, save=False): for msg in em.get_msgs( 20, path=["Interest", counterparty], ): base_dir = get_fpath(counterparty, save) if (msg.datetime_sent.date() >= start) and (msg.datetime_sent.date() <= end): if counterparty == "BAML": soup = BeautifulSoup(msg.body, features="lxml") a = soup.find("a") url = urlsplit(a["href"]) query = parse_qs(url.query) base_url = urlunsplit(url[:2] + ("",) * 3) try: download_from_secure_id( query["id"][0], query["brand"][0], base_dir, base_url ) except ValueError as e: logging.error(e) continue continue for attach in msg.attachments: fname = attach.name if (counterparty == "CS") and not ("Interest" in fname): continue p = base_dir / fname if not p.parent.exists(): p.parent.mkdir(parents=True) if not p.exists(): p.write_bytes(attach.content) def get_CS(g): for e in list(g)[-1]: if "This interest, margin" in e.text: if value == "No Accruals to Report": return 0 return float(value) value = e.text def get_BNP(g): for e in g: if "Due to" in e.text: value = next(g).text.replace(",", "") return -float(value) def get_CITI(path): df = pd.read_excel(path) for row in df.itertuples(): if "Net Interest Due To CP" in row: return -row._6 def get_GS(g): for e in g: if "due to" in e.text: return float(next(g).text.replace("USD", "").replace(",", "")) def get_MS(path): df = pd.read_excel(path) return -round(df["LOCAL_ACCRUAL"].sum(), 2) def get_BAML(g): for e in g: if "Net interest Amount" in e.text: return -float(next(g).text.replace("(", "-").replace(")", "")) def get_JPM(g): for e in g: if "Page" in e.text: return float(value.replace(",", "")) value = e.text def start_end(date): date = date - relativedelta(months=1) start = datetime.date(date.year, date.month, 1) end = start + relativedelta(months=1) end -= datetime.timedelta(days=1) return start, end def get_interest(counterparties, save=False): interest_amounts = defaultdict(float) for cp in counterparties: try: func = globals()[f"get_{cp}"] except KeyError: print(f"Missing cp {cp}") if cp in ("CITI", "MS"): for file in Path( f"/home/serenitas/Daily/Serenitas/MonthlyInterest/{cp}_reports" ).glob("*.xls*"): amount = func(file) interest_amounts[cp] = interest_amounts[cp] + amount else: for file in Path( f"/home/serenitas/Daily/Serenitas/MonthlyInterest/{cp}_reports" ).glob("*.pdf"): g = iter(load_pdf(file, pages=True if cp == "CS" else False)) amount = func(g) interest_amounts[cp] = interest_amounts[cp] + amount return pd.DataFrame(interest_amounts, index=[0]).T.rename( index={"BAML": "BAML_ISDA"}, columns={0: "monthly_statement"} ) if __name__ == "__main__": em = ExchangeMessage() counterparties = ["BNP", "CITI", "CS", "GS", "MS", "BAML", "JPM"] # First run --save, then run --accept if it looks good parser = argparse.ArgumentParser(description="Save, accept within tolerances") parser.add_argument( "date", type=datetime.date.fromisoformat, nargs="?", default=datetime.date.today(), ) parser.add_argument( "--save", action="store_true", default=False, help="store in proper folders", ) parser.add_argument( "--accept", action="store_true", default=False, help="accept the differences are within tolerance and edit the csv accordingly", ) args = parser.parse_args() start = datetime.date(args.date.year, args.date.month, 1) for cp in counterparties: download_messages(em, cp, start, args.date, args.save) interest_amounts = get_interest(counterparties, save=args.save) start, end = start_end(args.date) global df df = pd.merge( export_data(start, end).groupby("broker").sum(), interest_amounts, how="outer", left_index=True, right_index=True, ) df["difference"] = df["amount"] - df["monthly_statement"] if args.accept: df = export_data(start, end) for k, v in df["difference"].items(): df.loc[k].loc["CSH_CASH"] -= v from io import StringIO from serenitas.utils.exchange import ExchangeMessage, FileAttachment buf = StringIO() df.to_csv(buf) attach = [ FileAttachment( name=f"{start:%b}.csv", content=buf.getvalue().encode(), ) ] em = ExchangeMessage() em.send_email( subject=f"Allocation of Interest for {start:%b}", body="Please see attached", to_recipients=("serenitas.otc@sscinc.com", "SERENITAS.ops@sscinc.com"), cc_recipients=("nyops@lmcg.com",), attach=attach, )