from enum import Enum
from dataclasses import dataclass
from datetime import date
from typing import Any, Dict, List, Optional
from uuid import UUID
STD_DATE_FMT = '%Y-%m-%d'
STD_DATETIME_FMT = '%Y-%m-%d %H:%M:%S.%f'
[docs]
class Portfolio:
"""
Simple value object that can be used as an input for risk attribution, VaR calculations
and other client functions that require a portfolio as input. In general a user provides
the desired portfolio composition in their chosen symbology and then the AssetMoster object
can be used to do cross-referencing between multiple symbologies and Serenity's internal
asset ID: UUID values. This lets users work with easy-to-understand portfolio definitions
while making it a one-liner to translate for Serenity.
"""
def __init__(self, assets: Dict[UUID, float]):
"""
:param assets: a mapping from asset ID to qty, where negative qty is taken as a short position
"""
self.assets = assets
[docs]
def get_assets(self) -> Dict[UUID, float]:
"""
Gets the underlying map of asset ID to qty.
"""
return self.assets
[docs]
def to_asset_positions(self) -> List[Dict[str, Any]]:
"""
Internal helper that converts a Portfolio to the preferred format for risk attribution,
VaR compution, VaR backtest, etc.
"""
return [{'assetId': str(asset_id), 'quantity': qty} for (asset_id, qty) in self.assets.items()]
[docs]
class MarkTime(Enum):
"""
Snapshot time to use for daily close price purposes; as crypto is a 24x7 market users can
choose their preferred closing time for marking books. Note that UTC will not be supported
until the next release.
"""
NY_EOD = 'NY_EOD'
"""
Prices as of 4:30PM New York-local time
"""
LN_EOD = 'LN_EOD'
"""
Prices as of 4:30PM London-local time
"""
HK_EOD = 'HK_EOD'
"""
Prices as of 4:00PM Hong Kong-local time
"""
UTC = 'UTC'
"""
Prices as of UTC midnight
"""
[docs]
class CashTreatment(Enum):
"""
How should the portfolio valuator treat stablecoins? Like cash, or tokens? If CashTreatment
is FIAT_PEGGED_STABLECOINS, it will group together USD and USD-pegged stablecoins as "cash."
"""
FIAT_PEGGED_STABLECOINS = 'FIAT_PEGGED_STABLECOINS'
"""
Treat fiat-pegged stablecoins like cash
"""
FIAT_ONLY = 'FIAT_ONLY'
"""
Only treat fiat currencies as cash
"""
[docs]
@dataclass
class CalculationContext:
"""
Parameter object that groups together the common inputs for risk calculations. Everything
gets defaulted, so you need only populate any overrides.
"""
as_of_date: Optional[date] = None
"""
The effective date for all data loaded from Serenity's bitemporal database
"""
model_config_id: Optional[UUID] = None
"""
The factor risk or VaR model to use for calculations or when loading pre-computed matrices and other results
"""
mark_time: MarkTime = MarkTime.NY_EOD
"""
The mark time to use by convention for "close" in the 24x7 digital asset markets
"""
base_currency_id: Optional[UUID] = None
"""
The currency to use to expresss the value of portfolios, positions and exposures; in current version only USD is
supported but later on can be any asset
"""
[docs]
@dataclass
class PricingContext:
"""
Parameter object that groups together the common inputs for valuation. Everything
gets defaulted, so you need only populate any overrides.
"""
as_of_date: Optional[date] = None
"""
The effective date for all data loaded from Serenity's bitemporal database
"""
mark_time: MarkTime = MarkTime.NY_EOD
"""
The mark time to use by convention for "close" in the 24x7 digital asset markets
"""
cash_treatment: CashTreatment = CashTreatment.FIAT_ONLY
"""
How the valuation logic should define "cash position"
"""
base_currency_id: Optional[UUID] = None
"""
The currency to use to expresss the value of portfolios, positions and exposures; in current version only USD is
supported but later on can be any asset
"""
[docs]
@dataclass
class SectorPath:
sector_levels: List[str]
[docs]
def create_lookup_key(self, leaf_name: str):
"""
Helper function that joins the path segments with a terminal
node like an asset ID or a factor name. This gives you a unique
key for building tables that are indexed by these tuples.
"""
return f'{str(self)}/{leaf_name}'
def __str__(self) -> str:
return '/'.join(self.sector_levels)
def __hash__(self) -> int:
return hash(self.__str__())