voly 0.0.69__tar.gz → 0.0.71__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.69
3
+ Version: 0.0.71
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.69"
7
+ version = "0.0.71"
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.69"
63
+ python_version = "0.0.71"
64
64
  warn_return_any = true
65
65
  warn_unused_configs = true
66
66
  disallow_untyped_defs = true
@@ -373,41 +373,50 @@ class VolyClient:
373
373
  return {'iv_surface': iv_surface, 'x_surface': x_surface}
374
374
 
375
375
  @staticmethod
376
- def plot_model(fit_results: Dict[str, Any],
376
+ def plot_model(fit_results: pd.DataFrame,
377
377
  option_chain: pd.DataFrame = None,
378
- moneyness_params: Tuple[float, float, int] = (-2, 2, 500)
379
- ) -> Dict[str, go.Figure]:
378
+ log_moneyness_params: Tuple[float, float, int] = (-2, 2, 500),
379
+ return_domain: str = 'log_moneyness',
380
+ ) -> Dict[str, Any]:
380
381
  """
381
- Generate all plots for the fitted model and RND results.
382
+ Generate all plots for the fitted model.
382
383
 
383
384
  Parameters:
384
- - fit_results: Dictionary with fitting results from fit_model()
385
+ - fit_results: DataFrame with fitting results from fit_model()
385
386
  - option_chain: Optional market data for comparison
386
- - moneyness_params: Grid of log-moneyness values
387
+ - log_moneyness_params: Grid of log-moneyness values
388
+ - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'strikes', 'delta')
387
389
 
388
390
  Returns:
389
391
  - Dictionary of plot figures
390
392
  """
391
393
  plots = {}
392
394
 
393
- moneyness_array, iv_surface = get_iv_surface(fit_results, moneyness_params)
394
-
395
- # Extract data from fit results
396
- raw_param_matrix = fit_results['raw_param_matrix']
397
- jw_param_matrix = fit_results['jw_param_matrix']
398
- fit_performance = fit_results['fit_performance']
395
+ # Generate IV surface and domain
396
+ iv_surface, x_surface = get_iv_surface(fit_results, log_moneyness_params, return_domain)
399
397
 
400
398
  # Plot volatility smiles
401
- plots['smiles'] = plot_all_smiles(moneyness_array, iv_surface, option_chain)
402
-
403
- # Plot 3D surface
404
- plots['surface_3d'] = plot_3d_surface(moneyness_array, iv_surface)
399
+ plots['smiles'] = plot_all_smiles(
400
+ x_surface=x_surface,
401
+ iv_surface=iv_surface,
402
+ option_chain=option_chain,
403
+ domain_type=return_domain
404
+ )
405
405
 
406
406
  # Plot parameters
407
- plots['raw_params'], plots['jw_params'] = plot_parameters(raw_param_matrix, jw_param_matrix)
407
+ plots['raw_params'] = plot_parameters(fit_results)
408
+ plots['jw_params'] = plot_jw_parameters(fit_results)
409
+
410
+ # Plot fit statistics
411
+ plots['fit_performance'] = plot_fit_performance(fit_results)
408
412
 
409
- # Plot fit statistics if available
410
- plots['fit_performance'] = plot_fit_performance(fit_performance)
413
+ # Plot 3D surface
414
+ plots['surface_3d'] = plot_3d_surface(
415
+ x_surface=x_surface,
416
+ iv_surface=iv_surface,
417
+ fit_results=fit_results,
418
+ domain_type=return_domain
419
+ )
411
420
 
412
421
  return plots
413
422
 
@@ -73,11 +73,11 @@ def plot_volatility_smile(x_domain: np.ndarray,
73
73
  s = maturity_data['underlying_price'].iloc[0]
74
74
  market_x = s / np.exp(market_x)
75
75
  elif domain_type == 'delta':
76
- # For delta, we'd need more complex conversion - only show model curve
77
- pass
76
+ # For delta, we'd need more complex conversion - skip market data for this domain
77
+ market_x = None
78
78
 
79
79
  # Add bid and ask IVs if the domain type allows
80
- if domain_type != 'delta':
80
+ if domain_type != 'delta' and market_x is not None:
81
81
  for iv_type in ['bid_iv', 'ask_iv']:
82
82
  if iv_type in maturity_data.columns:
83
83
  fig.add_trace(
@@ -92,14 +92,21 @@ def plot_volatility_smile(x_domain: np.ndarray,
92
92
 
93
93
  dte_value = maturity_data['dtm'].iloc[0]
94
94
 
95
- # Update layout
96
- fig.update_layout(
97
- title=f'Vol Smile for {maturity} (DTE: {dte_value:.1f})',
98
- xaxis_title=domain_labels.get(domain_type, 'X Domain'),
99
- yaxis_title='Implied Volatility (%)',
100
- template='plotly_dark',
101
- legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1)
102
- )
95
+ # Update layout with DTE
96
+ title = f'Vol Smile for {maturity} (DTE: {dte_value:.1f})'
97
+ else:
98
+ title = f'Vol Smile for {maturity}'
99
+ else:
100
+ title = 'Volatility Smile'
101
+
102
+ # Update layout
103
+ fig.update_layout(
104
+ title=title,
105
+ xaxis_title=domain_labels.get(domain_type, 'X Domain'),
106
+ yaxis_title='Implied Volatility (%)',
107
+ template='plotly_dark',
108
+ legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1)
109
+ )
103
110
 
104
111
  return fig
105
112
 
@@ -158,7 +165,7 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
158
165
  maturity_names = fit_results.index
159
166
 
160
167
  # Create hover text with maturity info
161
- tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f})"
168
+ tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f}, YTE: {fit_results.loc[m, 'ytm']:.4f})"
162
169
  for m in maturity_names]
