voly 0.0.161__tar.gz → 0.0.163__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.
- {voly-0.0.161/src/voly.egg-info → voly-0.0.163}/PKG-INFO +1 -1
- {voly-0.0.161 → voly-0.0.163}/pyproject.toml +2 -2
- {voly-0.0.161 → voly-0.0.163}/src/voly/formulas.py +28 -35
- {voly-0.0.161 → voly-0.0.163/src/voly.egg-info}/PKG-INFO +1 -1
- {voly-0.0.161 → voly-0.0.163}/LICENSE +0 -0
- {voly-0.0.161 → voly-0.0.163}/README.md +0 -0
- {voly-0.0.161 → voly-0.0.163}/setup.cfg +0 -0
- {voly-0.0.161 → voly-0.0.163}/setup.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/__init__.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/client.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/__init__.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/charts.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/data.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/fit.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/hd.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/interpolate.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/core/rnd.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/exceptions.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/models.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/utils/__init__.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/utils/density.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly/utils/logger.py +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly.egg-info/SOURCES.txt +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly.egg-info/dependency_links.txt +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly.egg-info/requires.txt +0 -0
- {voly-0.0.161 → voly-0.0.163}/src/voly.egg-info/top_level.txt +0 -0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "voly"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.163"
|
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.
|
63
|
+
python_version = "0.0.163"
|
64
64
|
warn_return_any = true
|
65
65
|
warn_unused_configs = true
|
66
66
|
disallow_untyped_defs = true
|
@@ -15,22 +15,43 @@ from typing import Tuple
|
|
15
15
|
def vectorize_inputs(func):
|
16
16
|
"""
|
17
17
|
Decorator to vectorize Black-Scholes functions to handle both scalar and array inputs.
|
18
|
+
For invalid inputs (K <= 0, o <= 0, or t <= 0), returns np.nan instead of trying to compute.
|
18
19
|
"""
|
20
|
+
|
19
21
|
def wrapper(s, K, r, o, t, option_type='call'):
|
20
22
|
# Check if inputs are scalar
|
21
23
|
K_scalar = np.isscalar(K)
|
22
24
|
o_scalar = np.isscalar(o)
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
t_scalar = np.isscalar(t)
|
26
|
+
|
27
|
+
# Convert pandas Series to numpy arrays if needed
|
28
|
+
if isinstance(K, pd.Series):
|
29
|
+
K = K.values
|
30
|
+
if isinstance(o, pd.Series):
|
31
|
+
o = o.values
|
32
|
+
if isinstance(t, pd.Series):
|
33
|
+
t = t.values
|
34
|
+
|
35
|
+
# If all inputs are scalar, use the original function directly
|
36
|
+
if K_scalar and o_scalar and t_scalar:
|
26
37
|
return func(s, K, r, o, t, option_type)
|
27
38
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
39
|
+
# For arrays, we need to apply the function element-wise
|
40
|
+
# Instead of using np.vectorize directly, we'll create a custom vectorized function
|
41
|
+
# that handles the conditions properly
|
42
|
+
def safe_vectorized_func(K_val, o_val, t_val):
|
43
|
+
# Check for invalid strike price, volatility, or time values
|
44
|
+
if K_val <= 0 or o_val <= 0 or t_val <= 0:
|
45
|
+
return np.nan # Return NaN for invalid inputs
|
46
|
+
else:
|
47
|
+
# Only call the function if inputs are valid
|
48
|
+
return func(s, K_val, r, o_val, t_val, option_type)
|
49
|
+
|
50
|
+
# Now use numpy's vectorize on our safe function
|
51
|
+
vectorized_func = np.vectorize(safe_vectorized_func)
|
31
52
|
|
32
53
|
# Call the vectorized function with the inputs
|
33
|
-
return vectorized_func(K, o)
|
54
|
+
return vectorized_func(K, o, t)
|
34
55
|
|
35
56
|
return wrapper
|
36
57
|
|
@@ -39,8 +60,6 @@ def vectorize_inputs(func):
|
|
39
60
|
@vectorize_inputs
|
40
61
|
def d1(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
41
62
|
# option_type is ignored in this function but included for compatibility
|
42
|
-
if o <= 0 or t <= 0:
|
43
|
-
return np.nan
|
44
63
|
return (np.log(s / K) + (r + o ** 2 / 2) * t) / (o * np.sqrt(t))
|
45
64
|
|
46
65
|
|
@@ -48,8 +67,6 @@ def d1(s: float, K: float, r: float, o: float, t: float, option_type: str = 'cal
|
|
48
67
|
@vectorize_inputs
|
49
68
|
def d2(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
50
69
|
# option_type is ignored in this function but included for compatibility
|
51
|
-
if o <= 0 or t <= 0:
|
52
|
-
return np.nan
|
53
70
|
return d1(s, K, r, o, t, option_type) - o * np.sqrt(t)
|
54
71
|
|
55
72
|
|
@@ -93,9 +110,6 @@ def delta(s: float, K: float, r: float, o: float, t: float, option_type: str = '
|
|
93
110
|
@catch_exception
|
94
111
|
@vectorize_inputs
|
95
112
|
def gamma(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
96
|
-
if o <= 0 or t <= 0:
|
97
|
-
return 0.0
|
98
|
-
|
99
113
|
d1_val = d1(s, K, r, o, t, option_type)
|
100
114
|
return norm.pdf(d1_val) / (s * o * np.sqrt(t))
|
101
115
|
|
@@ -103,9 +117,6 @@ def gamma(s: float, K: float, r: float, o: float, t: float, option_type: str = '
|
|
103
117
|
@catch_exception
|
104
118
|
@vectorize_inputs
|
105
119
|
def vega(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
106
|
-
if o <= 0 or t <= 0:
|
107
|
-
return 0.0
|
108
|
-
|
109
120
|
d1_val = d1(s, K, r, o, t, option_type)
|
110
121
|
return s * norm.pdf(d1_val) * np.sqrt(t) / 100 # Divided by 100 for 1% change
|
111
122
|
|
@@ -113,9 +124,6 @@ def vega(s: float, K: float, r: float, o: float, t: float, option_type: str = 'c
|
|
113
124
|
@catch_exception
|
114
125
|
@vectorize_inputs
|
115
126
|
def theta(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
116
|
-
if o <= 0 or t <= 0:
|
117
|
-
return 0.0
|
118
|
-
|
119
127
|
d1_val = d1(s, K, r, o, t, option_type)
|
120
128
|
d2_val = d2(s, K, r, o, t, option_type)
|
121
129
|
|
@@ -135,9 +143,6 @@ def theta(s: float, K: float, r: float, o: float, t: float, option_type: str = '
|
|
135
143
|
@catch_exception
|
136
144
|
@vectorize_inputs
|
137
145
|
def rho(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
138
|
-
if o <= 0 or t <= 0:
|
139
|
-
return 0.0
|
140
|
-
|
141
146
|
d2_val = d2(s, K, r, o, t, option_type)
|
142
147
|
|
143
148
|
if option_type.lower() in ["call", "c"]:
|
@@ -149,9 +154,6 @@ def rho(s: float, K: float, r: float, o: float, t: float, option_type: str = 'ca
|
|
149
154
|
@catch_exception
|
150
155
|
@vectorize_inputs
|
151
156
|
def vanna(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
152
|
-
if o <= 0 or t <= 0:
|
153
|
-
return 0.0
|
154
|
-
|
155
157
|
d1_val = d1(s, K, r, o, t, option_type)
|
156
158
|
d2_val = d2(s, K, r, o, t, option_type)
|
157
159
|
|
@@ -161,9 +163,6 @@ def vanna(s: float, K: float, r: float, o: float, t: float, option_type: str = '
|
|
161
163
|
@catch_exception
|
162
164
|
@vectorize_inputs
|
163
165
|
def volga(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
164
|
-
if o <= 0 or t <= 0:
|
165
|
-
return 0.0
|
166
|
-
|
167
166
|
d1_val = d1(s, K, r, o, t, option_type)
|
168
167
|
d2_val = d2(s, K, r, o, t, option_type)
|
169
168
|
|
@@ -173,9 +172,6 @@ def volga(s: float, K: float, r: float, o: float, t: float, option_type: str = '
|
|
173
172
|
@catch_exception
|
174
173
|
@vectorize_inputs
|
175
174
|
def charm(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
|
176
|
-
if o <= 0 or t <= 0:
|
177
|
-
return 0.0
|
178
|
-
|
179
175
|
d1_val = d1(s, K, r, o, t, option_type)
|
180
176
|
d2_val = d2(s, K, r, o, t, option_type)
|
181
177
|
|
@@ -227,9 +223,6 @@ def iv(option_price: float, s: float, K: float, r: float, t: float,
|
|
227
223
|
Returns:
|
228
224
|
- Implied volatility
|
229
225
|
"""
|
230
|
-
if t <= 0:
|
231
|
-
return np.nan
|
232
|
-
|
233
226
|
# Check if option price is within theoretical bounds
|
234
227
|
if option_type.lower() in ["call", "c"]:
|
235
228
|
intrinsic = max(0, s - K * np.exp(-r * t))
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|