voly 0.0.16__tar.gz → 0.0.18__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.16
3
+ Version: 0.0.18
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.16"
7
+ version = "0.0.18"
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.16"
63
+ python_version = "0.0.18"
64
64
  warn_return_any = true
65
65
  warn_unused_configs = true
66
66
  disallow_untyped_defs = true
@@ -323,23 +323,22 @@ class VolyClient:
323
323
 
324
324
  @staticmethod
325
325
  def get_surface(param_matrix: pd.DataFrame,
326
- log_moneyness: Tuple[float, float, int] = (-2, 2, 500)) -> Dict[str, Any]:
326
+ moneyness_params: Tuple[float, float, int] = (-2, 2, 500)
327
+ ) -> Tuple[np.ndarray, Dict[float, np.ndarray], np.ndarray]:
327
328
  """
328
329
  Generate implied volatility surface using optimized SVI parameters.
329
330
 
330
331
  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
332
+ - param_matrix: Matrix of optimized SVI parameters from fit_results
333
+ - moneyness_params: Tuple of (min, max, num_points) for the moneyness grid
333
334
 
334
335
  Returns:
335
- - Dictionary with surface generation results
336
+ - Tuple of (moneyness_grid, iv_surface)
336
337
  """
337
- logger.info("Generating implied volatility surface")
338
-
339
338
  # Generate the surface
340
339
  moneyness_grid, iv_surface = get_surface(
341
340
  param_matrix=param_matrix,
342
- log_moneyness=log_moneyness
341
+ moneyness_params=moneyness_params
343
342
  )
344
343
 
345
344
  return {
@@ -347,6 +346,45 @@ class VolyClient:
347
346
  'iv_surface': iv_surface
348
347
  }
349
348
 
349
+ @staticmethod
350
+ def plot_model(fit_results: Dict[str, Any],
351
+ market_data: pd.DataFrame = None,
352
+ moneyness_params: Tuple[float, float, int] = (-2, 2, 500)
353
+ ) -> Dict[str, go.Figure]:
354
+ """
355
+ Generate all plots for the fitted model and RND results.
356
+
357
+ Parameters:
358
+ - fit_results: Dictionary with fitting results from fit_model()
359
+ - market_data: Optional market data for comparison
360
+ - moneyness_params: Grid of log-moneyness values
361
+
362
+ Returns:
363
+ - Dictionary of plot figures
364
+ """
365
+ plots = {}
366
+
367
+ moneyness_grid, iv_surface = get_surface(fit_results['raw_params_matrix'], moneyness_params)
368
+
369
+ # Extract data from fit results
370
+ raw_param_matrix = fit_results['raw_param_matrix']
371
+ jw_param_matrix = fit_results.get('jw_param_matrix')
372
+ fit_performance = fit_results.get('fit_performance')
373
+
374
+ # Plot volatility smiles
375
+ plots['smiles'] = plot_all_smiles(moneyness_grid, iv_surface, market_data)
376
+
377
+ # Plot 3D surface
378
+ plots['surface_3d'] = plot_3d_surface(moneyness_grid, iv_surface)
379
+
380
+ # Plot parameters
381
+ plots['raw_params'], plots['jw_params'] = plot_parameters(raw_param_matrix, jw_param_matrix)
382
+
383
+ # Plot fit statistics if available
384
+ plots['fit_performance'] = plot_fit_performance(fit_performance)
385
+
386
+ return plots
387
+
350
388
  # -------------------------------------------------------------------------
351
389
  # Risk-Neutral Density (RND)
352
390
  # -------------------------------------------------------------------------
@@ -8,11 +8,11 @@ risk-neutral densities, and model fitting results.
8
8
  import numpy as np
9
9
  import pandas as pd
10
10
  from typing import Dict, List, Tuple, Optional, Union, Any
11
+ from voly.utils.logger import logger, catch_exception
12
+ from voly.models import SVIModel
11
13
  import plotly.graph_objects as go
12
14
  from plotly.subplots import make_subplots
13
15
  import plotly.io as pio
