Skip to content

Commit b79d0bb

Browse files
Merge pull request #33 from SebastienEveno/add_docstrings
Add docstrings
2 parents b8b4cb0 + 36938a2 commit b79d0bb

13 files changed

+530
-37
lines changed

exotx/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.6.3"
1+
__version__ = "0.6.4"

exotx/helpers/dates.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@
55

66

77
def convert_maturity_to_ql_date(maturity: Union[str, datetime, ql.Date], string_format: str = '%Y-%m-%d') -> ql.Date:
8+
"""
9+
Converts a maturity date in various formats to a QuantLib Date object.
10+
11+
This function accepts input dates as strings, Python datetime objects, or QuantLib Date objects and
12+
returns the corresponding QuantLib Date object.
13+
14+
:param maturity: The maturity date to be converted, which can be a string, datetime, or QuantLib Date object.
15+
:type maturity: Union[str, datetime, ql.Date]
16+
:param string_format: The date format for string input, defaults to '%Y-%m-%d'.
17+
:type string_format: str, optional
18+
:return: The converted maturity date as a QuantLib Date object.
19+
:rtype: ql.Date
20+
:raises TypeError: If the input maturity type is not valid.
21+
"""
822
if isinstance(maturity, ql.Date):
923
return maturity
1024
elif isinstance(maturity, datetime):

exotx/instruments/asian_option.py

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,26 @@
1717

1818

1919
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+
2040
def __init__(self,
2141
strike: float, maturity: Union[str, datetime], option_type: Union[str, OptionType],
2242
average_type: Union[str, AverageType], average_calculation: Union[str, AverageCalculation],
@@ -28,15 +48,31 @@ def __init__(self,
2848
self.maturity = convert_maturity_to_ql_date(maturity)
2949
self.option_type = convert_option_type_to_ql(option_type)
3050
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)
3355
self.arithmetic_running_accumulator = arithmetic_running_accumulator
3456
self.geometric_running_accumulator = geometric_running_accumulator
3557
self.past_fixings = past_fixings
3658
self.future_fixing_dates = None if not future_fixing_dates else [ql.Date().from_date(future_fixing_date)
3759
for future_fixing_date in future_fixing_dates]
3860

3961
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+
"""
4076
reference_date: ql.Date = market_data.get_ql_reference_date()
4177
ql.Settings.instance().evaluationDate = reference_date
4278

@@ -49,7 +85,8 @@ def price(self, market_data, static_data, pricing_config: PricingConfiguration,
4985
ql_payoff = ql.PlainVanillaPayoff(self.option_type, self.strike)
5086
ql_exercise = ql.EuropeanExercise(self.maturity)
5187
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)
5390
elif self.average_calculation == AverageCalculation.DISCRETE:
5491
if self.average_type == ql.Average().Arithmetic:
5592
ql_option = ql.DiscreteAveragingAsianOption(self.average_type,
@@ -61,12 +98,15 @@ def price(self, market_data, static_data, pricing_config: PricingConfiguration,
6198
self.past_fixings, self.future_fixing_dates, ql_payoff,
6299
ql_exercise)
63100
else:
64-
raise ValueError(f"Invalid average type \"{self.average_type}\"")
101+
raise ValueError(
102+
f"Invalid average type \"{self.average_type}\"")
65103
else:
66-
raise ValueError(f"Invalid average calculation \"{self.average_calculation}\"")
104+
raise ValueError(
105+
f"Invalid average calculation \"{self.average_calculation}\"")
67106

68107
# 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)
70110
ql_option.setPricingEngine(ql_engine)
71111

72112
# price
@@ -91,20 +131,23 @@ def _get_ql_pricing_engine(self, market_data, static_data, pricing_config: Prici
91131
elif self.average_convention == AverageConvention.STRIKE:
92132
return ql.AnalyticDiscreteGeometricAverageStrikeAsianEngine(process)
93133
else:
94-
raise ValueError(f"Invalid average convention \"{self.average_convention}\"")
134+
raise ValueError(
135+
f"Invalid average convention \"{self.average_convention}\"")
95136
elif pricing_config.numerical_method == NumericalMethod.MC:
96137
# TODO: filter on pricing_config.pricing_model, here we assume black-scholes only
97138
bs_model = BlackScholesModel(market_data, static_data)
98139
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)
100142
if self.average_convention == AverageConvention.PRICE:
101143
return ql.MCDiscreteGeometricAPEngine(process, random_number_generator)
102144
elif self.average_convention == AverageConvention.STRIKE:
103145
return ValueError(
104146
f"No corresponding engine for asian option for numerical method {pricing_config.numerical_method}, "
105147
f"average type {self.average_type}, average calculation {self.average_calculation}, and average convention {self.average_convention}")
106148
else:
107-
raise ValueError(f"Invalid average convention \"{self.average_convention}\"")
149+
raise ValueError(
150+
f"Invalid average convention \"{self.average_convention}\"")
108151
else:
109152
raise ValueError(
110153
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
113156
if pricing_config.numerical_method == NumericalMethod.MC:
114157
bs_model = BlackScholesModel(market_data, static_data)
115158
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)
117161
if self.average_convention == AverageConvention.PRICE:
118162
return ql.MCDiscreteArithmeticAPEngine(process, random_number_generator)
119163
elif self.average_convention == AverageConvention.STRIKE:
120164
return ql.MCDiscreteArithmeticASEngine(process, random_number_generator)
121165
else:
122-
raise ValueError(f"Invalid average convention \"{self.average_convention}\"")
166+
raise ValueError(
167+
f"Invalid average convention \"{self.average_convention}\"")
123168
else:
124169
raise ValueError(f"Invalid average type {self.average_type}")
125170
elif self.average_calculation == AverageCalculation.CONTINUOUS:
@@ -136,7 +181,8 @@ def _get_ql_pricing_engine(self, market_data, static_data, pricing_config: Prici
136181
else:
137182
raise ValueError("No engine for asian option")
138183
else:
139-
raise ValueError(f"Invalid average calculation \"{self.average_calculation}\"")
184+
raise ValueError(
185+
f"Invalid average calculation \"{self.average_calculation}\"")
140186

141187
# region serialization/deserialization
142188
def to_json(self):
@@ -153,6 +199,34 @@ def from_json(cls, json_data):
153199

154200
# region Schema
155201
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+
"""
156230
strike = fields.Float()
157231
maturity = fields.Date(format="%Y-%m-%d")
158232
option_type = OptionTypeField()
@@ -162,5 +236,16 @@ class AsianOptionSchema(Schema):
162236

163237
@post_load
164238
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+
"""
165250
return AsianOption(**data)
166251
# endregion

0 commit comments

Comments
 (0)