17
17
18
18
19
19
class AsianOption (Instrument ):
20
+ """
21
+ A class representing an Asian option, which is an option type that derives its value from the average price of
22
+ the underlying asset over a specified period.
23
+
24
+ The AsianOption class extends the Instrument class and provides functionality for constructing and pricing
25
+ Asian options using the QuantLib library.
26
+
27
+ Attributes:
28
+ strike (float): The strike price of the option.
29
+ maturity (Union[str, datetime]): The maturity date of the option as a string or datetime object.
30
+ option_type (Union[str, OptionType]): The type of the option (call or put) as a string or OptionType object.
31
+ average_type (Union[str, AverageType]): The averaging type of the option as a string or AverageType object.
32
+ average_calculation (Union[str, AverageCalculation]): The average calculation method for the option as a string or AverageCalculation object.
33
+ average_convention (Union[str, AverageConvention]): The averaging convention of the option as a string or AverageConvention object.
34
+ arithmetic_running_accumulator (float, optional): The arithmetic running accumulator for the option. Defaults to 0.0.
35
+ geometric_running_accumulator (float, optional): The geometric running accumulator for the option. Defaults to 1.0.
36
+ past_fixings (int, optional): The number of past fixings for the option. Defaults to 0.
37
+ future_fixing_dates (List[datetime], optional): A list of future fixing dates for the option. Defaults to None.
38
+ """
39
+
20
40
def __init__ (self ,
21
41
strike : float , maturity : Union [str , datetime ], option_type : Union [str , OptionType ],
22
42
average_type : Union [str , AverageType ], average_calculation : Union [str , AverageCalculation ],
@@ -28,15 +48,31 @@ def __init__(self,
28
48
self .maturity = convert_maturity_to_ql_date (maturity )
29
49
self .option_type = convert_option_type_to_ql (option_type )
30
50
self .average_type = convert_average_type_to_ql (average_type )
31
- self .average_calculation : AverageCalculation = convert_average_calculation (average_calculation )
32
- self .average_convention : AverageConvention = convert_average_convention (average_convention )
51
+ self .average_calculation : AverageCalculation = convert_average_calculation (
52
+ average_calculation )
53
+ self .average_convention : AverageConvention = convert_average_convention (
54
+ average_convention )
33
55
self .arithmetic_running_accumulator = arithmetic_running_accumulator
34
56
self .geometric_running_accumulator = geometric_running_accumulator
35
57
self .past_fixings = past_fixings
36
58
self .future_fixing_dates = None if not future_fixing_dates else [ql .Date ().from_date (future_fixing_date )
37
59
for future_fixing_date in future_fixing_dates ]
38
60
39
61
def price (self , market_data , static_data , pricing_config : PricingConfiguration , seed : int = 1 ) -> dict :
62
+ """
63
+ Prices the Asian option using the given market data, static data, and pricing configuration.
64
+
65
+ :param market_data: An object containing the market data needed to price the option.
66
+ :type market_data: MarketData
67
+ :param static_data: An object containing the static data needed to price the option.
68
+ :type static_data: StaticData
69
+ :param pricing_config: A configuration object for the pricing process.
70
+ :type pricing_config: PricingConfiguration
71
+ :param seed: An optional integer seed for random number generation. Defaults to 1.
72
+ :type seed: int
73
+ :return: A dictionary containing the option price and, if applicable, Greeks.
74
+ :rtype: dict
75
+ """
40
76
reference_date : ql .Date = market_data .get_ql_reference_date ()
41
77
ql .Settings .instance ().evaluationDate = reference_date
42
78
@@ -49,7 +85,8 @@ def price(self, market_data, static_data, pricing_config: PricingConfiguration,
49
85
ql_payoff = ql .PlainVanillaPayoff (self .option_type , self .strike )
50
86
ql_exercise = ql .EuropeanExercise (self .maturity )
51
87
if self .average_calculation == AverageCalculation .CONTINUOUS :
52
- ql_option = ql .ContinuousAveragingAsianOption (self .average_type , ql_payoff , ql_exercise )
88
+ ql_option = ql .ContinuousAveragingAsianOption (
89
+ self .average_type , ql_payoff , ql_exercise )
53
90
elif self .average_calculation == AverageCalculation .DISCRETE :
54
91
if self .average_type == ql .Average ().Arithmetic :
55
92
ql_option = ql .DiscreteAveragingAsianOption (self .average_type ,
@@ -61,12 +98,15 @@ def price(self, market_data, static_data, pricing_config: PricingConfiguration,
61
98
self .past_fixings , self .future_fixing_dates , ql_payoff ,
62
99
ql_exercise )
63
100
else :
64
- raise ValueError (f"Invalid average type \" { self .average_type } \" " )
101
+ raise ValueError (
102
+ f"Invalid average type \" { self .average_type } \" " )
65
103
else :
66
- raise ValueError (f"Invalid average calculation \" { self .average_calculation } \" " )
104
+ raise ValueError (
105
+ f"Invalid average calculation \" { self .average_calculation } \" " )
67
106
68
107
# set the pricing engine
69
- ql_engine = self ._get_ql_pricing_engine (market_data , static_data , pricing_config , seed )
108
+ ql_engine = self ._get_ql_pricing_engine (
109
+ market_data , static_data , pricing_config , seed )
70
110
ql_option .setPricingEngine (ql_engine )
71
111
72
112
# price
@@ -91,20 +131,23 @@ def _get_ql_pricing_engine(self, market_data, static_data, pricing_config: Prici
91
131
elif self .average_convention == AverageConvention .STRIKE :
92
132
return ql .AnalyticDiscreteGeometricAverageStrikeAsianEngine (process )
93
133
else :
94
- raise ValueError (f"Invalid average convention \" { self .average_convention } \" " )
134
+ raise ValueError (
135
+ f"Invalid average convention \" { self .average_convention } \" " )
95
136
elif pricing_config .numerical_method == NumericalMethod .MC :
96
137
# TODO: filter on pricing_config.pricing_model, here we assume black-scholes only
97
138
bs_model = BlackScholesModel (market_data , static_data )
98
139
process = bs_model .setup ()
99
- random_number_generator = str (pricing_config .random_number_generator )
140
+ random_number_generator = str (
141
+ pricing_config .random_number_generator )
100
142
if self .average_convention == AverageConvention .PRICE :
101
143
return ql .MCDiscreteGeometricAPEngine (process , random_number_generator )
102
144
elif self .average_convention == AverageConvention .STRIKE :
103
145
return ValueError (
104
146
f"No corresponding engine for asian option for numerical method { pricing_config .numerical_method } , "
105
147
f"average type { self .average_type } , average calculation { self .average_calculation } , and average convention { self .average_convention } " )
106
148
else :
107
- raise ValueError (f"Invalid average convention \" { self .average_convention } \" " )
149
+ raise ValueError (
150
+ f"Invalid average convention \" { self .average_convention } \" " )
108
151
else :
109
152
raise ValueError (
110
153
f"No engine for asian option with numerical method { pricing_config .numerical_method } "
@@ -113,13 +156,15 @@ def _get_ql_pricing_engine(self, market_data, static_data, pricing_config: Prici
113
156
if pricing_config .numerical_method == NumericalMethod .MC :
114
157
bs_model = BlackScholesModel (market_data , static_data )
115
158
process = bs_model .setup ()
116
- random_number_generator = str (pricing_config .random_number_generator )
159
+ random_number_generator = str (
160
+ pricing_config .random_number_generator )
117
161
if self .average_convention == AverageConvention .PRICE :
118
162
return ql .MCDiscreteArithmeticAPEngine (process , random_number_generator )
119
163
elif self .average_convention == AverageConvention .STRIKE :
120
164
return ql .MCDiscreteArithmeticASEngine (process , random_number_generator )
121
165
else :
122
- raise ValueError (f"Invalid average convention \" { self .average_convention } \" " )
166
+ raise ValueError (
167
+ f"Invalid average convention \" { self .average_convention } \" " )
123
168
else :
124
169
raise ValueError (f"Invalid average type { self .average_type } " )
125
170
elif self .average_calculation == AverageCalculation .CONTINUOUS :
@@ -136,7 +181,8 @@ def _get_ql_pricing_engine(self, market_data, static_data, pricing_config: Prici
136
181
else :
137
182
raise ValueError ("No engine for asian option" )
138
183
else :
139
- raise ValueError (f"Invalid average calculation \" { self .average_calculation } \" " )
184
+ raise ValueError (
185
+ f"Invalid average calculation \" { self .average_calculation } \" " )
140
186
141
187
# region serialization/deserialization
142
188
def to_json (self ):
@@ -153,6 +199,34 @@ def from_json(cls, json_data):
153
199
154
200
# region Schema
155
201
class AsianOptionSchema (Schema ):
202
+ """
203
+ AsianOptionSchema is a Marshmallow schema class for deserializing and validating JSON data into an AsianOption object.
204
+
205
+ This schema defines the required fields for an AsianOption object and validates their types and values. It also provides
206
+ a post_load method to create an AsianOption object after deserialization and validation.
207
+
208
+ Fields:
209
+ - strike (float): The option's strike price.
210
+ - maturity (date): The option's maturity date in the format "YYYY-MM-DD".
211
+ - option_type (OptionType): The option type, either "call" or "put".
212
+ - average_type (AverageType): The averaging type, either "arithmetic" or "geometric".
213
+ - average_calculation (AverageCalculation): The averaging calculation, either "continuous" or "discrete".
214
+ - average_convention (AverageConvention): The average convention, either "price" or "strike".
215
+
216
+ Example usage:
217
+
218
+ >>> asian_option_data = {
219
+ ... "strike": 100.0,
220
+ ... "maturity": "2023-05-10",
221
+ ... "option_type": "call",
222
+ ... "average_type": "arithmetic",
223
+ ... "average_calculation": "continuous",
224
+ ... "average_convention": "price"
225
+ ... }
226
+ >>> schema = AsianOptionSchema()
227
+ >>> result = schema.load(asian_option_data)
228
+ >>> asian_option = result.data
229
+ """
156
230
strike = fields .Float ()
157
231
maturity = fields .Date (format = "%Y-%m-%d" )
158
232
option_type = OptionTypeField ()
@@ -162,5 +236,16 @@ class AsianOptionSchema(Schema):
162
236
163
237
@post_load
164
238
def make_asian_option (self , data , ** kwargs ) -> AsianOption :
239
+ """
240
+ Creates an AsianOption object after deserialization and validation of JSON data.
241
+
242
+ This method is called after the JSON data has been deserialized and validated against the schema. It constructs
243
+ an AsianOption object using the deserialized data.
244
+
245
+ :param data: The deserialized and validated JSON data.
246
+ :type data: dict
247
+ :return: The created AsianOption object.
248
+ :rtype: AsianOption
249
+ """
165
250
return AsianOption (** data )
166
251
# endregion
0 commit comments