voly 0.0.41__py3-none-any.whl → 0.0.43__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 CHANGED
@@ -23,19 +23,18 @@ pio.renderers.default = "browser"
23
23
 
24
24
  @catch_exception
25
25
  def plot_volatility_smile(moneyness_array: np.ndarray,
26
- iv_values: np.ndarray,
26
+ iv_array: np.ndarray,
27
27
  market_data: pd.DataFrame = None,
28
- expiry: Optional[float] = None,
28
+ maturity: Optional[float] = None,
29
29
  ) -> go.Figure:
30
30
  """
31
31
  Plot volatility smile for a single expiry.
32
32
 
33
33
  Parameters:
34
- - moneyness: Moneyness grid
35
- - iv: Implied volatility values
34
+ - moneyness_array: Moneyness grid
35
+ - iv_array: Implied volatility values
36
36
  - market_data: Optional market data for comparison
37
- - expiry: Optional expiry time (in years) for filtering market data
38
- - title: Optional plot title
37
+ - maturity: Maturity name for filtering market data
39
38
 
40
39
  Returns:
41
40
  - Plotly figure
@@ -46,7 +45,7 @@ def plot_volatility_smile(moneyness_array: np.ndarray,
46
45
  fig.add_trace(
47
46
  go.Scatter(
48
47
  x=moneyness_array,
49
- y=iv_values * 100, # Convert to percentage
48
+ y=iv_array * 100, # Convert to percentage
50
49
  mode='lines',
51
50
  name='Model',
52
51
  line=dict(color='#0080FF', width=2)
@@ -54,14 +53,14 @@ def plot_volatility_smile(moneyness_array: np.ndarray,
54
53
  )
55
54
 
56
55
  # Filter market data for the specific expiry
57
- expiry_data = market_data[market_data['yte'] == expiry]
56
+ maturity_data = market_data[market_data['maturity_name'] == maturity]
58
57
 
59
- if not expiry_data.empty:
58
+ if not maturity_data.empty:
60
59
  # Add bid IV
61
60
  fig.add_trace(
62
61
  go.Scatter(
63
- x=expiry_data['log_moneyness'],
64
- y=expiry_data['bid_iv'] * 100, # Convert to percentage
62
+ x=maturity_data['log_moneyness'],
63
+ y=maturity_data['bid_iv'] * 100, # Convert to percentage
65
64
  mode='markers',
66
65
  name='Bid IV',
67
66
  marker=dict(size=8, symbol='circle', opacity=0.7)
@@ -71,20 +70,19 @@ def plot_volatility_smile(moneyness_array: np.ndarray,
71
70
  # Add ask IV
72
71
  fig.add_trace(
73
72
  go.Scatter(
74
- x=expiry_data['log_moneyness'],
75
- y=expiry_data['ask_iv'] * 100, # Convert to percentage
73
+ x=maturity_data['log_moneyness'],
74
+ y=maturity_data['ask_iv'] * 100, # Convert to percentage
76
75
  mode='markers',
77
76
  name='Ask IV',
78
77
  marker=dict(size=8, symbol='circle', opacity=0.7)
79
78
  )
80
79
  )
81
80
 
82
- maturity_name = expiry_data['maturity_name'].iloc[0]
83
- dte_value = expiry_data['dte'].iloc[0]
81
+ dte_value = maturity_data['dte'].iloc[0]
84
82
 
85
83
  # Update layout
86
84
  fig.update_layout(
87
- title=f'Vol Smile for {maturity_name} (DTE: {dte_value:.1f})',
85
+ title=f'Vol Smile for {maturity} (DTE: {dte_value:.1f})',
88
86
  xaxis_title='Log Moneyness',
89
87
  yaxis_title='Implied Volatility (%)',
90
88
  template='plotly_dark',
@@ -111,16 +109,16 @@ def plot_all_smiles(moneyness_array: np.ndarray,
111
109
  """
112
110
  figures = []
113
111
 
114
- # Sort expiries in ascending order
115
- sorted_expiries = sorted(iv_surface.keys())
112
+ # Get maturities
113
+ maturities = list(iv_surface.keys())
116
114
 
117
115
  # Create a figure for each expiry
118
- for expiry in sorted_expiries:
116
+ for maturity in maturities:
119
117
  fig = plot_volatility_smile(
120
118
  moneyness_array=moneyness_array,
121
- iv_values=iv_surface[expiry],
119
+ iv_array=iv_surface[maturity],
122
120
  market_data=market_data,
123
- expiry=expiry
121
+ maturity=maturity
124
122
  )
