diff options
Diffstat (limited to 'utils/payoff.py')
| -rw-r--r-- | utils/payoff.py | 237 |
1 files changed, 165 insertions, 72 deletions
diff --git a/utils/payoff.py b/utils/payoff.py index fffbdba..7edeb87 100644 --- a/utils/payoff.py +++ b/utils/payoff.py @@ -8,21 +8,36 @@ Payoff of an option. """ from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import List +from typing import AnyStr, List, Union, Sequence from utils.enums import OptionRight, BarrierUpDown, BarrierInOut +from utils.misc import Number -__all__ = ['BasePayoff', 'VanillaPayOff', 'AsianArithmeticPayOff', - 'DiscreteBarrierPayOff'] +__all__ = [ + 'BasePayoff', + 'VanillaPayOff', + 'AsianArithmeticPayOff', + 'DiscreteBarrierPayOff' +] -@dataclass class BasePayoff(ABC): """Base class for calculating the payoff.""" - K: float - option_right: (str, OptionRight) + + def __init__(self, K: Number, + option_right: Union[AnyStr, OptionRight]) -> None: + self.K: Number = K + # https://github.com/python/mypy/issues/3004 + self.option_right = option_right # type: ignore + + def __repr__(self) -> str: + return ( + f'{self.__class__.__name__}(' + f'K={self.K!r}, ' + f'option_right={self.option_right!r}' + ')' + ) @property def option_right(self) -> OptionRight: @@ -30,7 +45,7 @@ class BasePayoff(ABC): return self._option_right @option_right.setter - def option_right(self, val: (str, OptionRight)) -> None: + def option_right(self, val: Union[AnyStr, OptionRight]) -> None: """Set the option_right of the option.""" if isinstance(val, str): if not hasattr(OptionRight, val): @@ -44,17 +59,43 @@ class BasePayoff(ABC): f'Expected str or OptionRight, instead got type {type(val)}!' ) - def _calculate_call(self, S: float) -> float: - """Call option.""" - return max(S - self.K, 0.) + def _calculate_call(self, S: Number) -> float: + """ + Price call option for a given underlying price. + + Parameters + ---------- + S : Number + Price of underlying. + + Returns + ------- + float + Price of the call option. + + """ + return float(max(S - self.K, 0.0)) + + def _calculate_put(self, S: Number) -> float: + """ + Price put option for a given underlying price. + + Parameters + ---------- + S : Number + Price of underlying. - def _calculate_put(self, S: float) -> float: - """Put option.""" - return max(self.K - S, 0.) + Returns + ------- + float + Price of the put option. + + """ + return float(max(self.K - S, 0.0)) @abstractmethod - def calculate(self, S: float) -> float: - """Calulate the payoff for a given spot.""" + def calculate(self, S: Sequence[Number]) -> float: + """Calulate the payoff for a given path.""" class VanillaPayOff(BasePayoff): @@ -63,46 +104,56 @@ class VanillaPayOff(BasePayoff): Attributes ---------- - option_right : Right of the option. - K : Strike price. + option_right : OptionRight + Right of the option. + K : Number + Strike price. Methods - ---------- + ------- calculate(S) - Calulate the payoff given a spot price. + Calulate the payoff for a given path. Examples - ---------- + -------- >>> from utils.payoff import VanillaPayOff >>> payoff = VanillaPayOff(option_right='Call', K=150.) - >>> print(payoff.calculate(160.)) - 10.0 + >>> print(payoff) + VanillaPayOff(K=150.0, option_right=Call) """ - def calculate(self, S: (float, List[float])) -> float: + def calculate(self, S: Sequence[Number]) -> float: """ - Calulate the payoff given a spot price. + Calulate the payoff for a given path. Parameters ---------- - S : Spot price or list of spot prices. + S : Sequence of Numbers + Set of prices for the underlying {S_t1, S_t2, ..., S_tn}. Returns - ---------- - payoff : Payoff. + ------- + payoff : float + Payoff. Notes - ---------- - If a list is given as input, the final entry will be taken to evaluate. + ----- + The final entry will be taken to evaluate the payoff. + + Examples + -------- + >>> from utils.payoff import VanillaPayOff + >>> payoff = VanillaPayOff(option_right='Call', K=150.) + >>> print(payoff.calculate([160.])) + 10.0 """ - if not isinstance(S, float): - S = S[-1] + # Calculate payoff using final price if self.option_right == OptionRight.Call: - payoff = self._calculate_call(S) + payoff = self._calculate_call(S[-1]) else: - payoff = self._calculate_put(S) + payoff = self._calculate_put(S[-1]) return payoff @@ -112,34 +163,45 @@ class AsianArithmeticPayOff(BasePayoff): Attributes ---------- - option_right : Right of the option. - K : Strike price. + option_right : OptionRight + Right of the option. + K : Number + Strike price. Methods - ---------- + ------- calculate(S) Calulate the payoff given a set of prices for the underlying. Examples - ---------- + -------- >>> from utils.payoff import AsianArithmeticPayOff >>> payoff = AsianArithmeticPayOff(option_right='Call', K=150) - >>> print(payoff.calculate([140, 150, 160, 170, 180])) - 10.0 + >>> print(payoff) + AsianArithmeticPayOff(K=150, option_right=Call) """ - def calculate(self, S: List[float]) -> float: + def calculate(self, S: Sequence[Number]) -> float: """ Calulate the payoff given a set of prices for the underlying. Parameters ---------- - S : Set of prices for the underlying {S_t1, S_t2, ..., S_tn}. + S : Sequence of Numbers + Set of prices for the underlying {S_t1, S_t2, ..., S_tn}. Returns - ---------- - payoff : Payoff. + ------- + payoff : float + Payoff. + + Examples + -------- + >>> from utils.payoff import AsianArithmeticPayOff + >>> payoff = AsianArithmeticPayOff(option_right='Call', K=150) + >>> print(payoff.calculate([140, 150, 160, 170, 180])) + 10.0 """ avg_sum = sum(S) / len(S) @@ -150,7 +212,6 @@ class AsianArithmeticPayOff(BasePayoff): return payoff -@dataclass(init=False) class DiscreteBarrierPayOff(BasePayoff): """ Class for calculating the payoff of a discrete barrier European style @@ -158,39 +219,56 @@ class DiscreteBarrierPayOff(BasePayoff): Attributes ---------- - option_right : Right of the option. - K : Strike price. - B : Barrier price. - barrier_updown : Up or down type barrier option. - barrier_inout : In or out type barrier option. + option_right : OptionRight + Right of the option. + K : Number + Strike price. + B : Number + Barrier price. + barrier_updown : BarrierUpDown + Up or down type barrier option. + barrier_inout : BarrierInOut + In or out type barrier option. Methods - ---------- + ------- calculate(S) Calulate the payoff given a set of prices for the underlying. Examples - ---------- + -------- >>> from utils.payoff import DiscreteBarrierPayOff - >>> payoff = DiscreteBarrierPayOff(option_right='Call', K=100, B=90, + >>> payoff = DiscreteBarrierPayOff(option_right='Call', K=100, B=90, \ barrier_updown='Down', barrier_inout='Out') - >>> print(payoff.calculate([100., 110., 120.])) - 20.0 - >>> print(payoff.calculate([100., 110., 120., 80., 110.])) - 0.0 + >>> print(payoff) + DiscreteBarrierPayOff(K=100, option_right=Call, B=90, barrier_updown=Down, barrier_inout=Out) """ - B: float - barrier_updown: (str, BarrierUpDown) - barrier_inout: (str, BarrierInOut) - def __init__(self, option_right: (str, OptionRight), K: float, B: float, - barrier_updown: (str, BarrierUpDown), - barrier_inout: (str, BarrierInOut)): + def __init__( + self, + option_right: Union[AnyStr, OptionRight], + K: Number, + B: Number, + barrier_updown: Union[AnyStr, BarrierUpDown], + barrier_inout: Union[AnyStr, BarrierInOut] + ) -> None: super().__init__(K, option_right) - self.B = B - self.barrier_updown = barrier_updown - self.barrier_inout = barrier_inout + self.B: Number = B + # https://github.com/python/mypy/issues/3004 + self.barrier_updown = barrier_updown # type: ignore + self.barrier_inout = barrier_inout # type: ignore + + def __repr__(self) -> str: + return ( + f'{self.__class__.__name__}(' + f'K={self.K!r}, ' + f'option_right={self.option_right!r}, ' + f'B={self.B!r}, ' + f'barrier_updown={self.barrier_updown!r}, ' + f'barrier_inout={self.barrier_inout!r}' + ')' + ) @property def barrier_updown(self) -> BarrierUpDown: @@ -198,7 +276,7 @@ class DiscreteBarrierPayOff(BasePayoff): return self._barrier_updown @barrier_updown.setter - def barrier_updown(self, val: (str, BarrierUpDown)) -> None: + def barrier_updown(self, val: Union[AnyStr, BarrierUpDown]) -> None: """Set either up or down type barrier option.""" if isinstance(val, str): if not hasattr(BarrierUpDown, val): @@ -218,7 +296,7 @@ class DiscreteBarrierPayOff(BasePayoff): return self._barrier_inout @barrier_inout.setter - def barrier_inout(self, val: (str, BarrierInOut)) -> None: + def barrier_inout(self, val: Union[AnyStr, BarrierInOut]) -> None: """Set either up or down type barrier option.""" if isinstance(val, str): if not hasattr(BarrierInOut, val): @@ -232,32 +310,47 @@ class DiscreteBarrierPayOff(BasePayoff): f'Expected str or BarrierInOut, instead got type {type(val)}!' ) - def calculate(self, S: List[float]) -> float: + def calculate(self, S: Sequence[Number]) -> float: """ Calulate the payoff given a set of prices for the underlying. Parameters ---------- - S : Set of prices for the underlying {S_t1, S_t2, ..., S_tn}. + S : Sequence of Numbers + Set of prices for the underlying {S_t1, S_t2, ..., S_tn}. Returns - ---------- - payoff : Payoff. + ------- + payoff : float + Payoff. + + Examples + -------- + >>> from utils.payoff import DiscreteBarrierPayOff + >>> payoff = DiscreteBarrierPayOff(option_right='Call', K=100, B=90, \ + barrier_updown='Down', barrier_inout='Out') + >>> print(payoff.calculate([100., 110., 120.])) + 20.0 + >>> print(payoff.calculate([100., 110., 120., 80., 110.])) + 0.0 """ # Calculate the heavyside + H: List[int] if self.barrier_updown == BarrierUpDown.Up: H = [1 if self.B - x > 0 else 0 for x in S] else: H = [1 if x - self.B > 0 else 0 for x in S] # Calculate whether it has been activated + activation: int if self.barrier_inout == BarrierInOut.In: activation = 1 if min(H) == 0 else 0 else: activation = min(H) # Calculate payoff using final price + payoff: float if self.option_right == OptionRight.Call: payoff = activation * self._calculate_call(S[-1]) else: |
