voly 0.0.69__py3-none-any.whl → 0.0.71__py3-none-any.whl
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/client.py +28 -19
- voly/core/charts.py +72 -62
- {voly-0.0.69.dist-info → voly-0.0.71.dist-info}/METADATA +1 -1
- {voly-0.0.69.dist-info → voly-0.0.71.dist-info}/RECORD +7 -7
- {voly-0.0.69.dist-info → voly-0.0.71.dist-info}/LICENSE +0 -0
- {voly-0.0.69.dist-info → voly-0.0.71.dist-info}/WHEEL +0 -0
- {voly-0.0.69.dist-info → voly-0.0.71.dist-info}/top_level.txt +0 -0
voly/client.py
CHANGED
|
@@ -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:
|
|
376
|
+
def plot_model(fit_results: pd.DataFrame,
|
|
377
377
|
option_chain: pd.DataFrame = None,
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
382
|
+
Generate all plots for the fitted model.
|
|
382
383
|
|
|
383
384
|
Parameters:
|
|
384
|
-
- fit_results:
|
|
385
|
+
- fit_results: DataFrame with fitting results from fit_model()
|
|
385
386
|
- option_chain: Optional market data for comparison
|
|
386
|
-
-
|
|
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
|
-
|
|
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(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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']
|
|
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
|
|
410
|
-
plots['
|
|
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
|
|
voly/core/charts.py
CHANGED
|
@@ -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 -
|
|
77
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
#
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
#
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
419
|
+
# Add a line for this maturity
|
|
413
420
|
fig.add_trace(go.Scatter3d(
|
|
414
|
-
x=
|
|
415
|
-
y=
|
|
416
|
-
z=
|
|
421
|
+
x=x_values,
|
|
422
|
+
y=y_values,
|
|
423
|
+
z=z_values,
|
|
417
424
|
mode='lines',
|
|
418
|
-
line=dict(
|
|
419
|
-
|
|
420
|
-
|
|
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,18 +1,18 @@
|
|
|
1
1
|
voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
|
|
2
|
-
voly/client.py,sha256
|
|
2
|
+
voly/client.py,sha256=-5-eMMYZwyRZcyupkTyNDUDhCnUwCv4NJzqb20_IRy4,20725
|
|
3
3
|
voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
|
|
4
4
|
voly/formulas.py,sha256=wSbGAH6GuQThT9QyQY4Ud2eUf9fo1YFHglUmP6fNris,11871
|
|
5
5
|
voly/models.py,sha256=LXXIlpXZQEfXTuCngxC8Hd3bWtw6wdXDCSGxTLmHM-c,3659
|
|
6
6
|
voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
|
|
7
|
-
voly/core/charts.py,sha256=
|
|
7
|
+
voly/core/charts.py,sha256=sGf6VFfcHyGs4hqoIIc1pKdpgt7g3Rd9iAcTbAxC6Gw,29031
|
|
8
8
|
voly/core/data.py,sha256=e8qBArubNqPkrfuIYB_q2WhRf7TKzA4Z3FhMC-xyLEE,8862
|
|
9
9
|
voly/core/fit.py,sha256=JOr2XjM-I9HtfbyEN0tdGuNCZimQ2ttm4lNUpF-tKb4,9226
|
|
10
10
|
voly/core/interpolate.py,sha256=ztVIePJZOh-CIbn69wkh1JW2rKywNe2FEewRN0zcSAo,8185
|
|
11
11
|
voly/core/rnd.py,sha256=8FTU-Qp9epW9yE4XSOdiFGIRXrGyXqF6mVgZn1NMvxk,11813
|
|
12
12
|
voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
|
|
13
13
|
voly/utils/logger.py,sha256=4-_2bVJmq17Q0d7Rd2mPg1AeR8gxv6EPvcmBDMFWcSM,1744
|
|
14
|
-
voly-0.0.
|
|
15
|
-
voly-0.0.
|
|
16
|
-
voly-0.0.
|
|
17
|
-
voly-0.0.
|
|
18
|
-
voly-0.0.
|
|
14
|
+
voly-0.0.71.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
|
|
15
|
+
voly-0.0.71.dist-info/METADATA,sha256=2y5OQ_mCtLFK8IMbSs5VmkmuYU2TDcBzGSnovFt4hRY,4092
|
|
16
|
+
voly-0.0.71.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
17
|
+
voly-0.0.71.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
|
|
18
|
+
voly-0.0.71.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|