voly 0.0.64__tar.gz → 0.0.66__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.64
3
+ Version: 0.0.66
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "voly"
7
- version = "0.0.64"
7
+ version = "0.0.66"
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.64"
63
+ python_version = "0.0.66"
64
64
  warn_return_any = true
65
65
  warn_unused_configs = true
66
66
  disallow_untyped_defs = true
@@ -19,17 +19,38 @@ pio.renderers.default = "browser"
19
19
 
20
20
 
21
21
  @catch_exception
22
- def plot_volatility_smile(moneyness_array: np.ndarray,
22
+ def plot_volatility_smile(x_domain: np.ndarray,
23
23
  iv_array: np.ndarray,
24
24
  option_chain: pd.DataFrame = None,
25
- maturity: Optional[str] = None) -> go.Figure:
26
- """Plot volatility smile for a single expiry."""
25
+ maturity: Optional[str] = None,
26
+ domain_type: str = 'log_moneyness') -> go.Figure:
27
+ """
28
+ Plot volatility smile for a single expiry.
29
+
30
+ Parameters:
31
+ - x_domain: Array of x-axis values (log_moneyness, moneyness, strikes, delta)
32
+ - iv_array: Implied volatility values
33
+ - option_chain: Optional market data for comparison
34
+ - maturity: Maturity name for filtering market data
35
+ - domain_type: Type of x-domain ('log_moneyness', 'moneyness', 'strikes', 'delta')
36
+
37
+ Returns:
38
+ - Plotly figure
39
+ """
27
40
  fig = go.Figure()
28
41
 
42
+ # Map domain types to axis labels
43
+ domain_labels = {
44
+ 'log_moneyness': 'Log Moneyness',
45
+ 'moneyness': 'Moneyness (S/K)',
46
+ 'strikes': 'Strike Price',
47
+ 'delta': 'Call Delta'
48
+ }
49
+
29
50
  # Add model curve
