aboutsummaryrefslogtreecommitdiffstats
path: root/python/report_ops
diff options
context:
space:
mode:
Diffstat (limited to 'python/report_ops')
-rw-r--r--python/report_ops/__main__.py49
-rw-r--r--python/report_ops/base.py43
-rw-r--r--python/report_ops/cash.py63
-rw-r--r--python/report_ops/wires.py6
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