125
123
  figures.append(fig)
126
124
 
@@ -392,8 +390,7 @@ def plot_3d_surface(moneyness_array: np.ndarray,
392
390
  @catch_exception
393
391
  def plot_rnd(moneyness_array: np.ndarray,
394
392
  rnd_values: np.ndarray,
395
- spot_price: float = 1.0,
396
- title: str = 'Risk-Neutral Density') -> go.Figure:
393
+ spot_price: float = 1.0) -> go.Figure:
397
394
  """
398
395
  Plot risk-neutral density (RND).
399
396
 
@@ -449,7 +446,7 @@ def plot_rnd(moneyness_array: np.ndarray,
449
446
 
450
447
  # Update layout
451
448
  fig.update_layout(
452
- title=title,
449
+ title='Risk-Neutral Density',
453
450
  xaxis_title='Price',
454
451
  yaxis_title='Density',
455
452
  template='plotly_dark',
@@ -462,7 +459,7 @@ def plot_rnd(moneyness_array: np.ndarray,
462
459
  @catch_exception
463
460
  def plot_rnd_all_expiries(moneyness_array: np.ndarray,
464
461
  rnd_surface: Dict[str, np.ndarray],
465
- param_matrix: pd.DataFrame,
462
+ fit_results: Dict[str, Any],
466
463
  spot_price: float = 1.0) -> go.Figure:
467
464
  """
468
465
  Plot risk-neutral densities for all expiries.