30
51
  fig.add_trace(
31
52
  go.Scatter(
32
- x=moneyness_array,
53
+ x=x_domain,
33
54
  y=iv_array * 100, # Convert to percentage
34
55
  mode='lines',
35
56
  name='Model',
@@ -42,25 +63,39 @@ def plot_volatility_smile(moneyness_array: np.ndarray,
42
63
  maturity_data = option_chain[option_chain['maturity_name'] == maturity]
43
64
 
44
65
  if not maturity_data.empty:
45
- # Add bid and ask IVs
46
- for iv_type in ['bid_iv', 'ask_iv']:
47
- if iv_type in maturity_data.columns:
48
- fig.add_trace(
49
- go.Scatter(
50
- x=maturity_data['log_moneyness'],
51
- y=maturity_data[iv_type] * 100, # Convert to percentage
52
- mode='markers',
53
- name=iv_type.replace('_', ' ').upper(),
54
- marker=dict(size=8, symbol='circle', opacity=0.7)
66
+ # For market data, use log_moneyness by default as x-axis
67
+ market_x = maturity_data['log_moneyness']
68
+
69
+ # If domain is not log_moneyness, convert market data to match the domain
70
+ if domain_type == 'moneyness':
71
+ market_x = np.exp(market_x)
72
+ elif domain_type == 'strikes':
73
+ s = maturity_data['underlying_price'].iloc[0]
74
+ market_x = s / np.exp(market_x)
75
+ elif domain_type == 'delta':
76
+ # For delta, we'd need more complex conversion - only show model curve
77
+ pass
78
+
79
+ # Add bid and ask IVs if the domain type allows
80
+ if domain_type != 'delta':
81
+ for iv_type in ['bid_iv', 'ask_iv']:
82
+ if iv_type in maturity_data.columns:
83
+ fig.add_trace(
84
+ go.Scatter(
85
+ x=market_x,
86
+ y=maturity_data[iv_type] * 100, # Convert to percentage
87
+ mode='markers',
88
+ name=iv_type.replace('_', ' ').upper(),
89
+ marker=dict(size=8, symbol='circle', opacity=0.7)
90
+ )
55
91
  )
56
- )
57
92
 
58
93
  dte_value = maturity_data['dtm'].iloc[0]
59
94
 
60
95
  # Update layout
61
96
  fig.update_layout(
62
97
  title=f'Vol Smile for {maturity} (DTE: {dte_value:.1f})',
63
- xaxis_title='Log Moneyness',
98
+ xaxis_title=domain_labels.get(domain_type, 'X Domain'),
64
99
  yaxis_title='Implied Volatility (%)',
65
100
  template='plotly_dark',
66
101
  legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1)
@@ -70,16 +105,29 @@ def plot_volatility_smile(moneyness_array: np.ndarray,
70
105
 
71
106
 
72
107
  @catch_exception
73
- def plot_all_smiles(moneyness_array: np.ndarray,
108
+ def plot_all_smiles(x_surface: Dict[str, np.ndarray],
74
109
  iv_surface: Dict[str, np.ndarray],
75
- option_chain: Optional[pd.DataFrame] = None) -> List[go.Figure]:
76
- """Plot volatility smiles for all expiries."""
110
+ option_chain: Optional[pd.DataFrame] = None,
111
+ domain_type: str = 'log_moneyness') -> List[go.Figure]:
112
+ """
113
+ Plot volatility smiles for all expiries.
114
+
115
+ Parameters:
116
+ - x_surface: Dictionary mapping maturity names to x-domain arrays
117
+ - iv_surface: Dictionary mapping maturity names to IV arrays
118
+ - option_chain: Optional market data for comparison
119
+ - domain_type: Type of x-domain ('log_moneyness', 'moneyness', 'strikes', 'delta')
120
+
121
+ Returns:
122
+ - List of Plotly figures
123
+ """
77
124
  return [
78
125
  plot_volatility_smile(
79
- moneyness_array=moneyness_array,
126
+ x_domain=x_surface[maturity],
80
127
  iv_array=iv_surface[maturity],
81
128
  option_chain=option_chain,
82
- maturity=maturity
129
+ maturity=maturity,
130
+ domain_type=domain_type
83
131
  )
84
132
  for maturity in iv_surface.keys()
85
133
  ]
@@ -87,7 +135,15 @@ def plot_all_smiles(moneyness_array: np.ndarray,
87
135
 
88
136
  @catch_exception
89
137
  def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
90
- """Plot raw SVI parameters across different expiries."""
138
+ """
139
+ Plot raw SVI parameters across different expiries.
140
+
141
+ Parameters:
142
+ - fit_results: DataFrame from fit_model() with maturity names as index
143
+
144
+ Returns:
145
+ - Plotly figure
146
+ """
91
147
  # Select parameters to plot
92
148
  param_names = ['a', 'b', 'sigma', 'rho', 'm']
93
149
 
@@ -98,9 +154,12 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
98
154
  for p in param_names] + ['']
99
155
  )
100
156
 
101
- # Get maturity names and prepare tick labels
102
- maturity_names = fit_results.columns
103
- tick_labels = [f"{m} (DTE: {fit_results.loc['dtm', m]:.1f})" for m in maturity_names]
157
+ # Get maturity names from index
158
+ maturity_names = fit_results.index
159
+
160
+ # Create hover text with maturity info
161
+ tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f}, YTE: {fit_results.loc[m, 'ytm']:.4f})"
162
+ for m in maturity_names]
104
163
 
105
164
  # Plot each parameter
106
165
  for i, param in enumerate(param_names):
@@ -109,7 +168,7 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
109
168
  fig.add_trace(
110
169
  go.Scatter(
111
170
  x=list(range(len(maturity_names))),
112
- y=fit_results.loc[param],
171
+ y=fit_results[param],
113
172
  mode='lines+markers',
114
173
  name=param,
115
174
  line=dict(width=2),
@@ -132,7 +191,7 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
132
191
  fig.update_layout(
133
192
  title='Raw SVI Parameters Across Expiries',
134
193
  template='plotly_dark',
135
- showlegend=False,
194
+ showlegend=False
136
195
  )
137
196
 
138
197
  return fig
@@ -140,7 +199,15 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
140
199
 
141
200
  @catch_exception
142
201
  def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
143
- """Plot Jump-Wing parameters across different expiries."""
202
+ """
203
+ Plot Jump-Wing parameters across different expiries.
204
+
205
+ Parameters:
206
+ - fit_results: DataFrame from fit_model() with maturity names as index
207
+
208
+ Returns:
209
+ - Plotly figure
210
+ """
144
211
  # Select parameters to plot
145
212
  param_names = ['nu', 'psi', 'p', 'c', 'nu_tilde']
146
213
 
@@ -151,9 +218,12 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
151
218
  for p in param_names] + ['']
152
219
  )
153
220
 
154
- # Get maturity names and prepare tick labels
155
- maturity_names = fit_results.columns
156
- tick_labels = [f"{m} (DTE: {fit_results.loc['dtm', m]:.1f})" for m in maturity_names]
221
+ # Get maturity names from index
222
+ maturity_names = fit_results.index
223
+
224
+ # Create hover text with maturity info
225
+ tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f})"
226
+ for m in maturity_names]
157
227
 
