1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# author : S. Mandalia
# shivesh.mandalia@outlook.com
#
# date : March 19, 2020
"""
Pricing engine for exotic options.
"""
import math
from statistics import mean, stdev
from dataclasses import dataclass
from typing import List
from utils.path import PathGenerator
from utils.payoff import BasePayoff
__all__ = ['MCResult', 'PricingEngine']
@dataclass
class MCResult:
"""
Price of option along with its MC error.
"""
price: float
stderr: float
@dataclass
class PricingEngine:
"""
Class for generating underlying prices using MC techniques.
Attributes
----------
payoff : Payoff object for calculating the options payoff.
path : PathGenerator object for generating the evolution of the underlying.
Methods
----------
price()
Examples
----------
>>> from utils.engine import PricingEngine
>>> from utils.path import PathGenerator
>>> from utils.payoff import AsianArithmeticPayOff
>>> path = PathGenerator(S=100., r=0.1, div=0.01, vol=0.3)
>>> payoff = AsianArithmeticPayOff(option_right='Call', K=110)
>>> engine = PricingEngine(payoff=payoff, path=path)
>>> print(engine.price(T=range(4)))
MCResult(price=12.003704847790525, stderr=0.2327352760696234)
"""
payoff: BasePayoff
path: PathGenerator
def price(self, T: List[float], ntrials: int = 1E4,
antithetic: bool = True) -> MCResult:
"""
Price the option using MC techniques.
Parameters
----------
T : Set of times {t1, t2, ..., tn} in years.
ntrials : Number of trials to simulate.
antithetic : Use antithetic variates technique.
Returns
----------
MCResult : Price of the option.
"""
if ntrials < len(T):
raise AssertionError('Number of trials cannot be less than the '
'number of setting dates!')
# Generation start
ntrials = int(ntrials // len(T))
payoffs = [0] * ntrials
for idx in range(ntrials):
# Generate a random path
if not antithetic:
spot_prices = self.path.generate(T)
else:
prices_tuple = self.path.generate_antithetic(T)
spot_prices, a_spot_prices = prices_tuple
# Calculate the payoff
payoff = self.payoff.calculate(spot_prices)
if antithetic:
a_po = self.payoff.calculate(a_spot_prices)
payoff = (payoff + a_po) / 2
payoffs[idx] = payoff
# Discount to current time
df = math.exp(-self.path.net_r * (T[-1] - T[0]))
dis_payoffs = [x * df for x in payoffs]
# Payoff expectation and standard error
exp_payoff = mean(dis_payoffs)
stderr = stdev(dis_payoffs, exp_payoff) / math.sqrt(ntrials)
return MCResult(exp_payoff, stderr)
|