voly 0.0.12__tar.gz → 0.0.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {voly-0.0.12/src/voly.egg-info → voly-0.0.13}/PKG-INFO +1 -1
- {voly-0.0.12 → voly-0.0.13}/pyproject.toml +2 -2
- {voly-0.0.12 → voly-0.0.13}/src/voly/client.py +33 -15
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/fit.py +113 -155
- {voly-0.0.12 → voly-0.0.13/src/voly.egg-info}/PKG-INFO +1 -1
- {voly-0.0.12 → voly-0.0.13}/LICENSE +0 -0
- {voly-0.0.12 → voly-0.0.13}/README.md +0 -0
- {voly-0.0.12 → voly-0.0.13}/setup.cfg +0 -0
- {voly-0.0.12 → voly-0.0.13}/setup.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/__init__.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/__init__.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/charts.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/data.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/interpolate.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/core/rnd.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/exceptions.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/formulas.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/models.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/utils/__init__.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly/utils/logger.py +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly.egg-info/SOURCES.txt +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly.egg-info/dependency_links.txt +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly.egg-info/requires.txt +0 -0
- {voly-0.0.12 → voly-0.0.13}/src/voly.egg-info/top_level.txt +0 -0
- {voly-0.0.12 → voly-0.0.13}/tests/test_client.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "voly"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.13"
|
|
8
8
|
description = "Options & volatility research package"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -60,7 +60,7 @@ line_length = 100
|
|
|
60
60
|
multi_line_output = 3
|
|
61
61
|
|
|
62
62
|
[tool.mypy]
|
|
63
|
-
python_version = "0.0.
|
|
63
|
+
python_version = "0.0.13"
|
|
64
64
|
warn_return_any = true
|
|
65
65
|
warn_unused_configs = true
|
|
66
66
|
disallow_untyped_defs = true
|
|
@@ -18,7 +18,7 @@ from voly.formulas import (
|
|
|
18
18
|
bs, delta, gamma, vega, theta, rho, vanna, volga, charm, greeks, iv
|
|
19
19
|
)
|
|
20
20
|
from voly.core.data import fetch_option_chain, process_option_chain
|
|
21
|
-
from voly.core.fit import fit_model
|
|
21
|
+
from voly.core.fit import fit_model, get_surface
|
|
22
22
|
from voly.core.rnd import calculate_rnd, calculate_pdf, calculate_cdf, calculate_strike_probability
|
|
23
23
|
from voly.core.interpolate import interpolate_model
|
|
24
24
|
from voly.core.charts import (
|
|
@@ -295,18 +295,16 @@ class VolyClient:
|
|
|
295
295
|
@staticmethod
|
|
296
296
|
def fit_model(market_data: pd.DataFrame,
|
|
297
297
|
model_name: str = 'svi',
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
plot: bool = False) -> Dict[str, Any]:
|
|
298
|
+
initial_params: Optional[List[float]] = None,
|
|
299
|
+
param_bounds: Optional[Tuple] = None) -> Dict[str, Any]:
|
|
301
300
|
"""
|
|
302
301
|
Fit a volatility model to market data.
|
|
303
302
|
|
|
304
303
|
Parameters:
|
|
305
304
|
- market_data: DataFrame with market data
|
|
306
305
|
- model_name: Name of model to fit (default: 'svi')
|
|
307
|
-
-
|
|
308
|
-
-
|
|
309
|
-
- plot: Whether to generate and return plots
|
|
306
|
+
- initial_params: Optional initial parameters for optimization
|
|
307
|
+
- param_bounds: Optional parameter bounds for optimization
|
|
310
308
|
|
|
311
309
|
Returns:
|
|
312
310
|
- Dictionary with fitting results and optional plots
|
|
@@ -317,18 +315,38 @@ class VolyClient:
|
|
|
317
315
|
fit_results = fit_model(
|
|
318
316
|
market_data=market_data,
|
|
319
317
|
model_name=model_name,
|
|
320
|
-
|
|
321
|
-
|
|
318
|
+
initial_params=initial_params,
|
|
319
|
+
param_bounds=param_bounds
|
|
322
320
|
)
|
|
323
321
|
|
|
324
|
-
# Generate plots if requested
|
|
325
|
-
if plot:
|
|
326
|
-
logger.info("Generating model fitting plots")
|
|
327
|
-
plots = generate_all_plots(fit_results, market_data=market_data)
|
|
328
|
-
fit_results['plots'] = plots
|
|
329
|
-
|
|
330
322
|
return fit_results
|
|
331
323
|
|
|
324
|
+
@staticmethod
|
|
325
|
+
def get_surface(param_matrix: pd.DataFrame,
|
|
326
|
+
log_moneyness: Tuple[float, float, int] = (-2, 2, 500)) -> Dict[str, Any]:
|
|
327
|
+
"""
|
|
328
|
+
Generate implied volatility surface using optimized SVI parameters.
|
|
329
|
+
|
|
330
|
+
Parameters:
|
|
331
|
+
- param_matrix: Matrix of optimized SVI parameters from fit_results['raw_param_matrix']
|
|
332
|
+
- log_moneyness: Tuple of (min, max, num_points) for the moneyness grid
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
- Dictionary with surface generation results
|
|
336
|
+
"""
|
|
337
|
+
logger.info("Generating implied volatility surface")
|
|
338
|
+
|
|
339
|
+
# Generate the surface
|
|
340
|
+
moneyness_grid, iv_surface = get_surface(
|
|
341
|
+
param_matrix=param_matrix,
|
|
342
|
+
log_moneyness=log_moneyness
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
'moneyness_grid': moneyness_grid,
|
|
347
|
+
'iv_surface': iv_surface
|
|
348
|
+
}
|
|
349
|
+
|
|
332
350
|
# -------------------------------------------------------------------------
|
|
333
351
|
# Risk-Neutral Density (RND)
|
|
334
352
|
# -------------------------------------------------------------------------
|
|
@@ -14,6 +14,7 @@ from voly.utils.logger import logger, catch_exception
|
|
|
14
14
|
from voly.exceptions import VolyError
|
|
15
15
|
from voly.models import SVIModel
|
|
16
16
|
import warnings
|
|
17
|
+
|
|
17
18
|
warnings.filterwarnings("ignore")
|
|
18
19
|
|
|
19
20
|
|
|
@@ -50,11 +51,11 @@ def calculate_residuals(params: List[float],
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
@catch_exception
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
def fit_svi_parameters(market_data: pd.DataFrame,
|
|
55
|
+
initial_params: Optional[List[float]] = None,
|
|
56
|
+
param_bounds: Optional[Tuple] = None) -> Tuple[pd.DataFrame, Dict[str, Dict]]:
|
|
56
57
|
"""
|
|
57
|
-
|
|
58
|
+
Fit SVI parameters for all unique expiries in the market data.
|
|
58
59
|
|
|
59
60
|
Parameters:
|
|
60
61
|
- market_data: DataFrame with market data
|
|
@@ -62,11 +63,8 @@ def optimize_svi_parameters(market_data: pd.DataFrame,
|
|
|
62
63
|
- param_bounds: Bounds for parameters (default: from SVIModel)
|
|
63
64
|
|
|
64
65
|
Returns:
|
|
65
|
-
-
|
|
66
|
+
- Tuple of (fit_performance DataFrame, params_dict)
|
|
66
67
|
"""
|
|
67
|
-
results = {}
|
|
68
|
-
unique_expiries = sorted(market_data['yte'].unique())
|
|
69
|
-
|
|
70
68
|
# Use defaults if not provided
|
|
71
69
|
if initial_params is None:
|
|
72
70
|
initial_params = SVIModel.DEFAULT_INITIAL_PARAMS
|
|
@@ -74,37 +72,78 @@ def optimize_svi_parameters(market_data: pd.DataFrame,
|
|
|
74
72
|
if param_bounds is None:
|
|
75
73
|
param_bounds = SVIModel.DEFAULT_PARAM_BOUNDS
|
|
76
74
|
|
|
77
|
-
for
|
|
75
|
+
# Initialize data for fit performance
|
|
76
|
+
fit_data = {
|
|
77
|
+
'Maturity': [],
|
|
78
|
+
'DTE': [],
|
|
79
|
+
'YTE': [],
|
|
80
|
+
'Success': [],
|
|
81
|
+
'Cost': [],
|
|
82
|
+
'Optimality': [],
|
|
83
|
+
'RMSE': [],
|
|
84
|
+
'MAE': [],
|
|
85
|
+
'R²': [],
|
|
86
|
+
'Max Error': [],
|
|
87
|
+
'Number of Points': []
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Dictionary to store parameters
|
|
91
|
+
params_dict = {}
|
|
92
|
+
|
|
93
|
+
# Get unique expiries
|
|
94
|
+
unique_expiries = sorted(market_data['yte'].unique())
|
|
95
|
+
|
|
96
|
+
for yte in unique_expiries:
|
|
78
97
|
# Get maturity name for reporting
|
|
79
|
-
expiry_data = market_data[market_data['yte'] ==
|
|
98
|
+
expiry_data = market_data[market_data['yte'] == yte]
|
|
80
99
|
maturity_name = expiry_data['maturity_name'].iloc[0]
|
|
81
100
|
dte_value = expiry_data['dte'].iloc[0]
|
|
82
101
|
|
|
83
|
-
logger.info(f"Optimizing for {maturity_name} (DTE: {dte_value:.1f}
|
|
102
|
+
logger.info(f"Optimizing for {maturity_name} (DTE: {dte_value:.1f})...")
|
|
84
103
|
|
|
85
104
|
# Optimize SVI parameters
|
|
86
105
|
try:
|
|
87
106
|
result = least_squares(
|
|
88
107
|
calculate_residuals,
|
|
89
108
|
initial_params,
|
|
90
|
-
args=(
|
|
109
|
+
args=(yte, market_data, SVIModel),
|
|
91
110
|
bounds=param_bounds,
|
|
92
111
|
max_nfev=1000
|
|
93
112
|
)
|
|
94
113
|
except Exception as e:
|
|
95
114
|
raise VolyError(f"Optimization failed for {maturity_name}: {str(e)}")
|
|
96
115
|
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
'
|
|
101
|
-
'cost': result.cost,
|
|
102
|
-
'optimality': result.optimality,
|
|
103
|
-
'message': result.message,
|
|
104
|
-
'yte': t_dte,
|
|
116
|
+
# Get parameters
|
|
117
|
+
params = result.x
|
|
118
|
+
params_dict[maturity_name] = {
|
|
119
|
+
'params': params,
|
|
105
120
|
'dte': dte_value
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
# Calculate model predictions for statistics
|
|
124
|
+
w_svi = np.array([SVIModel.svi(x, *params) for x in expiry_data['log_moneyness']])
|
|
125
|
+
iv_model = np.sqrt(w_svi / yte)
|
|
126
|
+
iv_market = expiry_data['mark_iv'].values
|
|
127
|
+
|
|
128
|
+
# Calculate statistics
|
|
129
|
+
rmse = np.sqrt(mean_squared_error(iv_market, iv_model))
|
|
130
|
+
mae = mean_absolute_error(iv_market, iv_model)
|
|
131
|
+
r2 = r2_score(iv_market, iv_model)
|
|
132
|
+
max_error = np.max(np.abs(iv_market - iv_model))
|
|
133
|
+
num_points = len(expiry_data)
|
|
134
|
+
|
|
135
|
+
# Add to fit data
|
|
136
|
+
fit_data['Maturity'].append(maturity_name)
|
|
137
|
+
fit_data['DTE'].append(dte_value)
|
|
138
|
+
fit_data['Success'].append(result.success)
|
|
139
|
+
fit_data['Cost'].append(result.cost)
|
|
140
|
+
fit_data['Optimality'].append(result.optimality)
|
|
141
|
+
fit_data['RMSE'].append(rmse)
|
|
142
|
+
fit_data['MAE'].append(mae)
|
|
143
|
+
fit_data['R²'].append(r2)
|
|
144
|
+
fit_data['Max Error'].append(max_error)
|
|
145
|
+
fit_data['Number of Points'].append(num_points)
|
|
146
|
+
|
|
108
147
|
if result.success:
|
|
109
148
|
logger.info(f'Optimization for {maturity_name} (DTE: {dte_value:.1f}): SUCCESS')
|
|
110
149
|
else:
|
|
@@ -112,17 +151,20 @@ def optimize_svi_parameters(market_data: pd.DataFrame,
|
|
|
112
151
|
|
|
113
152
|
logger.info('------------------------------------------')
|
|
114
153
|
|
|
115
|
-
|
|
154
|
+
# Create DataFrame with all fit performance data
|
|
155
|
+
fit_performance = pd.DataFrame(fit_data)
|
|
156
|
+
|
|
157
|
+
return fit_performance, params_dict
|
|
116
158
|
|
|
117
159
|
|
|
118
160
|
@catch_exception
|
|
119
|
-
def create_parameters_matrix(
|
|
161
|
+
def create_parameters_matrix(params_dict: Dict[str, Dict]) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
120
162
|
"""
|
|
121
163
|
Create matrices of optimized parameters for each expiry.
|
|
122
164
|
Uses maturity names as column names.
|
|
123
165
|
|
|
124
166
|
Parameters:
|
|
125
|
-
-
|
|
167
|
+
- params_dict: Dictionary of parameter results by maturity name
|
|
126
168
|
|
|
127
169
|
Returns:
|
|
128
170
|
- Tuple of DataFrames with optimized parameters:
|
|
@@ -130,8 +172,8 @@ def create_parameters_matrix(optimization_results: Dict[str, Dict[str, Any]]) ->
|
|
|
130
172
|
2. Jump-Wing parameters (nu, psi, p, c, nu_tilde)
|
|
131
173
|
"""
|
|
132
174
|
# Get maturity names in order by DTE
|
|
133
|
-
maturity_names = sorted(
|
|
134
|
-
key=lambda x:
|
|
175
|
+
maturity_names = sorted(params_dict.keys(),
|
|
176
|
+
key=lambda x: params_dict[x]['dte'])
|
|
135
177
|
|
|
136
178
|
# Create DataFrame for raw parameters with maturity names as columns
|
|
137
179
|
raw_param_matrix = pd.DataFrame(
|
|
@@ -151,7 +193,7 @@ def create_parameters_matrix(optimization_results: Dict[str, Dict[str, Any]]) ->
|
|
|
151
193
|
|
|
152
194
|
# Fill the matrices with optimized parameters
|
|
153
195
|
for maturity_name in maturity_names:
|
|
154
|
-
result =
|
|
196
|
+
result = params_dict[maturity_name]
|
|
155
197
|
|
|
156
198
|
# Extract raw SVI parameters
|
|
157
199
|
a, b, sigma, rho, m = result['params']
|
|
@@ -179,155 +221,71 @@ def create_parameters_matrix(optimization_results: Dict[str, Dict[str, Any]]) ->
|
|
|
179
221
|
|
|
180
222
|
|
|
181
223
|
@catch_exception
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
) -> Tuple[np.ndarray, Dict[float, np.ndarray]]:
|
|
187
|
-
"""
|
|
188
|
-
Generate implied volatility surface using optimized SVI parameters.
|
|
189
|
-
|
|
190
|
-
Parameters:
|
|
191
|
-
- param_matrix: Matrix of optimized SVI parameters with maturity names as columns
|
|
192
|
-
- moneyness_range: (min, max) range for moneyness grid
|
|
193
|
-
- num_points: Number of points for moneyness grid
|
|
194
|
-
|
|
195
|
-
Returns:
|
|
196
|
-
- Moneyness grid and implied volatility surface
|
|
197
|
-
"""
|
|
198
|
-
# Generate moneyness grid
|
|
199
|
-
min_m, max_m = moneyness_range
|
|
200
|
-
moneyness_values = np.linspace(min_m, max_m, num=num_points)
|
|
201
|
-
implied_volatility_surface = {}
|
|
202
|
-
|
|
203
|
-
# Get YTE values from the parameter matrix attributes
|
|
204
|
-
yte_values = param_matrix.attrs['yte_values']
|
|
205
|
-
|
|
206
|
-
# Generate implied volatility for each expiry
|
|
207
|
-
for maturity_name, yte in yte_values.items():
|
|
208
|
-
svi_params = param_matrix[maturity_name].values
|
|
209
|
-
w_svi = [SVIModel.svi(x, *svi_params) for x in moneyness_values]
|
|
210
|
-
implied_volatility_surface[yte] = np.sqrt(np.array(w_svi) / yte)
|
|
211
|
-
|
|
212
|
-
return moneyness_values, implied_volatility_surface
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
@catch_exception
|
|
216
|
-
def calculate_fit_statistics(market_data: pd.DataFrame, param_matrix: pd.DataFrame) -> pd.DataFrame:
|
|
224
|
+
def fit_model(market_data: pd.DataFrame,
|
|
225
|
+
model_name: str = 'svi',
|
|
226
|
+
initial_params: Optional[List[float]] = None,
|
|
227
|
+
param_bounds: Optional[Tuple] = None) -> Dict[str, Any]:
|
|
217
228
|
"""
|
|
218
|
-
|
|
229
|
+
Fit a volatility model to market data.
|
|
219
230
|
|
|
220
231
|
Parameters:
|
|
221
232
|
- market_data: DataFrame with market data
|
|
222
|
-
-
|
|
233
|
+
- model_name: Type of model to fit (default: 'svi')
|
|
234
|
+
- initial_params: Optional initial parameters for optimization (default: model's defaults)
|
|
235
|
+
- param_bounds: Optional parameter bounds for optimization (default: model's defaults)
|
|
223
236
|
|
|
224
237
|
Returns:
|
|
225
|
-
-
|
|
238
|
+
- Dictionary with fitting results
|
|
226
239
|
"""
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
dte_values = param_matrix.attrs['dte_values']
|
|
230
|
-
|
|
231
|
-
# Initialize lists for statistics
|
|
232
|
-
maturity_name_list = []
|
|
233
|
-
dte_list = []
|
|
234
|
-
yte_list = []
|
|
235
|
-
rmse_list = []
|
|
236
|
-
mae_list = []
|
|
237
|
-
r2_list = []
|
|
238
|
-
max_error_list = []
|
|
239
|
-
num_points_list = []
|
|
240
|
-
|
|
241
|
-
# Calculate statistics for each expiry
|
|
242
|
-
for maturity_name, yte in yte_values.items():
|
|
243
|
-
# Filter market data for the specific expiry
|
|
244
|
-
expiry_data = market_data[market_data['yte'] == yte]
|
|
245
|
-
dte_value = dte_values[maturity_name]
|
|
246
|
-
|
|
247
|
-
# Calculate SVI model predictions
|
|
248
|
-
svi_params = param_matrix[maturity_name].values
|
|
249
|
-
w_svi = np.array([SVIModel.svi(x, *svi_params) for x in expiry_data['log_moneyness']])
|
|
250
|
-
iv_model = np.sqrt(w_svi / yte)
|
|
240
|
+
if model_name.lower() != 'svi':
|
|
241
|
+
raise VolyError(f"Model type '{model_name}' is not supported. Currently only 'svi' is available.")
|
|
251
242
|
|
|
252
|
-
|
|
253
|
-
|
|
243
|
+
# Step 1: Fit model parameters and get performance metrics in one step
|
|
244
|
+
fit_performance, params_dict = fit_svi_parameters(
|
|
245
|
+
market_data,
|
|
246
|
+
initial_params=initial_params,
|
|
247
|
+
param_bounds=param_bounds
|
|
248
|
+
)
|
|
254
249
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
mae = mean_absolute_error(iv_market, iv_model)
|
|
258
|
-
r2 = r2_score(iv_market, iv_model)
|
|
259
|
-
max_error = np.max(np.abs(iv_market - iv_model))
|
|
260
|
-
num_points = len(expiry_data)
|
|
250
|
+
# Step 2: Create parameter matrices
|
|
251
|
+
raw_param_matrix, jw_param_matrix = create_parameters_matrix(params_dict)
|
|
261
252
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
mae_list.append(mae)
|
|
268
|
-
r2_list.append(r2)
|
|
269
|
-
max_error_list.append(max_error)
|
|
270
|
-
num_points_list.append(num_points)
|
|
271
|
-
|
|
272
|
-
# Create DataFrame with statistics
|
|
273
|
-
stats_df = pd.DataFrame({
|
|
274
|
-
'Maturity': maturity_name_list,
|
|
275
|
-
'DTE': dte_list,
|
|
276
|
-
'YTE': yte_list,
|
|
277
|
-
'RMSE': rmse_list,
|
|
278
|
-
'MAE': mae_list,
|
|
279
|
-
'R²': r2_list,
|
|
280
|
-
'Max Error': max_error_list,
|
|
281
|
-
'Number of Points': num_points_list
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
return stats_df
|
|
253
|
+
return {
|
|
254
|
+
'raw_param_matrix': raw_param_matrix,
|
|
255
|
+
'jw_param_matrix': jw_param_matrix,
|
|
256
|
+
'fit_performance': fit_performance,
|
|
257
|
+
}
|
|
285
258
|
|
|
286
259
|
|
|
287
260
|
@catch_exception
|
|
288
|
-
def
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
261
|
+
def get_surface(
|
|
262
|
+
param_matrix: pd.DataFrame,
|
|
263
|
+
log_moneyness: Tuple[float, float, int] = (-2, 2, 500)
|
|
264
|
+
) -> Tuple[np.ndarray, Dict[float, np.ndarray], np.ndarray]:
|
|
292
265
|
"""
|
|
293
|
-
|
|
266
|
+
Generate implied volatility surface using optimized SVI parameters.
|
|
294
267
|
|
|
295
268
|
Parameters:
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
- moneyness_range: (min, max) range for moneyness grid
|
|
299
|
-
- num_points: Number of points for moneyness grid
|
|
269
|
+
- param_matrix: Matrix of optimized SVI parameters from fit_results
|
|
270
|
+
- log_moneyness: Tuple of (min, max, num_points) for the moneyness grid
|
|
300
271
|
|
|
301
272
|
Returns:
|
|
302
|
-
-
|
|
273
|
+
- Tuple of (moneyness_grid, iv_surface, unique_expiries)
|
|
303
274
|
"""
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
# Step 1: Optimize model parameters
|
|
308
|
-
optimization_results = optimize_svi_parameters(market_data)
|
|
275
|
+
# Extract moneyness parameters
|
|
276
|
+
min_m, max_m, num_points = log_moneyness
|
|
309
277
|
|
|
310
|
-
#
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
# Step 3: Generate implied volatility surface
|
|
314
|
-
moneyness_grid, iv_surface = generate_implied_volatility_surface(
|
|
315
|
-
raw_param_matrix, moneyness_range, num_points
|
|
316
|
-
)
|
|
278
|
+
# Generate moneyness grid
|
|
279
|
+
moneyness_values = np.linspace(min_m, max_m, num=num_points)
|
|
280
|
+
implied_volatility_surface = {}
|
|
317
281
|
|
|
318
|
-
#
|
|
319
|
-
|
|
282
|
+
# Get YTE values from the parameter matrix attributes
|
|
283
|
+
yte_values = param_matrix.attrs['yte_values']
|
|
320
284
|
|
|
321
|
-
#
|
|
322
|
-
|
|
285
|
+
# Generate implied volatility for each expiry
|
|
286
|
+
for maturity_name, yte in yte_values.items():
|
|
287
|
+
svi_params = param_matrix[maturity_name].values
|
|
288
|
+
w_svi = [SVIModel.svi(x, *svi_params) for x in moneyness_values]
|
|
289
|
+
implied_volatility_surface[yte] = np.sqrt(np.array(w_svi) / yte)
|
|
323
290
|
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
'optimization_results': optimization_results,
|
|
327
|
-
'raw_param_matrix': raw_param_matrix,
|
|
328
|
-
'jw_param_matrix': jw_param_matrix,
|
|
329
|
-
'moneyness_grid': moneyness_grid,
|
|
330
|
-
'iv_surface': iv_surface,
|
|
331
|
-
'stats_df': stats_df,
|
|
332
|
-
'unique_expiries': unique_expiries_years,
|
|
333
|
-
}
|
|
291
|
+
return moneyness_values, implied_volatility_surface
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|