158
228
  # Plot each parameter
159
229
  for i, param in enumerate(param_names):
@@ -162,7 +232,7 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
162
232
  fig.add_trace(
163
233
  go.Scatter(
164
234
  x=list(range(len(maturity_names))),
165
- y=fit_results.loc[param],
235
+ y=fit_results[param],
166
236
  mode='lines+markers',
167
237
  name=param,
168
238
  line=dict(width=2, color='rgb(0, 180, 180)'),
@@ -185,15 +255,23 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
185
255
  fig.update_layout(
186
256
  title='Jump-Wing Parameters Across Expiries',
187
257
  template='plotly_dark',
188
- showlegend=False,
258
+ showlegend=False
189
259
  )
190
260
 
191
261
  return fig
192
262
 
193
263
 
194
264
  @catch_exception
195
- def plot_fit_performance(fit_performance: pd.DataFrame) -> go.Figure:
196
- """Plot the fitting accuracy statistics."""
265
+ def plot_fit_performance(fit_results: pd.DataFrame) -> go.Figure:
266
+ """
267
+ Plot the fitting accuracy statistics.
268
+
269
+ Parameters:
270
+ - fit_results: DataFrame from fit_model() with maturity names as index
271
+
272
+ Returns:
273
+ - Plotly figure
274
+ """
197
275
  # Define metrics to plot
198
276
  metrics = {
199
277
  'rmse': {'title': 'RMSE by Expiry', 'row': 1, 'col': 1, 'ylabel': 'RMSE (%)', 'scale': 100},
@@ -208,19 +286,19 @@ def plot_fit_performance(fit_performance: pd.DataFrame) -> go.Figure:
208
286
  subplot_titles=[metrics[m]['title'] for m in metrics]
209
287
  )
210
288
 
211
- # Get maturity names and create x-axis indices
212
- maturity_names = fit_performance.columns
289
+ # Get maturity names from index and create x-axis indices
290
+ maturity_names = fit_results.index
213
291
  x_indices = list(range(len(maturity_names)))
214
292
 
215
293
  # Create hover labels
216
- hover_labels = [f"{m}" for m in maturity_names]
294
+ hover_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f})" for m in maturity_names]
217
295
 
218
296
  # Plot each metric
219
297
  for metric, config in metrics.items():