163
170
 
164
171
  # Plot each parameter
@@ -191,7 +198,8 @@ def plot_parameters(fit_results: pd.DataFrame) -> go.Figure:
191
198
  fig.update_layout(
192
199
  title='Raw SVI Parameters Across Expiries',
193
200
  template='plotly_dark',
194
- showlegend=False
201
+ showlegend=False,
202
+ height=800
195
203
  )
196
204
 
197
205
  return fig
@@ -222,7 +230,7 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
222
230
  maturity_names = fit_results.index
223
231
 
224
232
  # Create hover text with maturity info
225
- tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f})"
233
+ tick_labels = [f"{m} (DTE: {fit_results.loc[m, 'dtm']:.1f}, YTE: {fit_results.loc[m, 'ytm']:.4f})"
226
234
  for m in maturity_names]
227
235
 
228
236
  # Plot each parameter
@@ -255,7 +263,8 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
255
263
  fig.update_layout(
256
264
  title='Jump-Wing Parameters Across Expiries',
257
265
  template='plotly_dark',
258
- showlegend=False
266
+ showlegend=False,
267
+ height=800
259
268
  )
260
269
 
261
270
  return fig
@@ -326,7 +335,8 @@ def plot_fit_performance(fit_results: pd.DataFrame) -> go.Figure:
326
335
  fig.update_layout(
327
336
  title='Model Fitting Accuracy Statistics',
328
337
  template='plotly_dark',
329
- showlegend=False
338
+ showlegend=False,
339
+ height=700
330
340
  )
331
341
 
332
342
  return fig
@@ -354,7 +364,7 @@ def plot_3d_surface(x_surface: Dict[str, np.ndarray],
354
364
  'log_moneyness': 'Log Moneyness',
355
365
  'moneyness': 'Moneyness (S/K)',
356
366
  'strikes': 'Strike Price',
357
- 'delta': 'Delta'
367
+ 'delta': 'Call Delta'
358
368
  }
359
369
 
360
370
  # Define custom colorscale
@@ -371,57 +381,54 @@ def plot_3d_surface(x_surface: Dict[str, np.ndarray],
371
381
  # Default to sequential values
372
382
  maturity_values = list(range(len(maturity_names)))
373
383
 
374
- # Create a mesh grid for each maturity to handle different x domains
375
- X = []
376
- Y = []
377
- Z = []
378
-
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()
384
+ # For domains with uniform x values across maturities (like log_moneyness, moneyness)
385
+ # we can use standard surface plot
386
+ if domain_type in ['log_moneyness', 'moneyness']:
387
+ # Create mesh grid
388
+ X, Y = np.meshgrid(list(x_surface.values())[0], maturity_values)
389
+ Z = np.array([iv_surface[m] * 100 for m in maturity_names]) # Convert to percentage
390
+
391
+ # Create figure
392
+ fig = go.Figure(data=[
393
+ go.Surface(
394
+ z=Z,
395
+ x=X,
396
+ y=Y,
397
+ colorscale=custom_blue_scale,
398
+ contours_z=dict(
399
+ show=True,
400
+ usecolormap=True,
401
+ highlightcolor="#0080FF",
402
+ project_z=True
403
+ )
404
+ )
405
+ ])
392
406
 
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,
400
- colorscale=custom_blue_scale,
401
- opacity=0.8
402
- ),
403
- hovertemplate="<b>%{y:.1f} days</b><br>X: %{x:.4f}<br>IV: %{z:.2f}%"
404
- ))
407
+ # For domains that might have different x values per maturity (like strikes, delta)
408
+ # we need to use a different approach
409
+ else:
410
+ # Create a 3D scatter plot with lines
411
+ fig = go.Figure()
405
412
 
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))
413
+ # For each maturity, create a curve
414
+ for i, maturity in enumerate(maturity_names):
415
+ x_values = x_surface[maturity]
416
+ z_values = iv_surface[maturity] * 100 # Convert to percentage
417
+ y_values = np.full_like(x_values, maturity_values[i])
411
418
 
412
- if len(indices) > 1:
419
+ # Add a line for this maturity
413
420
  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],
421
+ x=x_values,
422
+ y=y_values,
423
+ z=z_values,
417
424
  mode='lines',
418
- line=dict(color='blue', width=3),
419
- showlegend=False,
420
- hoverinfo='none'
425
+ line=dict(
426
+ color=f'rgb({30 + 225 * (i / len(maturity_names))}, {30 + 150 * (i / len(maturity_names))}, {200 - 170 * (i / len(maturity_names))})',
427
+ width=5
428
+ ),
429
+ name=maturity
421
430
  ))
422
431
 
423
- cumulative_index += points_count
424
-
425
432
  # Update layout
426
433
  fig.update_layout(
427
434
  title='Implied Volatility 3D Surface',
@@ -431,7 +438,10 @@ def plot_3d_surface(x_surface: Dict[str, np.ndarray],
431
438
  yaxis_title='Days to Expiry',
432
439
  zaxis_title='Implied Volatility (%)',
433
440
  aspectmode='manual',
434
- aspectratio=dict(x=1.5, y=1, z=1)
441
+ aspectratio=dict(x=1.5, y=1, z=1),
442
+ camera=dict(
443
+ eye=dict(x=1.5, y=-1.5, z=1)
444
+ )
435
445
  ),
436
446
  margin=dict(l=65, r=50, b=65, t=90)
437
447
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.69
3
+ Version: 0.0.71
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
File without changes