@@ -477,7 +474,7 @@ def plot_rnd_all_expiries(moneyness_array: np.ndarray,
477
474
  - Plotly figure
478
475
  """
479
476
  # Get maturity information
480
- dte_values = param_matrix.attrs['dte_values']
477
+ dte_values = fit_results['fit_performance']['DTE']
481
478
 
482
479
  # Create figure
483
480
  fig = go.Figure()
voly/formulas.py CHANGED
@@ -10,12 +10,40 @@ from voly.models import SVIModel
10
10
  from math import exp
11
11
 
12
12
 
13
+ @catch_exception
14
+ def vectorize_inputs(func):
15
+ """
16
+ Decorator to vectorize Black-Scholes functions to handle both scalar and array inputs.
17
+ """
18
+ def wrapper(s, k, r, vol, t, option_type='call'):
19
+ # Check if inputs are scalar
20
+ k_scalar = np.isscalar(k)
21
+ vol_scalar = np.isscalar(vol)
22
+
23
+ # If both inputs are scalar, use the original function directly
24
+ if k_scalar and vol_scalar:
25
+ return func(s, k, r, vol, t, option_type)
26
+
27
+ # Use NumPy's vectorize to handle array inputs
28
+ vectorized_func = np.vectorize(lambda k_val, vol_val:
29
+ func(s, k_val, r, vol_val, t, option_type))
30
+
31
+ # Call the vectorized function with the inputs
32
+ return vectorized_func(k, vol)
33
+
34
+ return wrapper
35
+
36
+
37
+ @catch_exception
38
+ @vectorize_inputs
13
39
  def d1(s: float, k: float, r: float, vol: float, t: float) -> float:
14
40
  if vol <= 0 or t <= 0:
15
41
  return np.nan
16
42
  return (np.log(s / k) + (r + vol * vol / 2) * t) / (vol * np.sqrt(t))
17
43
 
18
44
 
45
+ @catch_exception
46
+ @vectorize_inputs
19
47
  def d2(s: float, k: float, r: float, vol: float, t: float) -> float:
20
48
  if vol <= 0 or t <= 0:
21
49
  return np.nan
@@ -23,6 +51,7 @@ def d2(s: float, k: float, r: float, vol: float, t: float) -> float:
23
51
 
24
52
 
25
53
  @catch_exception
54
+ @vectorize_inputs
26
55
  def bs(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
27
56
  if vol <= 0 or t <= 0:
28
57
  # Intrinsic value at expiry
@@ -41,6 +70,7 @@ def bs(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'c
41
70
 
42
71
 
43
72
  @catch_exception
73
+ @vectorize_inputs
44
74
  def delta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
45
75
  if vol <= 0 or t <= 0:
46
76
  # At expiry, delta is either 0 or 1 for call, 0 or -1 for put
@@ -58,6 +88,7 @@ def delta(s: float, k: float, r: float, vol: float, t: float, option_type: str =
58
88
 
59
89
 
60
90
  @catch_exception
91
+ @vectorize_inputs
61
92
  def gamma(s: float, k: float, r: float, vol: float, t: float) -> float:
62
93
  if vol <= 0 or t <= 0:
63
94
  return 0.0
@@ -67,6 +98,7 @@ def gamma(s: float, k: float, r: float, vol: float, t: float) -> float:
67
98
 
68
99
 
69
100
  @catch_exception
101
+ @vectorize_inputs
70
102
  def vega(s: float, k: float, r: float, vol: float, t: float) -> float:
71
103
  if vol <= 0 or t <= 0:
72
104
  return 0.0
@@ -76,6 +108,7 @@ def vega(s: float, k: float, r: float, vol: float, t: float) -> float:
76
108
 
77
109
 
78
110
  @catch_exception
111
+ @vectorize_inputs
79
112
  def theta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
80
113
  if vol <= 0 or t <= 0:
81
114
  return 0.0
@@ -97,6 +130,7 @@ def theta(s: float, k: float, r: float, vol: float, t: float, option_type: str =
97
130
 
98
131
 
99
132
  @catch_exception
133
+ @vectorize_inputs
100
134
  def rho(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
101
135
  if vol <= 0 or t <= 0:
102
136
  return 0.0
@@ -110,6 +144,7 @@ def rho(s: float, k: float, r: float, vol: float, t: float, option_type: str = '
110
144
 
111
145
 
112
146
  @catch_exception
147
+ @vectorize_inputs
113
148
  def vanna(s: float, k: float, r: float, vol: float, t: float) -> float:
114
149
  if vol <= 0 or t <= 0:
115
150
  return 0.0
@@ -121,6 +156,7 @@ def vanna(s: float, k: float, r: float, vol: float, t: float) -> float:
121
156
 
122
157
 
123
158
  @catch_exception
159
+ @vectorize_inputs
124
160
  def volga(s: float, k: float, r: float, vol: float, t: float) -> float:
125
161
  if vol <= 0 or t <= 0:
126
162
  return 0.0
@@ -132,6 +168,7 @@ def volga(s: float, k: float, r: float, vol: float, t: float) -> float:
132
168
 
133
169
 
134
170
  @catch_exception
171
+ @vectorize_inputs
135
172
  def charm(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
136
173
  if vol <= 0 or t <= 0:
137
174
  return 0.0
@@ -153,6 +190,7 @@ def charm(s: float, k: float, r: float, vol: float, t: float, option_type: str =
153
190
 
154
191
 
155
192
  @catch_exception
193
+ @vectorize_inputs
156
194
  def greeks(s: float, k: float, r: float, vol: float, t: float,
157
195
  option_type: str = 'call') -> Dict[str, float]:
158
196
  return {
@@ -169,6 +207,7 @@ def greeks(s: float, k: float, r: float, vol: float, t: float,
169
207
 
170
208
 
171
209
  @catch_exception
210
+ @vectorize_inputs
172
211
  def iv(option_price: float, s: float, k: float, r: float, t: float,
173
212
  option_type: str = 'call', precision: float = 1e-8,
174
213
  max_iterations: int = 100) -> float:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.41
3
+ Version: 0.0.43
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -1,18 +1,18 @@
1
1
  voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
2
  voly/client.py,sha256=8nBT2cbjSCYn9Qf1xEFMifmlggkg7BI9ANFVNhHYKUI,19171
3
3
  voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
- voly/formulas.py,sha256=7NQZtYWFT_VOyAVNQHLB5-8caGAdtzU8y8eFcYJsZn8,7256
4
+ voly/formulas.py,sha256=FvxDNtian6bUvQdFbHEa1pGuhmsqyyW8wJ_4dwVQjKg,8320
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=-pynPs7s7WPZUqQhBrxFvVnetEzeCNL2CVJmkSuhx9g,26597
7
+ voly/core/charts.py,sha256=T8cogkiHj8NcFxfARumNvAjLJUAxsMlHUOVgshwjye8,26474
8
8
  voly/core/data.py,sha256=Dfk-ByHpdteUiLXr0p-wRLr3VAmdyjdDBKwjwdTgCjA,9939
9
9
  voly/core/fit.py,sha256=O4PMihVWI1NIEFU4_RkKvT73p0Jk0tB-ot5moMXAW78,9950
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.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,,
14
+ voly-0.0.43.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
15
+ voly-0.0.43.dist-info/METADATA,sha256=4-qY6hJJ4KWuDmGLkqTAbWVihwrWfoUF6Wm7KU8rh3U,4092
16
+ voly-0.0.43.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
17
+ voly-0.0.43.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
18
+ voly-0.0.43.dist-info/RECORD,,
File without changes
File without changes