diff options
Diffstat (limited to 'python/report_ops')
| -rw-r--r-- | python/report_ops/__main__.py | 49 | ||||
| -rw-r--r-- | python/report_ops/base.py | 43 | ||||
| -rw-r--r-- | python/report_ops/cash.py | 63 | ||||
| -rw-r--r-- | python/report_ops/wires.py | 6 |
4 files changed, 68 insertions, 93 deletions
diff --git a/python/report_ops/__main__.py b/python/report_ops/__main__.py index 4372137a..4124ef08 100644 --- a/python/report_ops/__main__.py +++ b/python/report_ops/__main__.py @@ -83,31 +83,36 @@ if args.sma_positions: if args.cash_reports or args.wire_reports: em = ExchangeMessage() + if args.cash_reports: + Report = CashReport + else: + Report = WireReport for fund, custodians in _fund_custodians.items(): for custodian in custodians: get_custodian_download_fun(custodian)(args.date, fund, em=em) - if args.cash_reports: - cash_report = CashReport[custodian] - try: - for row in cash_report.yield_rows(args.date, fund): - cash = cash_report.from_report_line( - row | {"fund": fund, "knowledge_date": args.date} - ) - cash.stage() - cash_report.commit() - cash_report.clear() - except (MissingDataError, RuntimeError) as e: - logger.warning(e) - if args.wire_reports: - wire_report = WireReport[custodian] - try: - for row in wire_report.yield_rows(args.date, fund): - wire = wire_report.from_report_line(row | {"fund": fund}) - wire.stage() - wire_report.commit() - wire_report.clear() - except (MissingDataError, RuntimeError) as e: - logger.warning(e) + report = Report[custodian](args.date, fund) + report.commit() + # cash_report = CashReport[custodian] + # try: + # for row in cash_report.yield_rows(args.date, fund): + # cash = cash_report.from_report_line( + # row | {"fund": fund, "knowledge_date": args.date} + # ) + # cash.stage() + # cash_report.commit() + # cash_report.clear() + # except (MissingDataError, RuntimeError) as e: + # logger.warning(e) + # if args.wire_reports: + # wire_report = WireReport[custodian] + # try: + # for row in wire_report.yield_rows(args.date, fund): + # wire = wire_report.from_report_line(row | {"fund": fund}) + # wire.stage() + # wire_report.commit() + # wire_report.clear() + # except (MissingDataError, RuntimeError) as e: + # logger.warning(e) if args.isosel_reports: for fund in ("ISOSEL",): diff --git a/python/report_ops/base.py b/python/report_ops/base.py index 367faf50..7517591b 100644 --- a/python/report_ops/base.py +++ b/python/report_ops/base.py @@ -1,43 +1,22 @@ from typing import ClassVar -from dataclasses import Field from serenitas.utils.db2 import dbconn class Report: _sql_insert: ClassVar[str] - _insert_queue: ClassVar[list] - _insert_fields: ClassVar[list] _conn: ClassVar = dbconn("dawndb") + _registry: ClassVar[dict] = {} - def __init_subclass__(cls, table_name: str): - cls._insert_fields = list(filter(cls.is_insert_field, cls.__annotations__)) - place_holders = ",".join(["%s"] * len(cls._insert_fields)) - cls._sql_insert = f"INSERT INTO {table_name}({','.join(cls._insert_fields)}) VALUES({place_holders}) ON CONFLICT DO NOTHING RETURNING *" - cls._insert_queue = [] + def __init_subclass__(cls, table_name: str, columns: tuple[str], **kwargs): + super().__init_subclass__(**kwargs) + place_holders = ",".join(["%s"] * len(columns)) + cls._sql_insert = f"INSERT INTO {table_name}({','.join(columns)}) VALUES({place_holders}) ON CONFLICT DO NOTHING RETURNING *" - @classmethod - def is_insert_field(cls, attr): - """fields are insertable by default unless specified otherwise""" - match getattr(cls, attr, None): - case Field(metadata={"insert": False}) | Field(init=False): - return False - case _: - return True + def __class_getitem__(cls, key): + return cls._registry[key] - @classmethod - def clear(cls): - cls._insert_queue.clear() - - def stage(self): - self._insert_queue.append( - tuple([getattr(self, col) for col in self._insert_fields]) - ) - - @classmethod - def commit(cls): - conn = cls._conn - with conn.cursor() as c: - c.executemany(cls._sql_insert, cls._insert_queue) - conn.commit() - cls._insert_queue.clear() + def commit(self): + with self._conn.cursor() as c: + c.executemany(self._sql_insert, self) + self._conn.commit() diff --git a/python/report_ops/cash.py b/python/report_ops/cash.py index 552058a8..2742e1f7 100644 --- a/python/report_ops/cash.py +++ b/python/report_ops/cash.py @@ -1,11 +1,10 @@ import datetime from typing import ClassVar -from dataclasses import dataclass, field +from dataclasses import dataclass import re import pandas as pd from serenitas.utils.env import DAILY_DIR -from serenitas.utils.db2 import dbconn from serenitas.analytics.dates import prev_business_day from serenitas.analytics.exceptions import MissingDataError from serenitas.ops.trade_dataclasses import Ccy @@ -16,43 +15,44 @@ from .base import Report @dataclass -class CashReport(Report, table_name="cash_balances"): +class CashReport( + Report, + table_name="cash_balances", + columns=( + "date", + "fund", + "account_name", + "account_number", + "currency_code", + "balance", + ), +): date: datetime.date fund: Fund - account_name: str - account_number: str - currency_code: Ccy - balance: float - custodian: ClassVar[Custodian] = field(metadata={"insert": False}) - _registry: ClassVar = field(default={}, metadata={"insert": False}) + custodian: ClassVar[Custodian] - def __init_subclass__(cls, custodian): + def __init_subclass__(cls, custodian, **kwargs): cls.custodian = custodian cls._registry[custodian] = cls - def __class_getitem__(cls, key): - return cls._registry[key] - - @classmethod - def get_report(cls, knowledge_date, fund): - report_dir = get_dir(knowledge_date) - pattern = f"{cls.custodian}_CASH_{fund}_" + def get_report(self): + report_dir = get_dir(self.date) + pattern = f"{self.custodian}_CASH_{self.fund}_" reports = [ f for f in report_dir.iterdir() - if f.name.startswith(pattern) - and cls.get_ts(f.name).date() == knowledge_date + if f.name.startswith(pattern) and self.get_ts(f.name).date() == self.date ] p = max( reports, - key=lambda f: cls.get_ts(f.name), + key=lambda f: self.get_ts(f.name), default=None, ) if p: return p else: raise MissingDataError( - f"Report not ready {knowledge_date}: {cls.custodian} {fund}" + f"Report not ready {self.date}: {self.custodian} {self.fund}" ) @staticmethod @@ -62,21 +62,14 @@ class CashReport(Report, table_name="cash_balances"): class NTCashReport(CashReport, custodian="NT"): - @classmethod - def yield_rows(cls, knowledge_date, fund): - df = pd.read_csv(cls.get_report(knowledge_date, fund), on_bad_lines="warn") + def __iter__(self): + df = pd.read_csv(self.get_report(), on_bad_lines="warn") df = df[df["T-NARR-LONG"] == "CLOSING BALANCE"] - yield from df.to_dict(orient="records") - - @classmethod - def from_report_line(cls, d): - return cls( - date=prev_business_day(d["knowledge_date"]), - fund=d["fund"], - account_name=d["Consolidation"], - account_number=d["Account Number"], - currency_code=d["Currency code"], - balance=d["A-TRAN-AMT"], + return ( + (prev_business_day(self.date), self.fund, *t) + for t in df[ + ["Consolidation", "Account Number", "Currency code", "A-TRAN-AMT"] + ].itertuples(index=False) ) diff --git a/python/report_ops/wires.py b/python/report_ops/wires.py index 6da3ac29..1ecfb99a 100644 --- a/python/report_ops/wires.py +++ b/python/report_ops/wires.py @@ -2,7 +2,7 @@ import datetime from csv import DictReader from typing import ClassVar from functools import partial -from dataclasses import dataclass, field, Field +from dataclasses import dataclass import pandas as pd from serenitas.ops.trade_dataclasses import Ccy @@ -10,7 +10,6 @@ from serenitas.ops.dataclass_mapping import Fund from serenitas.analytics.dates import prev_business_day from serenitas.analytics.exceptions import MissingDataError from serenitas.utils.env import DAILY_DIR -from serenitas.utils.db2 import dbconn from .misc import get_dir, dt_from_fname, Custodian from .base import Report @@ -28,8 +27,7 @@ class WireReport(Report, table_name="custodian_wires"): amount: float wire_details: str unique_ref: str - dtkey: ClassVar = field(metadata={"insert": False}) - _registry: ClassVar = field(default={}, metadata={"insert": False}) + dtkey: ClassVar def __init_subclass__(cls, custodian, dtkey): cls.custodian = custodian |