14
- from voly.utils.logger import logger, catch_exception
15
- from voly.models import SVIModel
16
16
 
17
17
  # Set default renderer to browser for interactive plots
18
18
  pio.renderers.default = "browser"
@@ -243,7 +243,7 @@ def plot_parameters(raw_param_matrix: pd.DataFrame,
243
243
 
244
244
 
245
245
  @catch_exception
246
- def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
246
+ def plot_fit_statistics(fit_performance: pd.DataFrame) -> go.Figure:
247
247
  """
248
248
  Plot the fitting accuracy statistics.
249
249
 
@@ -260,17 +260,17 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
260
260
  )
261
261
 
262
262
  # Create custom tick labels with maturity name, DTE, and YTE
263
- tick_labels = [f"{m} (DTE: {d:.1f}, YTE: {y:.4f})" for m, d, y in
264
- zip(stats_df['Maturity'], stats_df['DTE'], stats_df['YTE'])]
263
+ tick_labels = [f"{m} (DTE: {d:.1f})" for m, d in
264
+ zip(fit_performance['Maturity'], fit_performance['DTE'])]
265
265
 
266
266
  # Get x-axis values for plotting (use indices for positioning)
267
- x_indices = list(range(len(stats_df)))
267
+ x_indices = list(range(len(fit_performance)))
268
268
 
269
269
  # Plot RMSE
270
270
  fig.add_trace(
271
271
  go.Scatter(
272
272
  x=x_indices,
273
- y=stats_df['RMSE'] * 100, # Convert to percentage
273
+ y=fit_performance['RMSE'] * 100, # Convert to percentage
274
274
  mode='lines+markers',
275
275
  name='RMSE',
276
276
  line=dict(width=2),
@@ -284,7 +284,7 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
284
284
  fig.add_trace(
285
285
  go.Scatter(
286
286
  x=x_indices,
287
- y=stats_df['MAE'] * 100, # Convert to percentage
287
+ y=fit_performance['MAE'] * 100, # Convert to percentage
288
288
  mode='lines+markers',
289
289
  name='MAE',
290
290
  line=dict(width=2),
@@ -298,7 +298,7 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
298
298
  fig.add_trace(
299
299
  go.Scatter(
300
300
  x=x_indices,
301
- y=stats_df['R²'],
301
+ y=fit_performance['R²'],
302
302
  mode='lines+markers',
303
303
  name='R²',
304
304
  line=dict(width=2),
@@ -312,7 +312,7 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
312
312
  fig.add_trace(
313
313
  go.Scatter(
314
314
  x=x_indices,
315
- y=stats_df['Max Error'] * 100, # Convert to percentage
315
+ y=fit_performance['Max Error'] * 100, # Convert to percentage
316
316
  mode='lines+markers',
317
317
  name='Max Error',
318
318
  line=dict(width=2),
@@ -327,7 +327,7 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
327
327
  for col in range(1, 3):
328
328
  fig.update_xaxes(
329
329
  tickvals=x_indices,
330
- ticktext=stats_df['Maturity'],
330
+ ticktext=fit_performance['Maturity'],
331
331
  tickangle=45,
332
332
  row=row, col=col
333
333
  )
@@ -349,29 +349,27 @@ def plot_fit_statistics(stats_df: pd.DataFrame) -> go.Figure:
349
349
 
350
350
 
351
351
  @catch_exception
352
- def plot_3d_surface(moneyness: np.ndarray,
353
- expiries: np.ndarray,
354
- iv_surface: Dict[float, np.ndarray],
355
- interpolate: bool = True,
356
- title: str = 'Implied Volatility Surface') -> go.Figure:
352
+ def plot_3d_surface(moneyness_grid: np.ndarray,
353
+ iv_surface: Dict[float, np.ndarray]) -> go.Figure:
357
354
  """
358
355
  Plot 3D implied volatility surface.
359
356
 
360
357
  Parameters:
361
- - moneyness: Moneyness grid
362
- - expiries: Expiry times in years
358
+ - log_moneyness_grid: grid of log_moneyness values
363
359
  - iv_surface: Dictionary mapping expiry times to IV arrays
364
- - interpolate: Whether to interpolate the surface
365
360
  - title: Plot title
366
361
 
367
362
  Returns:
368
363
  - Plotly figure
369
364
  """
365
+
366
+ yte_values = list(iv_surface.keys())
367
+
370
368
  # Convert implied volatility surface to array
371
- z_array = np.array([iv_surface[t] for t in expiries])
369
+ z_array = np.array([iv_surface[t] for t in yte_values])
372
370
 
373
371
  # Create mesh grid
374
- X, Y = np.meshgrid(moneyness, expiries)
372
+ X, Y = np.meshgrid(moneyness_grid, yte_values)
375
373
  Z = z_array * 100 # Convert to percentage
376
374
 
377
375
  # Create 3D surface plot
@@ -383,7 +381,7 @@ def plot_3d_surface(moneyness: np.ndarray,
383
381
 
384
382
  # Update layout
385
383
  fig.update_layout(
386
- title=title,
384
+ title='Implied Volatility 3D Surface',
387
385
  template='plotly_dark',
388
386
  scene=dict(
389
387
  xaxis_title='Log Moneyness',
@@ -904,81 +902,3 @@ def plot_interpolated_surface(
904
902
 
905
903
  return fig
906
904
 
907
-
908
- @catch_exception
909
- def generate_all_plots(fit_results: Dict[str, Any],
910
- rnd_results: Optional[Dict[str, Any]] = None,
911
- market_data: Optional[pd.DataFrame] = None) -> Dict[str, go.Figure]:
912
- """
913
- Generate all plots for the fitted model and RND results.
914
-
915
- Parameters:
916
- - fit_results: Dictionary with fitting results from fit_model()
917
- - rnd_results: Optional dictionary with RND results from calculate_rnd()
918
- - market_data: Optional market data for comparison
919
-
920
- Returns:
921
- - Dictionary of plot figures
922
- """
923
- plots = {}
924
-
925
- # Extract data from fit results
926
- moneyness_grid = fit_results['moneyness_grid']
927
- iv_surface = fit_results['iv_surface']
928
- raw_param_matrix = fit_results['raw_param_matrix']
929
- jw_param_matrix = fit_results.get('jw_param_matrix')
930
- stats_df = fit_results.get('stats_df')
931
- unique_expiries = fit_results['unique_expiries']
932
-
933
- # Plot volatility smiles
934
- logger.info("Generating volatility smile plots...")
935
- plots['smiles'] = plot_all_smiles(moneyness_grid, iv_surface, market_data)
936
-
937
- # Plot 3D surface
938
- logger.info("Generating 3D volatility surface plot...")
939
- plots['surface_3d'] = plot_3d_surface(moneyness_grid, unique_expiries, iv_surface)
940
-
941
- # Plot parameters
942
- logger.info("Generating parameter plots...")
943
- plots['raw_params'], plots['jw_params'] = plot_parameters(raw_param_matrix, jw_param_matrix)
944
-
945
- # Plot fit statistics if available
946
- if stats_df is not None:
947
- logger.info("Generating fit statistics plot...")
948
- plots['fit_stats'] = plot_fit_statistics(stats_df)
949
-
950
- # Plot RND results if available
951
- if rnd_results is not None:
952
- logger.info("Generating RND plots...")
953
-
954
- # Extract RND data
955
- rnd_surface = rnd_results['rnd_surface']
956
- rnd_statistics = rnd_results['rnd_statistics']
957
- rnd_probabilities = rnd_results['rnd_probabilities']
958
- spot_price = rnd_results['spot_price']
959
-
960
- # Plot RND for each expiry
961
- plots['rnd'] = {}
962
- for maturity_name, rnd_values in rnd_surface.items():
963
- plots['rnd'][maturity_name] = plot_rnd(
964
- moneyness_grid, rnd_values, spot_price,
965
- title=f"Risk-Neutral Density - {maturity_name}"
966
- )
967
-
968
- # Plot all RNDs in one figure
969
- plots['rnd_all'] = plot_rnd_all_expiries(moneyness_grid, rnd_surface, raw_param_matrix, spot_price)
970
-
971
- # Plot 3D RND surface
972
- plots['rnd_3d'] = plot_rnd_3d(moneyness_grid, rnd_surface, raw_param_matrix, spot_price)
973
-
974
- # Plot RND statistics
975
- plots['rnd_stats'], plots['rnd_probs'] = plot_rnd_statistics(rnd_statistics, rnd_probabilities)
976
-
977
- # Plot PDF and CDF for the first expiry
978
- first_maturity = list(rnd_surface.keys())[0]
979
- first_rnd = rnd_surface[first_maturity]
980
-
981
- plots['pdf'] = plot_pdf(moneyness_grid, first_rnd, spot_price)
982
- plots['cdf'] = plot_cdf(moneyness_grid, first_rnd, spot_price)
983
-
984
- return plots
@@ -156,7 +156,7 @@ def fit_svi_parameters(market_data: pd.DataFrame,
156
156
  else:
157
157
  logger.warning(f'Optimization for {maturity_name}: {RED}FAILED{RESET}')
158
158
 
159
- logger.info('------------------------------------------')
159
+ logger.info('-------------------------------------')
160
160
 
161
161
  # Create DataFrame with all fit performance data
162
162
  fit_performance = pd.DataFrame(fit_data)
@@ -265,34 +265,35 @@ def fit_model(market_data: pd.DataFrame,
265
265
 
266
266
 
267
267
  @catch_exception
268
- def get_surface(
269
- param_matrix: pd.DataFrame,
270
- log_moneyness: Tuple[float, float, int] = (-2, 2, 500)
271
- ) -> Tuple[np.ndarray, Dict[float, np.ndarray], np.ndarray]:
268
+ def get_surface(param_matrix: pd.DataFrame,
269
+ moneyness_params: Tuple[float, float, int] = (-2, 2, 500)
270
+ ) -> Tuple[np.ndarray, Dict[float, np.ndarray], np.ndarray]:
272
271
  """
273
272
  Generate implied volatility surface using optimized SVI parameters.
274
273
 
275
274
  Parameters:
276
275
  - param_matrix: Matrix of optimized SVI parameters from fit_results
277
- - log_moneyness: Tuple of (min, max, num_points) for the moneyness grid
276
+ - moneyness_params: Tuple of (min, max, num_points) for the moneyness grid
278
277
 
279
278
  Returns:
280
- - Tuple of (moneyness_grid, iv_surface, unique_expiries)
279
+ - Tuple of (moneyness_grid, iv_surface)
281
280
  """
281
+ iv_surface = {}
282
+
282
283
  # Extract moneyness parameters
283
- min_m, max_m, num_points = log_moneyness
284
+ min_m, max_m, num_points = moneyness_params
284
285
 
285
286
  # Generate moneyness grid
286
- moneyness_values = np.linspace(min_m, max_m, num=num_points)
287
- implied_volatility_surface = {}
287
+ moneyness_grid = np.linspace(min_m, max_m, num=num_points)
288
288
 
289
289
  # Get YTE values from the parameter matrix attributes
290
- yte_values = param_matrix.attrs['yte_values']
290
+ yte_values = fit_results['fit_performance']['YTE']
291
+ maturity_values = fit_results['fit_performance']['Maturity']
291
292
 
292
293
  # Generate implied volatility for each expiry
293
- for maturity_name, yte in yte_values.items():
294
- svi_params = param_matrix[maturity_name].values
294
+ for maturity, yte in zip(maturity_values, yte_values):
295
+ svi_params = param_matrix[maturity].values
295
296
  w_svi = [SVIModel.svi(x, *svi_params) for x in moneyness_values]
296
- implied_volatility_surface[yte] = np.sqrt(np.array(w_svi) / yte)
297
+ iv_surface[yte] = np.sqrt(np.array(w_svi) / yte)
297
298
 
298
- return moneyness_values, implied_volatility_surface
299
+ return moneyness_grid, iv_surface
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.16
3
+ Version: 0.0.18
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