I have a class that has two mutually exclusive arguments (prices
and returns
). That is, they must not be both provided in order to instantiate an object.
However, the class needs both for internal computations. So I want to compute the missing pd.Series
from the one provided by the user.
I created two alternative class constructors (from_prices
and from_returns
). Using these constructors, the class will be correctly instantiated.
Here's the code. It makes use of the attrs
library (www.attrs.org).
import pandas as pd
import attr
@attr.s
class MutuallyExclusive:
prices: pd.Series = attr.ib()
returns: pd.Series = attr.ib()
trading_days_per_year: int = attr.ib(default=252)
@classmethod
def from_prices(cls, price_series: pd.Series, trading_days: int = 252):
return cls(
price_series,
price_series.pct_change(),
trading_days,
)
@classmethod
def from_returns(cls, return_series: pd.Series):
return cls(
pd.Series(data=100 + 100 * (returns.add(1).cumprod() - 1)),
return_series,
)
if __name__ == "__main__":
prices = pd.Series(data=[100, 101, 98, 104, 102, 108])
returns = pd.Series(data=[0.01, 0.03, -0.02, 0.01, -0.03, 0.04])
obj_returns = MutuallyExclusive.from_returns(returns)
obj_prices = MutuallyExclusive.from_prices(prices, trading_days=100)
However, the user could still call obj = MutuallyExclusive(prices, returns)
, eventhough these two series are not compatible to each other. What's the best way to catch that situation and throw an error?
EDIT:
Would it be possible to "disable" the regular constructor alltogether? If it would be possible to instantiate the object via alternative constructors only, this would solve the problem, wouldn't it?