from dataclasses import field, dataclass from serenitas.ops.trade_dataclasses import Deal, Fund from serenitas.analytics.dates import prev_business_day, next_business_day import datetime import gpg from serenitas.utils.exchange import ExchangeMessage from serenitas.utils.env import DAILY_DIR import pandas as pd from serenitas.utils.db import dbconn, dawn_engine from typing import ClassVar from .misc import get_dir def dt_from_fname(f): return datetime.datetime.strptime( f.name.removesuffix(".csv").rsplit("_")[-1], "%Y%m%d%H%M" ) @dataclass class IAMDeal(Deal, deal_type=None, table_name="iam_tickets"): trade_date: datetime.date = field(metadata={"globeop": "SettlementDate"}) action: str = field(metadata={"globeop": "Action"}) strategy: str = field(metadata={"globeop": "Folder"}) counterparty: str = field(metadata={"globeop": "Counterparty"}) maturity: datetime.date start_money: float = field(metadata={"globeop": "StartMoney"}) currency: str = field(metadata={"globeop": "Currency"}) booked_offset: bool uploaded: bool fund: Fund dealid: str = field(metadata={"insert": False}) id: int = field(metadata={"insert": False}) def to_globeop(self, action): obj = super().to_globeop(action) obj["Deal Type"] = "IamDeal" obj["ExpirationDate"] = self.trade_date if self.action == "UPDATE" else None obj["CallNoticeIndicator"] = "24H" if self.action == "NEW" else None obj["TransactionIndicator"] = ("DEPOSIT" if obj["StartMoney"] > 0 else "LOAN",) obj["StartMoney"] = abs(obj["StartMoney"]) obj["Folder"] = ( "M_CSH_CASH" if obj["strategy"] == "CSH_CASH" else obj["strategy"] ) obj["DealFunction"] = "OTC" obj["MarginType"] = "Variation" obj["Basis"] = "ACT/360" return obj @dataclass class CashReport: fund: ClassVar[str] account_number: ClassVar[str] date: datetime.date _conn: ClassVar[dbconn] = dbconn("dawndb") def __init_subclass__(cls, fund, account_number): cls.fund = fund cls.account_number = account_number class IsoselCashReport(CashReport, fund="ISOSEL", account_number="ISOS01"): @classmethod def download_reports(cls, date=datetime.date.today()): em = ExchangeMessage() for msg in em.get_msgs(path=["SeleneOps", "Passport"]): for attach in msg.attachments: message_time = attach.last_modified_time if attach.name == "Attachment1.pgp" and message_time.date() == date: dest = get_dir(message_time.date(), archived=False) dest.mkdir(exist_ok=True, parents=True) with attach.fp as fp: plaintext, result, verify_result = gpg.Context().decrypt( fp.read(), passphrase="Serenitas1" ) fname = ( "custodian_wires" if "custodian" in verify_result.file_name else "cash" ) dest = dest / f"{fname}_{message_time:%Y%m%d%H%M}.csv" with open(dest, "w") as csvFile: text = plaintext.decode("utf-8").replace("\t", ",") csvFile.write(text) def to_db(self): self.download_reports(self.date) report_dir = get_dir(next_business_day(self.date)) report_dir.mkdir(exist_ok=True, parents=True) p = max( [ f for f in get_dir(next_business_day(self.date)).iterdir() if "cash" in f.name ], key=dt_from_fname, default=None, ) if not p: raise ValueError( f"No reports found for fund: {self.fund} date: {self.date}" ) df = pd.read_csv(p, on_bad_lines="warn") df = df[df["T-NARR-LONG"] == "CLOSING BALANCE"] df = df[["Consolidation", "Currency code", "A-TRAN-AMT", "Account name"]] df = df.reset_index(drop=True).rename( { "Consolidation": "account_number", "Currency code": "currency_code", "Account name": "account_name", "A-TRAN-AMT": "balance", }, axis=1, ) df["date"] = self.date df["fund"] = self.fund with self._conn.cursor() as c: c.execute( "DELETE FROM cash_balances WHERE fund=%s AND date=%s AND account_number=%s", ( self.fund, self.date, self.account_number, ), ) self._conn.commit() df.to_sql("cash_balances", index=False, if_exists="append", con=dawn_engine)