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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# 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, Sequence
from utils.misc import Number
from utils.path import PathGenerator
from utils.payoff import BasePayoff
__all__ = ['MCResult', 'PricingEngine']
@dataclass
class MCResult:
"""
Price of option along with its MC error.
Attributes
----------
price : float
Spot Price.
path : float
MC standard error.
"""
__slots__ = ['price', 'stderr']
price: float
stderr: float
@dataclass
class PricingEngine:
"""
Class for generating underlying prices using MC techniques.
Attributes
----------
payoff : BasePayoff
Payoff object for calculating the options payoff.
path : PathGenerator
PathGenerator object for generating the evolution of the underlying.
Methods
-------
price()
Price the option using MC techniques.
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)
PricingEngine(payoff=AsianArithmeticPayOff(K=110, option_right=Call),
path=PathGenerator(S=100.0, r=0.1, div=0.01, vol=0.3))
"""
__slots__ = 'payoff', 'path'
payoff: BasePayoff
path: PathGenerator
def price(
self,
T: Sequence[Number],
ntrials: int = 10_000,
antithetic: bool = True
) -> MCResult:
"""
Price the option using MC techniques.
Parameters
----------
T : Sequence of Numbers
Set of times {t1, t2, ..., tn} in years.
ntrials : int
Number of trials to simulate.
antithetic : bool
Use antithetic variates technique.
Returns
-------
MCResult
Price of the option.
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)
"""
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: List[float] = [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)
|