220
298
  fig.add_trace(
221
299
  go.Scatter(
222
300
  x=x_indices,
223
- y=fit_performance.loc[metric] * config['scale'],
301
+ y=fit_results[metric] * config['scale'],
224
302
  mode='lines+markers',
225
303
  name=metric.upper(),
226
304
  line=dict(width=2),
@@ -248,17 +326,37 @@ def plot_fit_performance(fit_performance: pd.DataFrame) -> go.Figure:
248
326
  fig.update_layout(
249
327
  title='Model Fitting Accuracy Statistics',
250
328
  template='plotly_dark',
251
- showlegend=False,
329
+ showlegend=False
252
330
  )
253
331
 
254
332
  return fig
255
333
 
256
334
 
257
335
  @catch_exception
258
- def plot_3d_surface(moneyness_array: np.ndarray,
336
+ def plot_3d_surface(x_surface: Dict[str, np.ndarray],
259
337
  iv_surface: Dict[str, np.ndarray],
260
- fit_results: pd.DataFrame = None) -> go.Figure:
261
- """Plot 3D implied volatility surface."""
338
+ fit_results: pd.DataFrame = None,
339
+ domain_type: str = 'log_moneyness') -> go.Figure:
340
+ """
341
+ Plot 3D implied volatility surface.
342
+
343
+ Parameters:
344
+ - x_surface: Dictionary mapping maturity names to x-domain arrays
345
+ - iv_surface: Dictionary mapping maturity names to IV arrays
346
+ - fit_results: Optional DataFrame with maturity information
347
+ - domain_type: Type of x-domain ('log_moneyness', 'moneyness', 'strikes', 'delta')
348
+
349
+ Returns:
350
+ - Plotly figure
351
+ """
352
+ # Map domain types to axis labels
353
+ domain_labels = {
354
+ 'log_moneyness': 'Log Moneyness',
355
+ 'moneyness': 'Moneyness (S/K)',
356
+ 'strikes': 'Strike Price',
357
+ 'delta': 'Delta'
358
+ }
359
+
262
360
  # Define custom colorscale
263
361
  custom_blue_scale = [[0, '#60AEFC'], [1, '#002040']]
264
362
 
@@ -268,39 +366,72 @@ def plot_3d_surface(moneyness_array: np.ndarray,
268
366
  # Get z-axis values (days to expiry)
269
367
  if fit_results is not None:
270
368
  # Use DTM values from fit_results
271
- maturity_values = [fit_results.loc['dtm', name] for name in maturity_names]
369
+ maturity_values = [fit_results.loc[name, 'dtm'] for name in maturity_names]
272
370
  else:
273
371
  # Default to sequential values
274
372
  maturity_values = list(range(len(maturity_names)))
275
373
 
276
- # Create 3D surface data
277
- z_array = np.array([iv_surface[m] * 100 for m in maturity_names]) # Convert to percentage
278
- X, Y = np.meshgrid(moneyness_array, maturity_values)
374
+ # Create a mesh grid for each maturity to handle different x domains
375
+ X = []
376
+ Y = []
377
+ Z = []
279
378
 
280
- # Create figure
281
- fig = go.Figure(data=[
282
- go.Surface(
283
- z=z_array,
284
- x=X,
285
- y=Y,
379
+ for i, m in enumerate(maturity_names):
380
+ x_values = x_surface[m]
381
+ z_values = iv_surface[m] * 100 # Convert to percentage
382
+ y_value = maturity_values[i]
383
+
384
+ # Add to point lists
385
+ for j in range(len(x_values)):
386
+ X.append(x_values[j])
387
+ Y.append(y_value)
388
+ Z.append(z_values[j])
389
+
390
+ # Create 3D scatter plot with lines connecting points within each maturity
391
+ fig = go.Figure()
392
+
393
+ # Add data as a 3D scatter plot
394
+ fig.add_trace(go.Scatter3d(
395
+ x=X, y=Y, z=Z,
396
+ mode='markers',
397
+ marker=dict(
398
+ size=3,
399
+ color=Z,
286
400
  colorscale=custom_blue_scale,
287
- contours_z=dict(
288
- show=True,
289
- usecolormap=True,
290
- highlightcolor="#0080FF",
291
- project_z=True
292
- )
293
- )
294
- ])
401
+ opacity=0.8
402
+ ),
403
+ hovertemplate="<b>%{y:.1f} days</b><br>X: %{x:.4f}<br>IV: %{z:.2f}%"
404
+ ))
405
+
406
+ # Add lines connecting points for each maturity
407
+ cumulative_index = 0
408
+ for i, m in enumerate(maturity_names):
409
+ points_count = len(x_surface[m])
410
+ indices = list(range(cumulative_index, cumulative_index + points_count))
411
+
412
+ if len(indices) > 1:
413
+ fig.add_trace(go.Scatter3d(
414
+ x=[X[j] for j in indices],
415
+ y=[Y[j] for j in indices],
416
+ z=[Z[j] for j in indices],
417
+ mode='lines',
418
+ line=dict(color='blue', width=3),
419
+ showlegend=False,
420
+ hoverinfo='none'
421
+ ))
422
+
423
+ cumulative_index += points_count
295
424
 
296
425
  # Update layout
297
426
  fig.update_layout(
298
427
  title='Implied Volatility 3D Surface',
299
428
  template='plotly_dark',
300
429
  scene=dict(
301
- xaxis_title='Log Moneyness',
430
+ xaxis_title=domain_labels.get(domain_type, 'X Domain'),
302
431
  yaxis_title='Days to Expiry',
303
- zaxis_title='Implied Volatility (%)'
432
+ zaxis_title='Implied Volatility (%)',
433
+ aspectmode='manual',
434
+ aspectratio=dict(x=1.5, y=1, z=1)
304
435
  ),
305
436
  margin=dict(l=65, r=50, b=65, t=90)
306
437
  )
@@ -33,12 +33,18 @@ def calculate_residuals(params: List[float], ytm: float, option_chain: pd.DataFr
33
33
  def fit_model(option_chain: pd.DataFrame,
34
34
  model_name: str = 'svi',
35
35
  initial_params: Optional[List[float]] = None,
36
- param_bounds: Optional[Tuple] = None) -> Tuple[pd.DataFrame, pd.DataFrame]:
36
+ param_bounds: Optional[Tuple] = None) -> pd.DataFrame:
37
37
  """
38
38
  Fit a volatility model to market data.
39
39
 
40
+ Parameters:
41
+ - option_chain: DataFrame with market data
42
+ - model_name: Type of model to fit (default: 'svi')
43
+ - initial_params: Optional initial parameters for optimization (default: model's defaults)
44
+ - param_bounds: Optional parameter bounds for optimization (default: model's defaults)
45
+
40
46
  Returns:
41
- - Tuple of (fit_results_df, fit_performance_df)
47
+ - DataFrame with all fit results and performance metrics as columns, maturity_names as index
42
48
  """
43
49
  if model_name.lower() != 'svi':
44
50
  raise VolyError(f"Model type '{model_name}' is not supported. Currently only 'svi' is available.")
@@ -47,22 +53,22 @@ def fit_model(option_chain: pd.DataFrame,
47
53
  initial_params = initial_params or SVIModel.DEFAULT_INITIAL_PARAMS
48
54
  param_bounds = param_bounds or SVIModel.DEFAULT_PARAM_BOUNDS
49
55
 
50
- # Define indices for result DataFrames
51
- results_index = ['s', 'u', 'maturity_date', 'dtm', 'ytm',
52
- 'a', 'b', 'sigma', 'rho', 'm',
53
- 'nu', 'psi', 'p', 'c', 'nu_tilde',
54
- 'oi', 'volume', 'r']
55
-
56
- performance_index = ['fit_success', 'cost', 'optimality',
57
- 'rmse', 'mae', 'r2', 'max_error', 'n_points']
56
+ # Define column names for the result DataFrame
57
+ result_columns = [
58
+ 's', 'u', 'maturity_date', 'dtm', 'ytm',
59
+ 'a', 'b', 'rho', 'm', 'sigma',
60
+ 'nu', 'psi', 'p', 'c', 'nu_tilde',
61
+ 'oi', 'volume', 'r',
62
+ 'fit_success', 'cost', 'optimality',
63
+ 'rmse', 'mae', 'r2', 'max_error', 'n_points'
64
+ ]
58
65
 
59
66
  # Get unique maturities and sort them
60
67
  unique_ytms = sorted(option_chain['ytm'].unique())
61
68
  maturity_names = [option_chain[option_chain['ytm'] == ytm]['maturity_name'].iloc[0] for ytm in unique_ytms]
62
69
 
63
- # Create empty DataFrames
64
- fit_results_df = pd.DataFrame(index=results_index, columns=maturity_names)
65
- fit_performance_df = pd.DataFrame(index=performance_index, columns=maturity_names)
70
+ # Create empty DataFrame with maturity_names as index
71
+ results_df = pd.DataFrame(index=maturity_names, columns=result_columns)
66
72
 
67
73
  # ANSI color codes for terminal output
68
74
  GREEN, RED, RESET = '\033[32m', '\033[31m', '\033[0m'
@@ -108,42 +114,38 @@ def fit_model(option_chain: pd.DataFrame,
108
114
  # Aggregate open interest and volume
109
115
  oi = maturity_data['open_interest'].sum() if 'open_interest' in maturity_data.columns else 0
110
116
  volume = maturity_data['volume'].sum() if 'volume' in maturity_data.columns else 0
111
- r = 0.0
117
+ r = maturity_data['interest_rate'].iloc[0] if 'interest_rate' in maturity_data.columns else 0
112
118
 
113
119
  # Calculate Jump-Wing parameters
114
120
  nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a, b, sigma, rho, m, ytm)
115
121
 
116
- # Store results
117
- result_values = [s, u, maturity_data['maturity_date'].iloc[0], dtm, ytm,
118
- a, b, rho, m, sigma, nu, psi, p, c, nu_tilde, oi, volume, r]
119
-
120
- for idx, val in zip(results_index, result_values):
121
- fit_results_df.loc[idx, maturity_name] = val
122
-
123
- # Store performance metrics
124
- perf_values = [result.success, result.cost, result.optimality,
125
- rmse, mae, r2, max_error, len(maturity_data)]
126
-
127
- for idx, val in zip(performance_index, perf_values):
128
- fit_performance_df.loc[idx, maturity_name] = val
122
+ # Store all results in the DataFrame
123
+ results_df.loc[maturity_name] = [
124
+ s, u, maturity_data['maturity_date'].iloc[0], dtm, ytm,
125
+ a, b, rho, m, sigma,
126
+ nu, psi, p, c, nu_tilde,
127
+ oi, volume, r,
128
+ result.success, result.cost, result.optimality,
129
+ rmse, mae, r2, max_error, len(maturity_data)
130
+ ]
129
131
 
130
132
  # Log result
131
133
  status = f'{GREEN}SUCCESS{RESET}' if result.success else f'{RED}FAILED{RESET}'
132
134
  logger.info(f'Optimization for {maturity_name}: {status}')
133
135
  logger.info('-------------------------------------')
134
136
 
135
- return fit_results_df, fit_performance_df
137
+ return results_df
136
138
 
137
139
 
138
140
  @catch_exception
139
141
  def get_iv_surface(fit_results: pd.DataFrame,
140
142
  log_moneyness_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
141
- return_domain: str = 'log_moneyness') -> Tuple[Dict[str, np.ndarray], np.ndarray]:
143
+ return_domain: str = 'log_moneyness') -> Tuple[Dict[str, np.ndarray], Dict[str, np.ndarray]]:
142
144
  """
143
145
  Generate implied volatility surface using optimized SVI parameters.
144
146
 
145
147
  Parameters:
146
- - fit_results: DataFrame from fit_model()
148
+ - fit_results: DataFrame from fit_model() with maturity names as index
147
149
  - log_moneyness_params: Tuple of (min, max, num_points) for the moneyness grid
148
150
  - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'strikes', 'delta')
149
151
 
@@ -159,30 +161,32 @@ def get_iv_surface(fit_results: pd.DataFrame,
159
161
 
160
162
  iv_surface = {}
161
163
  x_surface = {}
162
- for maturity in fit_results.columns:
164
+
165
+ # Process each maturity
166
+ for maturity in fit_results.index:
163
167
  # Calculate SVI total implied variance and convert to IV
164
168
  params = [
165
- fit_results.loc['a', maturity],
166
- fit_results.loc['b', maturity],
167
- fit_results.loc['sigma', maturity],
168
- fit_results.loc['rho', maturity],
169
- fit_results.loc['m', maturity]
169
+ fit_results.loc[maturity, 'a'],
170
+ fit_results.loc[maturity, 'b'],
171
+ fit_results.loc[maturity, 'sigma'],
172
+ fit_results.loc[maturity, 'rho'],
173
+ fit_results.loc[maturity, 'm']
170
174
  ]
171
- ytm = fit_results.loc['ytm', maturity]
175
+ ytm = fit_results.loc[maturity, 'ytm']
172
176
 
177
+ # Calculate implied volatility
173
178
  w_svi = np.array([SVIModel.svi(x, *params) for x in log_moneyness_array])
174
179
  iv_array = np.sqrt(w_svi / ytm)
175
180
  iv_surface[maturity] = iv_array
176
181
 
177
- x_domain = get_x_domain(
182
+ # Calculate x domain for this maturity
183
+ x_surface[maturity] = get_x_domain(
178
184
  log_moneyness_params=log_moneyness_params,
179
185
  return_domain=return_domain,
180
- s=fit_results.loc['s', maturity],
181
- r=fit_results.loc['r', maturity],
186
+ s=fit_results.loc[maturity, 's'],
187
+ r=fit_results.loc[maturity, 'r'],
182
188
  iv_array=iv_array,
183
- ytm=fit_results.loc['ytm', maturity]
189
+ ytm=ytm
184
190
  )
185
191
 
186
- x_surface[maturity] = x_domain
187
-
188
192
  return iv_surface, x_surface
@@ -316,10 +316,8 @@ def get_x_domain(log_moneyness_params: Tuple[float, float, int] = (-1.5, 1.5, 10
316
316
  If required parameters are missing for the specified return_domain.
317
317
  """
318
318
 
319
- # Extract log-moneyness parameters
319
+ # Extract log-moneyness parameters and generate array
320
320
  min_m, max_m, num_points = log_moneyness_params
321
-
322
- # Generate the base log-moneyness array
323
321
  log_moneyness = np.linspace(min_m, max_m, num_points)
324
322
 
325
323
  # Handle different return domains
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.64
3
+ Version: 0.0.66
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
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