from serenitas.ops.trade_dataclasses import Deal from dataclasses import dataclass import datetime from typing import Literal from serenitas.utils.db2 import dbconn firmness = Literal["FIRM", "INDICATIVE"] def maturity_dt(d): try: return datetime.date( int(d["maturityyear"]), int(d["maturitymonth"]), int(d["maturityday"]) ) except ( ValueError, KeyError, ): # Sometimes maturity isn't included but we still have tenor return class MarkitQuoteKind: def __class_getitem__(cls, quote_type: str): match quote_type: case "CD": return SingleNameQuote case "ABS": return BondQuote case "TRS": return TRSQuote class MarkitQuote(Deal, table_name=None, deal_type=None): def __init_subclass__(cls, deal_type, table_name: str, **kwargs): super().__init_subclass__(deal_type=deal_type, table_name=table_name, **kwargs) cls._sql_insert = cls._sql_insert.replace( "RETURNING *", "ON CONFLICT (quoteid) DO NOTHING RETURNING *" ) cls.init_dbconn(dbconn("serenitasdb")) @classmethod def from_markit_line(cls, d): base_attributes = { "msg_id": d["message"]["id"], "quotedate": datetime.datetime.fromtimestamp(d["receiveddatetime"] / 1000), "quotesource": d["sourceshortname"], } return base_attributes @classmethod def clear(cls): cls._insert_queue.clear() @classmethod def already_uploaded(cls): with cls._conn.cursor() as c: c.execute(f"SELECT distinct msg_id as msg_id FROM {cls._table_name}") return set(row.msg_id for row in c) # TODO # @property # def message(self): # return QuoteDetails.from_tradeid(self.msg_id) @dataclass class SingleNameQuote( MarkitQuote, table_name="markit_singlename_quotes", deal_type=None ): quoteid: int msg_id: str quotesource: str confidence: int redcode: str = None ticker: str = None maturity: datetime.date = None tenor: int = None runningcoupon: int = None bidconventionalspread: float = None bidupfront: float = None bidsize: float = None askconventionalspread: float = None askupfront: float = None asksize: float = None firmness: firmness = None quotedate: datetime.datetime = None @classmethod def from_markit_line(cls, d): base_attributes = super().from_markit_line(d) additional_attributes = { "maturity": maturity_dt(d), "tenor": f"{d['tenor']}Y", } d.update(base_attributes | additional_attributes) return cls.from_dict(**d) @dataclass class BondQuote(MarkitQuote, table_name="markit_bond_quotes", deal_type=None): quoteid: int msg_id: str quotesource: str confidence: int identifier: str = None cusip: str = None bidprice: float = None bidsize: float = None askprice: float = None asksize: float = None pricelevel: float = None subtype: str = None quotetype: str = None firmness: firmness = None quotedate: datetime.datetime = None @classmethod def from_markit_line(cls, d): base_attributes = super().from_markit_line(d) additional_attributes = { "identifier": d["internalinstrumentidentifier"], "pricelevel": d.get("pricelevelnormalized"), } d.update(base_attributes | additional_attributes) return cls.from_dict(**d) @dataclass class TRSQuote(MarkitQuote, table_name="markit_trs_quotes", deal_type=None): quoteid: int msg_id: str quotesource: str confidence: int maturity: datetime.date identifier: str = None bidlevel: float = None asklevel: float = None nav: float = None ref: float = None firmness: firmness = None funding_benchmark: str = None quotedate: datetime.datetime = None @classmethod def from_markit_line(cls, d): base_attributes = super().from_markit_line(d) additional_attributes = { "identifier": d["ticker"], "ref": d.get("reference"), "nav": d.get("inavparsed"), "funding_benchmark": d.get("parsedbenchmark"), "maturity": maturity_dt(d), } d.update(base_attributes | additional_attributes) return cls.from_dict(**d)