voly 0.0.39__py3-none-any.whl → 0.0.41__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/core/charts.py +39 -42
- voly/core/fit.py +1 -1
- voly/core/rnd.py +1 -4
- {voly-0.0.39.dist-info → voly-0.0.41.dist-info}/METADATA +1 -1
- {voly-0.0.39.dist-info → voly-0.0.41.dist-info}/RECORD +8 -8
- {voly-0.0.39.dist-info → voly-0.0.41.dist-info}/LICENSE +0 -0
- {voly-0.0.39.dist-info → voly-0.0.41.dist-info}/WHEEL +0 -0
- {voly-0.0.39.dist-info → voly-0.0.41.dist-info}/top_level.txt +0 -0
voly/core/charts.py
CHANGED
|
@@ -22,11 +22,11 @@ pio.renderers.default = "browser"
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@catch_exception
|
|
25
|
-
def plot_volatility_smile(
|
|
26
|
-
|
|
27
|
-
market_data:
|
|
25
|
+
def plot_volatility_smile(moneyness_array: np.ndarray,
|
|
26
|
+
iv_values: np.ndarray,
|
|
27
|
+
market_data: pd.DataFrame = None,
|
|
28
28
|
expiry: Optional[float] = None,
|
|
29
|
-
|
|
29
|
+
) -> go.Figure:
|
|
30
30
|
"""
|
|
31
31
|
Plot volatility smile for a single expiry.
|
|
32
32
|
|
|
@@ -45,8 +45,8 @@ def plot_volatility_smile(moneyness: np.ndarray,
|
|
|
45
45
|
# Add model curve
|
|
46
46
|
fig.add_trace(
|
|
47
47
|
go.Scatter(
|
|
48
|
-
x=
|
|
49
|
-
y=
|
|
48
|
+
x=moneyness_array,
|
|
49
|
+
y=iv_values * 100, # Convert to percentage
|
|
50
50
|
mode='lines',
|
|
51
51
|
name='Model',
|
|
52
52
|
line=dict(color='#0080FF', width=2)
|
|
@@ -79,15 +79,12 @@ def plot_volatility_smile(moneyness: np.ndarray,
|
|
|
79
79
|
)
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
maturity_name = expiry_data['maturity_name'].iloc[0]
|
|
85
|
-
dte_value = expiry_data['dte'].iloc[0]
|
|
86
|
-
title = f'Vol Smile for {maturity_name} (DTE: {dte_value:.1f})'
|
|
82
|
+
maturity_name = expiry_data['maturity_name'].iloc[0]
|
|
83
|
+
dte_value = expiry_data['dte'].iloc[0]
|
|
87
84
|
|
|
88
85
|
# Update layout
|
|
89
86
|
fig.update_layout(
|
|
90
|
-
title=
|
|
87
|
+
title=f'Vol Smile for {maturity_name} (DTE: {dte_value:.1f})',
|
|
91
88
|
xaxis_title='Log Moneyness',
|
|
92
89
|
yaxis_title='Implied Volatility (%)',
|
|
93
90
|
template='plotly_dark',
|
|
@@ -98,7 +95,7 @@ def plot_volatility_smile(moneyness: np.ndarray,
|
|
|
98
95
|
|
|
99
96
|
|
|
100
97
|
@catch_exception
|
|
101
|
-
def plot_all_smiles(
|
|
98
|
+
def plot_all_smiles(moneyness_array: np.ndarray,
|
|
102
99
|
iv_surface: Dict[float, np.ndarray],
|
|
103
100
|
market_data: Optional[pd.DataFrame] = None) -> List[go.Figure]:
|
|
104
101
|
"""
|
|
@@ -120,8 +117,8 @@ def plot_all_smiles(moneyness: np.ndarray,
|
|
|
120
117
|
# Create a figure for each expiry
|
|
121
118
|
for expiry in sorted_expiries:
|
|
122
119
|
fig = plot_volatility_smile(
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
moneyness_array=moneyness_array,
|
|
121
|
+
iv_values=iv_surface[expiry],
|
|
125
122
|
market_data=market_data,
|
|
126
123
|
expiry=expiry
|
|
127
124
|
)
|
|
@@ -346,13 +343,13 @@ def plot_fit_performance(fit_performance: pd.DataFrame) -> go.Figure:
|
|
|
346
343
|
|
|
347
344
|
|
|
348
345
|
@catch_exception
|
|
349
|
-
def plot_3d_surface(
|
|
346
|
+
def plot_3d_surface(moneyness_array: np.ndarray,
|
|
350
347
|
iv_surface: dict[float, np.ndarray]) -> go.Figure:
|
|
351
348
|
"""
|
|
352
349
|
Plot 3D implied volatility surface.
|
|
353
350
|
|
|
354
351
|
Parameters:
|
|
355
|
-
-
|
|
352
|
+
- moneyness_array: grid of moneyness values
|
|
356
353
|
- iv_surface: Dictionary mapping expiry times to IV arrays
|
|
357
354
|
|
|
358
355
|
Returns:
|
|
@@ -361,13 +358,13 @@ def plot_3d_surface(moneyness_grid: np.ndarray,
|
|
|
361
358
|
start_color = '#60AEFC'
|
|
362
359
|
end_color = '#002040' # Darker blue
|
|
363
360
|
custom_blue_scale = [[0, start_color], [1, end_color]]
|
|
364
|
-
|
|
361
|
+
maturities = list(iv_surface.keys())
|
|
365
362
|
|
|
366
363
|
# Convert implied volatility surface to array
|
|
367
|
-
z_array = np.array([iv_surface[
|
|
364
|
+
z_array = np.array([iv_surface[m] for m in maturities])
|
|
368
365
|
|
|
369
366
|
# Create mesh grid
|
|
370
|
-
X, Y = np.meshgrid(
|
|
367
|
+
X, Y = np.meshgrid(moneyness_array, maturities)
|
|
371
368
|
Z = z_array * 100 # Convert to percentage
|
|
372
369
|
|
|
373
370
|
# Create 3D surface plot with custom blue colorscale
|
|
@@ -383,7 +380,7 @@ def plot_3d_surface(moneyness_grid: np.ndarray,
|
|
|
383
380
|
template='plotly_dark',
|
|
384
381
|
scene=dict(
|
|
385
382
|
xaxis_title='Log Moneyness',
|
|
386
|
-
yaxis_title='
|
|
383
|
+
yaxis_title='Maturities',
|
|
387
384
|
zaxis_title='Implied Volatility (%)'
|
|
388
385
|
),
|
|
389
386
|
margin=dict(l=65, r=50, b=65, t=90)
|
|
@@ -393,7 +390,7 @@ def plot_3d_surface(moneyness_grid: np.ndarray,
|
|
|
393
390
|
|
|
394
391
|
|
|
395
392
|
@catch_exception
|
|
396
|
-
def plot_rnd(
|
|
393
|
+
def plot_rnd(moneyness_array: np.ndarray,
|
|
397
394
|
rnd_values: np.ndarray,
|
|
398
395
|
spot_price: float = 1.0,
|
|
399
396
|
title: str = 'Risk-Neutral Density') -> go.Figure:
|
|
@@ -401,7 +398,7 @@ def plot_rnd(moneyness_grid: np.ndarray,
|
|
|
401
398
|
Plot risk-neutral density (RND).
|
|
402
399
|
|
|
403
400
|
Parameters:
|
|
404
|
-
-
|
|
401
|
+
- moneyness_array: Grid of log-moneyness values
|
|
405
402
|
- rnd_values: RND values
|
|
406
403
|
- spot_price: Spot price for converting to absolute prices
|
|
407
404
|
- title: Plot title
|
|
@@ -413,10 +410,10 @@ def plot_rnd(moneyness_grid: np.ndarray,
|
|
|
413
410
|
fig = go.Figure()
|
|
414
411
|
|
|
415
412
|
# Convert to prices and normalize RND
|
|
416
|
-
prices = spot_price * np.exp(
|
|
413
|
+
prices = spot_price * np.exp(moneyness_array)
|
|
417
414
|
|
|
418
415
|
# Normalize the RND to integrate to 1
|
|
419
|
-
dx =
|
|
416
|
+
dx = moneyness_array[1] - moneyness_array[0]
|
|
420
417
|
total_density = np.sum(rnd_values) * dx
|
|
421
418
|
normalized_rnd = rnd_values / total_density if total_density > 0 else rnd_values
|
|
422
419
|
|
|
@@ -463,7 +460,7 @@ def plot_rnd(moneyness_grid: np.ndarray,
|
|
|
463
460
|
|
|
464
461
|
|
|
465
462
|
@catch_exception
|
|
466
|
-
def plot_rnd_all_expiries(
|
|
463
|
+
def plot_rnd_all_expiries(moneyness_array: np.ndarray,
|
|
467
464
|
rnd_surface: Dict[str, np.ndarray],
|
|
468
465
|
param_matrix: pd.DataFrame,
|
|
469
466
|
spot_price: float = 1.0) -> go.Figure:
|
|
@@ -471,7 +468,7 @@ def plot_rnd_all_expiries(moneyness_grid: np.ndarray,
|
|
|
471
468
|
Plot risk-neutral densities for all expiries.
|
|
472
469
|
|
|
473
470
|
Parameters:
|
|
474
|
-
-
|
|
471
|
+
- moneyness_array: Grid of log-moneyness values
|
|
475
472
|
- rnd_surface: Dictionary mapping maturity names to RND arrays
|
|
476
473
|
- param_matrix: Matrix containing model parameters with maturity info
|
|
477
474
|
- spot_price: Spot price for converting to absolute prices
|
|
@@ -494,7 +491,7 @@ def plot_rnd_all_expiries(moneyness_grid: np.ndarray,
|
|
|
494
491
|
for i in range(n_maturities)]
|
|
495
492
|
|
|
496
493
|
# Convert to prices
|
|
497
|
-
prices = spot_price * np.exp(
|
|
494
|
+
prices = spot_price * np.exp(moneyness_array)
|
|
498
495
|
|
|
499
496
|
# Add traces for each expiry
|
|
500
497
|
for i, maturity_name in enumerate(maturity_names):
|
|
@@ -502,7 +499,7 @@ def plot_rnd_all_expiries(moneyness_grid: np.ndarray,
|
|
|
502
499
|
dte = dte_values[maturity_name]
|
|
503
500
|
|
|
504
501
|
# Normalize the RND
|
|
505
|
-
dx =
|
|
502
|
+
dx = moneyness_array[1] - moneyness_array[0]
|
|
506
503
|
total_density = np.sum(rnd) * dx
|
|
507
504
|
normalized_rnd = rnd / total_density if total_density > 0 else rnd
|
|
508
505
|
|
|
@@ -543,7 +540,7 @@ def plot_rnd_all_expiries(moneyness_grid: np.ndarray,
|
|
|
543
540
|
|
|
544
541
|
|
|
545
542
|
@catch_exception
|
|
546
|
-
def plot_rnd_3d(
|
|
543
|
+
def plot_rnd_3d(moneyness_array: np.ndarray,
|
|
547
544
|
rnd_surface: Dict[str, np.ndarray],
|
|
548
545
|
param_matrix: pd.DataFrame,
|
|
549
546
|
spot_price: float = 1.0) -> go.Figure:
|
|
@@ -551,7 +548,7 @@ def plot_rnd_3d(moneyness_grid: np.ndarray,
|
|
|
551
548
|
Plot 3D surface of risk-neutral densities.
|
|
552
549
|
|
|
553
550
|
Parameters:
|
|
554
|
-
-
|
|
551
|
+
- moneyness_array: Grid of log-moneyness values
|
|
555
552
|
- rnd_surface: Dictionary mapping maturity names to RND arrays
|
|
556
553
|
- param_matrix: Matrix containing model parameters with maturity info
|
|
557
554
|
- spot_price: Spot price for converting to absolute prices
|
|
@@ -569,7 +566,7 @@ def plot_rnd_3d(moneyness_grid: np.ndarray,
|
|
|
569
566
|
dte_list = [dte_values[name] for name in maturity_names]
|
|
570
567
|
|
|
571
568
|
# Convert to prices
|
|
572
|
-
prices = spot_price * np.exp(
|
|
569
|
+
prices = spot_price * np.exp(moneyness_array)
|
|
573
570
|
|
|
574
571
|
# Create z-data matrix and normalize RNDs
|
|
575
572
|
z_data = np.zeros((len(maturity_names), len(prices)))
|
|
@@ -578,7 +575,7 @@ def plot_rnd_3d(moneyness_grid: np.ndarray,
|
|
|
578
575
|
rnd = rnd_surface[name]
|
|
579
576
|
|
|
580
577
|
# Normalize the RND
|
|
581
|
-
dx =
|
|
578
|
+
dx = moneyness_array[1] - moneyness_array[0]
|
|
582
579
|
total_density = np.sum(rnd) * dx
|
|
583
580
|
normalized_rnd = rnd / total_density if total_density > 0 else rnd
|
|
584
581
|
|
|
@@ -770,7 +767,7 @@ def plot_rnd_statistics(rnd_statistics: pd.DataFrame,
|
|
|
770
767
|
|
|
771
768
|
|
|
772
769
|
@catch_exception
|
|
773
|
-
def plot_cdf(
|
|
770
|
+
def plot_cdf(moneyness_array: np.ndarray,
|
|
774
771
|
rnd_values: np.ndarray,
|
|
775
772
|
spot_price: float = 1.0,
|
|
776
773
|
title: str = 'Cumulative Distribution Function') -> go.Figure:
|
|
@@ -778,7 +775,7 @@ def plot_cdf(moneyness_grid: np.ndarray,
|
|
|
778
775
|
Plot the cumulative distribution function (CDF) from RND values.
|
|
779
776
|
|
|
780
777
|
Parameters:
|
|
781
|
-
-
|
|
778
|
+
- moneyness_array: Grid of log-moneyness values
|
|
782
779
|
- rnd_values: RND values
|
|
783
780
|
- spot_price: Spot price for converting to absolute prices
|
|
784
781
|
- title: Plot title
|
|
@@ -787,10 +784,10 @@ def plot_cdf(moneyness_grid: np.ndarray,
|
|
|
787
784
|
- Plotly figure
|
|
788
785
|
"""
|
|
789
786
|
# Convert to prices and normalize RND
|
|
790
|
-
prices = spot_price * np.exp(
|
|
787
|
+
prices = spot_price * np.exp(moneyness_array)
|
|
791
788
|
|
|
792
789
|
# Normalize the RND
|
|
793
|
-
dx =
|
|
790
|
+
dx = moneyness_array[1] - moneyness_array[0]
|
|
794
791
|
total_density = np.sum(rnd_values) * dx
|
|
795
792
|
normalized_rnd = rnd_values / total_density if total_density > 0 else rnd_values
|
|
796
793
|
|
|
@@ -850,7 +847,7 @@ def plot_cdf(moneyness_grid: np.ndarray,
|
|
|
850
847
|
|
|
851
848
|
|
|
852
849
|
@catch_exception
|
|
853
|
-
def plot_pdf(
|
|
850
|
+
def plot_pdf(moneyness_array: np.ndarray,
|
|
854
851
|
rnd_values: np.ndarray,
|
|
855
852
|
spot_price: float = 1.0,
|
|
856
853
|
title: str = 'Probability Density Function') -> go.Figure:
|
|
@@ -858,7 +855,7 @@ def plot_pdf(moneyness_grid: np.ndarray,
|
|
|
858
855
|
Plot the probability density function (PDF) from RND values.
|
|
859
856
|
|
|
860
857
|
Parameters:
|
|
861
|
-
-
|
|
858
|
+
- moneyness_array: Grid of log-moneyness values
|
|
862
859
|
- rnd_values: RND values
|
|
863
860
|
- spot_price: Spot price for converting to absolute prices
|
|
864
861
|
- title: Plot title
|
|
@@ -867,7 +864,7 @@ def plot_pdf(moneyness_grid: np.ndarray,
|
|
|
867
864
|
- Plotly figure
|
|
868
865
|
"""
|
|
869
866
|
# This is essentially the same as plot_rnd but with a different title
|
|
870
|
-
return plot_rnd(
|
|
867
|
+
return plot_rnd(moneyness_array, rnd_values, spot_price, title)
|
|
871
868
|
|
|
872
869
|
|
|
873
870
|
@catch_exception
|
|
@@ -886,13 +883,13 @@ def plot_interpolated_surface(
|
|
|
886
883
|
- Plotly figure
|
|
887
884
|
"""
|
|
888
885
|
# Extract data from interpolation results
|
|
889
|
-
|
|
886
|
+
moneyness_array = interp_results['moneyness_array']
|
|
890
887
|
target_expiries_years = interp_results['target_expiries_years']
|
|
891
888
|
iv_surface = interp_results['iv_surface']
|
|
892
889
|
|
|
893
890
|
# Create a 3D surface plot
|
|
894
891
|
fig = plot_3d_surface(
|
|
895
|
-
moneyness=
|
|
892
|
+
moneyness=moneyness_array,
|
|
896
893
|
expiries=target_expiries_years,
|
|
897
894
|
iv_surface=iv_surface,
|
|
898
895
|
title=title
|
voly/core/fit.py
CHANGED
|
@@ -295,6 +295,6 @@ def get_iv_surface(fit_results: Dict[str, Any],
|
|
|
295
295
|
for maturity, yte in zip(maturity_values, yte_values):
|
|
296
296
|
svi_params = param_matrix[maturity].values
|
|
297
297
|
w_svi = [SVIModel.svi(x, *svi_params) for x in moneyness_array]
|
|
298
|
-
iv_surface[
|
|
298
|
+
iv_surface[maturity] = np.sqrt(np.array(w_svi) / yte)
|
|
299
299
|
|
|
300
300
|
return moneyness_array, iv_surface
|
voly/core/rnd.py
CHANGED
|
@@ -43,11 +43,9 @@ def get_rnd_surface(fit_results: Dict[str, Any],
|
|
|
43
43
|
maturity_values = fit_results['fit_performance']['Maturity']
|
|
44
44
|
param_matrix = fit_results['raw_param_matrix']
|
|
45
45
|
|
|
46
|
-
# Generate
|
|
46
|
+
# Generate rnd for each expiry
|
|
47
47
|
for maturity, yte in zip(maturity_values, yte_values):
|
|
48
48
|
svi_params_list = list(param_matrix[maturity].values)
|
|
49
|
-
|
|
50
|
-
# Adjust parameter order to match function signatures
|
|
51
49
|
a, b, sigma, rho, m = svi_params_list
|
|
52
50
|
|
|
53
51
|
# Calculate total variance
|
|
@@ -55,7 +53,6 @@ def get_rnd_surface(fit_results: Dict[str, Any],
|
|
|
55
53
|
|
|
56
54
|
# Calculate risk-neutral density using the base RND function
|
|
57
55
|
rnd_values = np.array([rnd(x, var) for x, var in zip(moneyness_array, total_var)])
|
|
58
|
-
|
|
59
56
|
rnd_surface[maturity] = rnd_values
|
|
60
57
|
|
|
61
58
|
return moneyness_array, rnd_surface
|
|
@@ -4,15 +4,15 @@ voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
|
|
|
4
4
|
voly/formulas.py,sha256=7NQZtYWFT_VOyAVNQHLB5-8caGAdtzU8y8eFcYJsZn8,7256
|
|
5
5
|
voly/models.py,sha256=YJ12aamLz_-aOni4Qm0_XV9u4bjKK3vfJz0J2gc1h0o,3565
|
|
6
6
|
voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
|
|
7
|
-
voly/core/charts.py,sha256
|
|
7
|
+
voly/core/charts.py,sha256=-pynPs7s7WPZUqQhBrxFvVnetEzeCNL2CVJmkSuhx9g,26597
|
|
8
8
|
voly/core/data.py,sha256=Dfk-ByHpdteUiLXr0p-wRLr3VAmdyjdDBKwjwdTgCjA,9939
|
|
9
|
-
voly/core/fit.py,sha256=
|
|
9
|
+
voly/core/fit.py,sha256=O4PMihVWI1NIEFU4_RkKvT73p0Jk0tB-ot5moMXAW78,9950
|
|
10
10
|
voly/core/interpolate.py,sha256=ztVIePJZOh-CIbn69wkh1JW2rKywNe2FEewRN0zcSAo,8185
|
|
11
|
-
voly/core/rnd.py,sha256=
|
|
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.41.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
|
|
15
|
+
voly-0.0.41.dist-info/METADATA,sha256=MOauUodPYfgz4Ie1_FAtCObTNfPIX2iUX8ZZ5AVr1H8,4092
|
|
16
|
+
voly-0.0.41.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
17
|
+
voly-0.0.41.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
|
|
18
|
+
voly-0.0.41.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|