voly 0.0.94__py3-none-any.whl → 0.0.96__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/formulas.py CHANGED
@@ -15,72 +15,72 @@ def vectorize_inputs(func):
15
15
  """
16
16
  Decorator to vectorize Black-Scholes functions to handle both scalar and array inputs.
17
17
  """
18
- def wrapper(s, k, r, vol, t, option_type='call'):
18
+ def wrapper(s, K, r, o, t, option_type='call'):
19
19
  # Check if inputs are scalar
20
- k_scalar = np.isscalar(k)
21
- vol_scalar = np.isscalar(vol)
20
+ K_scalar = np.isscalar(K)
21
+ o_scalar = np.isscalar(o)
22
22
 
23
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)
24
+ if K_scalar and o_scalar:
25
+ return func(s, K, r, o, t, option_type)
26
26
 
27
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))
28
+ vectorized_func = np.vectorize(lambda K_val, o_val:
29
+ func(s, K_val, r, o_val, t, option_type))
30
30
 
31
31
  # Call the vectorized function with the inputs
32
- return vectorized_func(k, vol)
32
+ return vectorized_func(K, o)
33
33
 
34
34
  return wrapper
35
35
 
36
36
 
37
37
  @catch_exception
38
38
  @vectorize_inputs
39
- def d1(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
39
+ def d1(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
40
40
  # option_type is ignored in this function but included for compatibility
41
- if vol <= 0 or t <= 0:
41
+ if o <= 0 or t <= 0:
42
42
  return np.nan
43
- return (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
43
+ return (np.log(s / K) + (r + o ** 2 / 2) * t) / (o * np.sqrt(t))
44
44
 
45
45
  @catch_exception
46
46
  @vectorize_inputs
47
- def d2(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
47
+ def d2(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
48
48
  # option_type is ignored in this function but included for compatibility
49
- if vol <= 0 or t <= 0:
49
+ if o <= 0 or t <= 0:
50
50
  return np.nan
51
- return d1(s, k, r, vol, t, option_type) - vol * np.sqrt(t)
51
+ return d1(s, K, r, o, t, option_type) - o * np.sqrt(t)
52
52
 
53
53
 
54
54
  @catch_exception
55
55
  @vectorize_inputs
56
- def bs(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
57
- if vol <= 0 or t <= 0:
56
+ def bs(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
57
+ if o <= 0 or t <= 0:
58
58
  # Intrinsic value at expiry
59
59
  if option_type.lower() in ["call", "c"]:
60
- return max(0, s - k)
60
+ return max(0, s - K)
61
61
  else:
62
- return max(0, k - s)
62
+ return max(0, K - s)
63
63
 
64
- d1_val = d1(s, k, r, vol, t)
65
- d2_val = d2(s, k, r, vol, t)
64
+ d1_val = d1(s, K, r, o, t)
65
+ d2_val = d2(s, K, r, o, t)
66
66
 
67
67
  if option_type.lower() in ["call", "c"]:
68
- return s * norm.cdf(d1_val) - k * np.exp(-r * t) * norm.cdf(d2_val)
68
+ return s * norm.cdf(d1_val) - K * np.exp(-r * t) * norm.cdf(d2_val)
69
69
  else: # put
70
- return k * np.exp(-r * t) * norm.cdf(-d2_val) - s * norm.cdf(-d1_val)
70
+ return K * np.exp(-r * t) * norm.cdf(-d2_val) - s * norm.cdf(-d1_val)
71
71
 
72
72
 
73
73
  @catch_exception
74
74
  @vectorize_inputs
75
- def delta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
76
- if vol <= 0 or t <= 0:
75
+ def delta(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
76
+ if o <= 0 or t <= 0:
77
77
  # At expiry, delta is either 0 or 1 for call, 0 or -1 for put
78
78
  if option_type.lower() in ["call", "c"]:
79
- return 1.0 if s > k else 0.0
79
+ return 1.0 if s > K else 0.0
80
80
  else:
81
- return -1.0 if s < k else 0.0
81
+ return -1.0 if s < K else 0.0
82
82
 
83
- d1_val = d1(s, k, r, vol, t)
83
+ d1_val = d1(s, K, r, o, t)
84
84
 
85
85
  if option_type.lower() in ["call", "c"]:
86
86
  return norm.cdf(d1_val)
@@ -90,41 +90,41 @@ def delta(s: float, k: float, r: float, vol: float, t: float, option_type: str =
90
90
 
91
91
  @catch_exception
92
92
  @vectorize_inputs
93
- def gamma(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
94
- if vol <= 0 or t <= 0:
93
+ def gamma(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
94
+ if o <= 0 or t <= 0:
95
95
  return 0.0
96
96
 
97
- d1_val = d1(s, k, r, vol, t, option_type)
98
- return norm.pdf(d1_val) / (s * vol * np.sqrt(t))
97
+ d1_val = d1(s, K, r, o, t, option_type)
98
+ return norm.pdf(d1_val) / (s * o * np.sqrt(t))
99
99
 
100
100
 
101
101
  @catch_exception
102
102
  @vectorize_inputs
103
- def vega(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
104
- if vol <= 0 or t <= 0:
103
+ def vega(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
104
+ if o <= 0 or t <= 0:
105
105
  return 0.0
106
106
 
107
- d1_val = d1(s, k, r, vol, t, option_type)
107
+ d1_val = d1(s, K, r, o, t, option_type)
108
108
  return s * norm.pdf(d1_val) * np.sqrt(t) / 100 # Divided by 100 for 1% change
109
109
 
110
110
 
111
111
  @catch_exception
112
112
  @vectorize_inputs
113
- def theta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
114
- if vol <= 0 or t <= 0:
113
+ def theta(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
114
+ if o <= 0 or t <= 0:
115
115
  return 0.0
116
116
 
117
- d1_val = d1(s, k, r, vol, t, option_type)
118
- d2_val = d2(s, k, r, vol, t, option_type)
117
+ d1_val = d1(s, K, r, o, t, option_type)
118
+ d2_val = d2(s, K, r, o, t, option_type)
119
119
 
120
120
  # First part of theta (same for both call and put)
121
- theta_part1 = -s * norm.pdf(d1_val) * vol / (2 * np.sqrt(t))
121
+ theta_part1 = -s * norm.pdf(d1_val) * o / (2 * np.sqrt(t))
122
122
 
123
123
  # Second part depends on option type
124
124
  if option_type.lower() in ["call", "c"]:
125
- theta_part2 = -r * k * np.exp(-r * t) * norm.cdf(d2_val)
125
+ theta_part2 = -r * K * np.exp(-r * t) * norm.cdf(d2_val)
126
126
  else: # put
127
- theta_part2 = r * k * np.exp(-r * t) * norm.cdf(-d2_val)
127
+ theta_part2 = r * K * np.exp(-r * t) * norm.cdf(-d2_val)
128
128
 
129
129
  # Return theta per day (t is in years)
130
130
  return (theta_part1 + theta_part2) / 365.0
@@ -132,50 +132,50 @@ def theta(s: float, k: float, r: float, vol: float, t: float, option_type: str =
132
132
 
133
133
  @catch_exception
134
134
  @vectorize_inputs
135
- def rho(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
136
- if vol <= 0 or t <= 0:
135
+ def rho(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
136
+ if o <= 0 or t <= 0:
137
137
  return 0.0
138
138
 
139
- d2_val = d2(s, k, r, vol, t, option_type)
139
+ d2_val = d2(s, K, r, o, t, option_type)
140
140
 
141
141
  if option_type.lower() in ["call", "c"]:
142
- return k * t * np.exp(-r * t) * norm.cdf(d2_val) / 100
142
+ return K * t * np.exp(-r * t) * norm.cdf(d2_val) / 100
143
143
  else: # put
144
- return -k * t * np.exp(-r * t) * norm.cdf(-d2_val) / 100
144
+ return -K * t * np.exp(-r * t) * norm.cdf(-d2_val) / 100
145
145
 
146
146
 
147
147
  @catch_exception
148
148
  @vectorize_inputs
149
- def vanna(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
150
- if vol <= 0 or t <= 0:
149
+ def vanna(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
150
+ if o <= 0 or t <= 0:
151
151
  return 0.0
152
152
 
153
- d1_val = d1(s, k, r, vol, t, option_type)
154
- d2_val = d2(s, k, r, vol, t, option_type)
153
+ d1_val = d1(s, K, r, o, t, option_type)
154
+ d2_val = d2(s, K, r, o, t, option_type)
155
155
 
156
- return -norm.pdf(d1_val) * d2_val / vol
156
+ return -norm.pdf(d1_val) * d2_val / o
157
157
 
158
158
 
159
159
  @catch_exception
160
160
  @vectorize_inputs
161
- def volga(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
162
- if vol <= 0 or t <= 0:
161
+ def volga(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
162
+ if o <= 0 or t <= 0:
163
163
  return 0.0
164
164
 
165
- d1_val = d1(s, k, r, vol, t, option_type)
166
- d2_val = d2(s, k, r, vol, t, option_type)
165
+ d1_val = d1(s, K, r, o, t, option_type)
166
+ d2_val = d2(s, K, r, o, t, option_type)
167
167
 
168
- return s * norm.pdf(d1_val) * np.sqrt(t) * d1_val * d2_val / vol
168
+ return s * norm.pdf(d1_val) * np.sqrt(t) * d1_val * d2_val / o
169
169
 
170
170
 
171
171
  @catch_exception
172
172
  @vectorize_inputs
173
- def charm(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
174
- if vol <= 0 or t <= 0:
173
+ def charm(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
174
+ if o <= 0 or t <= 0:
175
175
  return 0.0
176
176
 
177
- d1_val = d1(s, k, r, vol, t, option_type)
178
- d2_val = d2(s, k, r, vol, t, option_type)
177
+ d1_val = d1(s, K, r, o, t, option_type)
178
+ d2_val = d2(s, K, r, o, t, option_type)
179
179
 
180
180
  # First term is the same for calls and puts
181
181
  term1 = -norm.pdf(d1_val) * d1_val / (2 * t)
@@ -192,34 +192,34 @@ def charm(s: float, k: float, r: float, vol: float, t: float, option_type: str =
192
192
 
193
193
  @catch_exception
194
194
  @vectorize_inputs
195
- def greeks(s: float, k: float, r: float, vol: float, t: float,
195
+ def greeks(s: float, K: float, r: float, o: float, t: float,
196
196
  option_type: str = 'call') -> Dict[str, float]:
197
197
  return {
198
- 'price': bs(s, k, r, vol, t, option_type),
199
- 'delta': delta(s, k, r, vol, t, option_type),
200
- 'gamma': gamma(s, k, r, vol, t, option_type),
201
- 'vega': vega(s, k, r, vol, t, option_type),
202
- 'theta': theta(s, k, r, vol, t, option_type),
203
- 'rho': rho(s, k, r, vol, t, option_type),
204
- 'vanna': vanna(s, k, r, vol, t, option_type),
205
- 'volga': volga(s, k, r, vol, t, option_type),
206
- 'charm': charm(s, k, r, vol, t, option_type)
198
+ 'price': bs(s, K, r, o, t, option_type),
199
+ 'delta': delta(s, K, r, o, t, option_type),
200
+ 'gamma': gamma(s, K, r, o, t, option_type),
201
+ 'vega': vega(s, K, r, o, t, option_type),
202
+ 'theta': theta(s, K, r, o, t, option_type),
203
+ 'rho': rho(s, K, r, o, t, option_type),
204
+ 'vanna': vanna(s, K, r, o, t, option_type),
205
+ 'volga': volga(s, K, r, o, t, option_type),
206
+ 'charm': charm(s, K, r, o, t, option_type)
207
207
  }
208
208
 
209
209
 
210
210
  @catch_exception
211
211
  @vectorize_inputs
212
- def iv(option_price: float, s: float, k: float, r: float, t: float,
212
+ def iv(option_price: float, s: float, K: float, r: float, t: float,
213
213
  option_type: str = 'call', precision: float = 1e-8,
214
214
  max_iterations: int = 100) -> float:
215
215
  """
216
216
  Calculate implied volatility using Newton-Raphson method.
217
217
 
218
218
  Parameters:
219
- - option_price: Market price of the option
219
+ - option_price: MarKet price of the option
220
220
  - s: Underlying price
221
- - k: Strike price
222
- - r: Risk-free rate
221
+ - K: Strike price
222
+ - r: RisK-free rate
223
223
  - t: Time to expiry in years
224
224
  - option_type: 'call' or 'put'
225
225
  - precision: Desired precision
@@ -233,46 +233,46 @@ def iv(option_price: float, s: float, k: float, r: float, t: float,
233
233
 
234
234
  # Check if option price is within theoretical bounds
235
235
  if option_type.lower() in ["call", "c"]:
236
- intrinsic = max(0, s - k * np.exp(-r * t))
236
+ intrinsic = max(0, s - K * np.exp(-r * t))
237
237
  if option_price < intrinsic:
238
238
  return np.nan # Price below intrinsic value
239
239
  if option_price >= s:
240
240
  return np.inf # Price exceeds underlying
241
241
  else: # put
242
- intrinsic = max(0, k * np.exp(-r * t) - s)
242
+ intrinsic = max(0, K * np.exp(-r * t) - s)
243
243
  if option_price < intrinsic:
244
244
  return np.nan # Price below intrinsic value
245
- if option_price >= k:
245
+ if option_price >= K:
246
246
  return np.inf # Price exceeds strike
247
247
 
248
248
  # Initial guess - Manaster and Koehler (1982) method
249
- vol = np.sqrt(2 * np.pi / t) * option_price / s
249
+ o = np.sqrt(2 * np.pi / t) * option_price / s
250
250
 
251
251
  # Ensure initial guess is reasonable
252
- vol = max(0.001, min(vol, 5.0))
252
+ o = max(0.001, min(o, 5.0))
253
253
 
254
254
  for _ in range(max_iterations):
255
255
  # Calculate option price and vega with current volatility
256
- price = bs(s, k, r, vol, t, option_type)
257
- v = vega(s, k, r, vol, t, option_type)
256
+ price = bs(s, K, r, o, t, option_type)
257
+ v = vega(s, K, r, o, t, option_type)
258
258
 
259
259
  # Calculate price difference
260
260
  price_diff = price - option_price
261
261
 
262
262
  # Check if precision reached
263
263
  if abs(price_diff) < precision:
264
- return vol
264
+ return o
265
265
 
266
266
  # Avoid division by zero
267
267
  if abs(v) < 1e-10:
268
268
  # Change direction based on whether price is too high or too low
269
- vol = vol * 1.5 if price_diff < 0 else vol * 0.5
269
+ o = o * 1.5 if price_diff < 0 else o * 0.5
270
270
  else:
271
271
  # Newton-Raphson update
272
- vol = vol - price_diff / (v * 100) # Vega is for 1% change
272
+ o = o - price_diff / (v * 100) # Vega is for 1% change
273
273
 
274
274
  # Ensure volatility stays in reasonable bounds
275
- vol = max(0.001, min(vol, 5.0))
275
+ o = max(0.001, min(o, 5.0))
276
276
 
277
277
  # If we reach here, we didn't converge
278
278
  return np.nan
@@ -286,7 +286,7 @@ def get_domain(domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
286
286
  t: float = None,
287
287
  return_domain: str = 'log_moneyness') -> np.ndarray:
288
288
  """
289
- Compute the x-domain for a given return type (log-moneyness, moneyness, strikes, or delta).
289
+ Compute the x-domain for a given return type (log-moneyness, moneyness, returns, strikes, or delta).
290
290
 
291
291
  Parameters:
292
292
  -----------
@@ -336,7 +336,7 @@ def get_domain(domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
336
336
 
337
337
  elif return_domain == 'delta':
338
338
  # Check for required parameters
339
- required_params = {'s': s, 'o': o, 'r': r, 't': t}
339
+ required_params = {'s': s, 'r': r, 'o': o, 't': t}
340
340
  missing_params = [param for param, value in required_params.items() if value is None]
341
341
  if missing_params:
342
342
  raise ValueError(f"The following parameters are required for return_domain='delta': {missing_params}")
@@ -350,9 +350,9 @@ def get_domain(domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
350
350
  K = s / np.exp(LM)
351
351
 
352
352
  # Compute deltas
353
- D = np.array([delta(s, K, r, o, t, 'call') for K, o in zip(K, o)])
353
+ D = delta(s, K, r, o, t, 'call')
354
354
  return D
355
355
 
356
356
  else:
357
357
  raise ValueError(
358
- f"Invalid return_domain: {return_domain}. Must be one of ['log_moneyness', 'moneyness', 'returns', 'strikes', 'delta'].")
358
+ f"Invalid return_domain: {return_domain}. Must be one of ['log_moneyness', 'moneyness', 'returns', 'striKes', 'delta'].")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.94
3
+ Version: 0.0.96
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
  voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
2
  voly/client.py,sha256=M_XmGQAheVSPOBJPXVYJzm1s8ZOcBZ6keeZhb9Yu0R4,15367
3
3
  voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
- voly/formulas.py,sha256=DgWw_RYYUcOsiUHrW2h4m0_tgPpGXFYowoFJOwrETIQ,11750
4
+ voly/formulas.py,sha256=6yuu0-vO6TkV7p94W8d_akuC_byySs02-yhy62W8eA4,11575
5
5
  voly/models.py,sha256=Vs69QZYQ_Tt6hkaZ6Mfnq3II1dHDqQrZTzBNiaOIbwo,3516
6
6
  voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
7
7
  voly/core/charts.py,sha256=E21OZB5lTY4YL2flgaFJ6s5g3_ExtAQT2zryZZxLPyM,12735
@@ -11,8 +11,8 @@ voly/core/interpolate.py,sha256=JkK172-FXyhesW3hY4pEeuJWG3Bugq7QZXbeKoRpLuo,5305
11
11
  voly/core/rnd.py,sha256=yNkQuiiw--exiJLHa9Wd9zy5sZN5_QJ3itKmr04zg-Q,10047
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.94.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
15
- voly-0.0.94.dist-info/METADATA,sha256=yzEyWT7dvnY8qViyVbgNhuhHSL_Z4P_fN0TYFZZPzcc,4092
16
- voly-0.0.94.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
17
- voly-0.0.94.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
18
- voly-0.0.94.dist-info/RECORD,,
14
+ voly-0.0.96.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
15
+ voly-0.0.96.dist-info/METADATA,sha256=235T9rVu_SsCzwKU9dK1ZEwdBI_4-ztB5akF-A-tS7E,4092
16
+ voly-0.0.96.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
17
+ voly-0.0.96.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
18
+ voly-0.0.96.dist-info/RECORD,,
File without changes
File without changes