voly 0.0.127__py3-none-any.whl → 0.0.128__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 CHANGED
@@ -368,40 +368,3 @@ class VolyClient:
368
368
  'x_surface': x_surface,
369
369
  'moments': moments
370
370
  }
371
-
372
- @staticmethod
373
- def get_rv_surface(model_results: pd.DataFrame,
374
- pdf_surface: Dict[str, np.ndarray],
375
- x_surface: Dict[str, np.ndarray],
376
- domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
377
- return_domain: str = 'log_moneyness') -> Dict[str, Any]:
378
- """
379
- Transform historical density surface into a volatility surface.
380
-
381
- Parameters:
382
- - model_results: DataFrame from fit_model() with maturity information
383
- - pdf_surface: Dictionary mapping maturity names to historical density arrays
384
- - x_surface: Dictionary mapping maturity names to x domain arrays
385
- - domain_params: Tuple of (min, max, num_points) for the log-moneyness grid
386
- - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'returns', 'strikes', 'delta')
387
-
388
- Returns:
389
- - Dictionary with implied volatility surface information
390
- """
391
-
392
- logger.info("Calculating realized volatility surface")
393
-
394
- # Generate the surface
395
- fit_results, rv_surface, x_surface = get_rv_surface(
396
- model_results=model_results,
397
- pdf_surface=pdf_surface,
398
- x_surface=x_surface,
399
- domain_params=domain_params,
400
- return_domain=return_domain
401
- )
402
-
403
- return {
404
- 'fit_results': fit_results,
405
- 'rv_surface': rv_surface,
406
- 'x_surface': x_surface
407
- }
voly/core/hd.py CHANGED
@@ -205,169 +205,3 @@ def get_hd_surface(model_results: pd.DataFrame,
205
205
  moments = pd.DataFrame(all_moments).T
206
206
 
207
207
  return pdf_surface, cdf_surface, x_surface, moments
208
-
209
-
210
- @catch_exception
211
- def get_rv_surface(model_results: pd.DataFrame,
212
- pdf_surface: Dict[str, np.ndarray],
213
- x_surface: Dict[str, np.ndarray],
214
- domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
215
- return_domain: str = 'log_moneyness') -> Tuple[
216
- Dict[str, np.ndarray], Dict[str, np.ndarray], pd.DataFrame]:
217
-
218
- # Check if required columns are present
219
- required_columns = ['s', 't', 'r']
220
- missing_columns = [col for col in required_columns if col not in model_results.columns]
221
- if missing_columns:
222
- raise VolyError(f"Required columns missing in model_results: {missing_columns}")
223
-
224
- rv_surface = {}
225
- new_x_surface = {}
226
- all_params = {}
227
-
228
- # Check if pdf_surface is empty
229
- if not pdf_surface:
230
- logger.warning("Historical density surface is empty.")
231
- return {}, {}, pd.DataFrame()
232
-
233
- # Process each maturity
234
- for i in model_results.index:
235
- if i not in pdf_surface:
236
- logger.warning(f"No historical density available for maturity {i}, skipping.")
237
- continue
238
-
239
- # Get parameters for this maturity
240
- s = model_results.loc[i, 's']
241
- r = model_results.loc[i, 'r']
242
- t = model_results.loc[i, 't']
243
-
244
- # Get historical density for this maturity
245
- pdf = pdf_surface[i]
246
- x = x_surface[i]
247
-
248
- # Calculate x_domain grids
249
- LM = get_domain(domain_params, s, r, None, t, 'log_moneyness')
250
- M = get_domain(domain_params, s, r, None, t, 'moneyness')
251
- R = get_domain(domain_params, s, r, None, t, 'returns')
252
- K = get_domain(domain_params, s, r, None, t, 'log_moneyness')
253
-
254
- # Recover call prices from the PDF
255
- c_recovered = np.zeros_like(LM)
256
- for j, lm_k in enumerate(LM):
257
- mask = LM >= lm_k
258
- if np.any(mask):
259
- integrand = s * (np.exp(LM[mask]) - np.exp(lm_k)) * pdf[mask]
260
- c_recovered[j] = np.exp(-r * t) * np.trapz(integrand, LM[mask])
261
-
262
- # Ensure call prices are at least the intrinsic value
263
- intrinsic_values = np.maximum(s - K, 0)
264
- c_recovered = np.maximum(c_recovered, intrinsic_values)
265
-
266
- # Determine min_lm and max_lm based on days to expiry (DTE)
267
- dte = t * 365.25
268
- if dte <= 30:
269
- min_lm, max_lm = -0.3, 0.3
270
- elif dte <= 90:
271
- min_lm, max_lm = -0.6, 0.6
272
- else:
273
- min_lm, max_lm = -0.9, 0.9
274
-
275
- # Generate key log-moneyness points
276
- key_lm_points = generate_lm_points(min_lm, max_lm)
277
-
278
- # Find the indices of the key log-moneyness points
279
- key_indices = [np.argmin(np.abs(LM - lm)) for lm in key_lm_points]
280
- key_lm_actual = LM[key_indices] # Actual log moneyness values
281
- key_strikes = K[key_indices] # Corresponding strikes
282
-
283
- # Extract call prices at key log-moneyness points
284
- key_call_prices = c_recovered[key_indices]
285
-
286
- # Compute IV at key log-moneyness points using our own iv function
287
- key_ivs = []
288
- for j, idx in enumerate(key_indices):
289
- call_price = key_call_prices[j]
290
- strike = key_strikes[j]
291
- if call_price <= 0:
292
- iv_value = 0.01 # Minimum IV of 1%
293
- else:
294
- try:
295
- iv_value = iv(option_price=call_price, s=s, K=strike, r=r, t=t, option_type='call')
296
- iv_value = max(0.01, min(iv_value, 3.0)) # Clamp between 1% and 300%
297
- except Exception as e:
298
- logger.warning(f"IV calculation failed for strike {strike}: {str(e)}")
299
- iv_value = 0.01 # Fallback to 1%
300
- key_ivs.append(iv_value)
301
- key_ivs = np.array(key_ivs)
302
-
303
- # Create a synthetic option chain for SVI fitting
304
- # Convert to DataFrame columns that fit_model expects
305
- synthetic_chain = pd.DataFrame({
306
- 'maturity_name': [i] * len(key_strikes),
307
- 'maturity_date': pd.Timestamp.now() + pd.Timedelta(days=int(t * 365.25)),
308
- 'index_price': s,
309
- 'underlying_price': s,
310
- 'strike': key_strikes,
311
- 'log_moneyness': key_lm_actual,
312
- 'mark_iv': key_ivs,
313
- 't': t,
314
- 'r': r,
315
- 'option_type': 'call'
316
- })
317
-
318
- # Fit the SVI model to the recovered IVs
319
- fit_results_rv = fit_model(option_chain=synthetic_chain, model_name='svi')
320
-
321
- # Get the parameters for this maturity
322
- a = fit_results_rv.loc[i, 'a']
323
- b = fit_results_rv.loc[i, 'b']
324
- sigma = fit_results_rv.loc[i, 'sigma']
325
- rho = fit_results_rv.loc[i, 'rho']
326
- m = fit_results_rv.loc[i, 'm']
327
-
328
- # Store the parameters
329
- params = {
330
- 's': s,
331
- 'r': r,
332
- 't': t,
333
- 'a': a,
334
- 'b': b,
335
- 'sigma': sigma,
336
- 'rho': rho,
337
- 'm': m
338
- }
339
-
340
- nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(params['a'], params['b'], params['sigma'], params['rho'], params['m'], params['t'])
341
- params.update({
342
- 'nu': nu,
343
- 'psi': psi,
344
- 'p': p,
345
- 'c': c,
346
- 'nu_tilde': nu_tilde,
347
- })
348
- all_params[i] = params
349
-
350
- # Calculate implied volatility using SVI model
351
- w = np.array([SVIModel.svi(lm, a, b, sigma, rho, m) for lm in LM])
352
- o_recovered = np.sqrt(w / t)
353
-
354
- # Store results
355
- rv_surface[i] = o_recovered
356
-
357
- if return_domain == 'log_moneyness':
358
- x = LM
359
- elif return_domain == 'moneyness':
360
- x = M
361
- elif return_domain == 'returns':
362
- x = R
363
- elif return_domain == 'strikes':
364
- x = K
365
- elif return_domain == 'delta':
366
- x = get_domain(domain_params, s, r, o_recovered, t, 'delta')
367
-
368
- new_x_surface[i] = x
369
-
370
- # Create a DataFrame with parameters
371
- fit_results = pd.DataFrame(all_params).T
372
- x_surface = new_x_surface
373
- return fit_results, rv_surface, x_surface
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voly
3
- Version: 0.0.127
3
+ Version: 0.0.128
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
- voly/client.py,sha256=R5QSKSJhwYYoJe9LFPL9dynOVwz0q6LI8TU--vnsILs,14887
2
+ voly/client.py,sha256=CJjQzes3K6ZdzPV1JFBoXQ35_EDz05MyP26YQKJe9dc,13402
3
3
  voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
4
  voly/formulas.py,sha256=HejPfgVh6-hmWDhvwbHgAIfQMt8iPbIiaKqcBrB5Spw,10766
5
5
  voly/models.py,sha256=BF-O7BjGf0BLMpw4rCtfwW7s8_f4iyUZdUY6q1dVxLs,3363
@@ -7,13 +7,13 @@ voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
7
7
  voly/core/charts.py,sha256=E21OZB5lTY4YL2flgaFJ6s5g3_ExtAQT2zryZZxLPyM,12735
8
8
  voly/core/data.py,sha256=pDeuYhP0GX4RbtlqByvsE3rfHcIkix0BU5MLW8sKIeI,8935
9
9
  voly/core/fit.py,sha256=Tb9eeG7e_2dQTcqt6aqEwFrZdy6jR9rSNqe6tzOdVhQ,9245
10
- voly/core/hd.py,sha256=nU_sjOn-DVRFNmjUiZfeMf-RJehm-I0HrqfOmupINiw,13459
10
+ voly/core/hd.py,sha256=spxNmqw_L3xym-CcPQ_GJEpIFibsuP9bPGLZj0YhDtw,7423
11
11
  voly/core/interpolate.py,sha256=JkK172-FXyhesW3hY4pEeuJWG3Bugq7QZXbeKoRpLuo,5305
12
12
  voly/core/rnd.py,sha256=0VE77lxesx_BPAO46QwKpcauZNaHnPTiDhmRbSURn3c,10022
13
13
  voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
14
14
  voly/utils/logger.py,sha256=4-_2bVJmq17Q0d7Rd2mPg1AeR8gxv6EPvcmBDMFWcSM,1744
15
- voly-0.0.127.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
16
- voly-0.0.127.dist-info/METADATA,sha256=8s8R7TsvT7fLIhVumViAOTfJ3ofzCJshQBc2QrFOx3k,4115
17
- voly-0.0.127.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
18
- voly-0.0.127.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
19
- voly-0.0.127.dist-info/RECORD,,
15
+ voly-0.0.128.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
16
+ voly-0.0.128.dist-info/METADATA,sha256=izR6T5uT6uB0FAEb5OvLB9q1uva2THo3kYt5q-O_7wQ,4115
17
+ voly-0.0.128.dist-info/WHEEL,sha256=DK49LOLCYiurdXXOXwGJm6U4DkHkg4lcxjhqwRa0CP4,91
18
+ voly-0.0.128.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
19
+ voly-0.0.128.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.0.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5