aboutsummaryrefslogtreecommitdiffstats
path: root/utils/payoff.py
diff options
context:
space:
mode:
authorShivesh Mandalia <shivesh.mandalia@outlook.com>2020-04-20 00:03:47 +0100
committerShivesh Mandalia <shivesh.mandalia@outlook.com>2020-04-20 00:03:47 +0100
commit1c8649fdcd9f56cca5b191ae3cbaec4977569380 (patch)
tree14299d85de895df774a5c2b5b44ee3d10e0f5f7f /utils/payoff.py
parent92375e2d232538c72e9dfe7d6f067dfcc7e5979f (diff)
downloadMCOptionPricing-1c8649fdcd9f56cca5b191ae3cbaec4977569380.tar.gz
MCOptionPricing-1c8649fdcd9f56cca5b191ae3cbaec4977569380.zip
Linting, type checking
Diffstat (limited to 'utils/payoff.py')
-rw-r--r--utils/payoff.py237
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: