voly 0.0.154__tar.gz → 0.0.156__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.
Files changed (26) hide show
  1. {voly-0.0.154/src/voly.egg-info → voly-0.0.156}/PKG-INFO +1 -1
  2. {voly-0.0.154 → voly-0.0.156}/pyproject.toml +2 -2
  3. {voly-0.0.154 → voly-0.0.156}/src/voly/client.py +10 -6
  4. {voly-0.0.154 → voly-0.0.156}/src/voly/core/hd.py +16 -2
  5. {voly-0.0.154 → voly-0.0.156}/src/voly/core/rnd.py +16 -2
  6. {voly-0.0.154 → voly-0.0.156}/src/voly/utils/density.py +50 -1
  7. {voly-0.0.154 → voly-0.0.156/src/voly.egg-info}/PKG-INFO +1 -1
  8. {voly-0.0.154 → voly-0.0.156}/LICENSE +0 -0
  9. {voly-0.0.154 → voly-0.0.156}/README.md +0 -0
  10. {voly-0.0.154 → voly-0.0.156}/setup.cfg +0 -0
  11. {voly-0.0.154 → voly-0.0.156}/setup.py +0 -0
  12. {voly-0.0.154 → voly-0.0.156}/src/voly/__init__.py +0 -0
  13. {voly-0.0.154 → voly-0.0.156}/src/voly/core/__init__.py +0 -0
  14. {voly-0.0.154 → voly-0.0.156}/src/voly/core/charts.py +0 -0
  15. {voly-0.0.154 → voly-0.0.156}/src/voly/core/data.py +0 -0
  16. {voly-0.0.154 → voly-0.0.156}/src/voly/core/fit.py +0 -0
  17. {voly-0.0.154 → voly-0.0.156}/src/voly/core/interpolate.py +0 -0
  18. {voly-0.0.154 → voly-0.0.156}/src/voly/exceptions.py +0 -0
  19. {voly-0.0.154 → voly-0.0.156}/src/voly/formulas.py +0 -0
  20. {voly-0.0.154 → voly-0.0.156}/src/voly/models.py +0 -0
  21. {voly-0.0.154 → voly-0.0.156}/src/voly/utils/__init__.py +0 -0
  22. {voly-0.0.154 → voly-0.0.156}/src/voly/utils/logger.py +0 -0
  23. {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/SOURCES.txt +0 -0
  24. {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/dependency_links.txt +0 -0
  25. {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/requires.txt +0 -0
  26. {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voly
3
- Version: 0.0.154
3
+ Version: 0.0.156
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "voly"
7
- version = "0.0.154"
7
+ version = "0.0.156"
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.154"
63
+ python_version = "0.0.156"
64
64
  warn_return_any = true
65
65
  warn_unused_configs = true
66
66
  disallow_untyped_defs = true
@@ -306,7 +306,8 @@ class VolyClient:
306
306
  def get_rnd_surface(model_results: pd.DataFrame,
307
307
  domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
308
308
  return_domain: str = 'log_moneyness',
309
- method: str = 'rookley') -> Dict[str, Any]:
309
+ method: str = 'rookley',
310
+ centered: bool = False) -> Dict[str, Any]:
310
311
  """
311
312
  Generate risk-neutral density surface from volatility surface parameters.
312
313
 
@@ -315,6 +316,7 @@ class VolyClient:
315
316
  - domain_params: Tuple of (min_log_moneyness, max_log_moneyness, num_points)
316
317
  - return_domain: Domain for results ('log_moneyness', 'moneyness', 'returns', 'strikes')
317
318
  - method: Method for RND estimation ('rookley' or 'breeden')
319
+ - centered: Whether to center distributions at their modes (peaks)
318
320
 
319
321
  Returns:
320
322
  - Dictionary with pdf_surface, cdf_surface, x_surface, and moments
@@ -325,7 +327,8 @@ class VolyClient:
325
327
  model_results=model_results,
326
328
  domain_params=domain_params,
327
329
  return_domain=return_domain,
328
- method=method
330
+ method=method,
331
+ centered=centered
329
332
  )
330
333
 
331
334
  # -------------------------------------------------------------------------
@@ -349,8 +352,6 @@ class VolyClient:
349
352
  Returns:
350
353
  - Historical price data with OHLCV columns and datetime index
351
354
  """
352
- logger.info(f"Fetching historical {currency} data from {exchange_name}...")
353
-
354
355
  return get_historical_data(
355
356
  currency=currency,
356
357
  lookback_days=lookback_days,
@@ -362,7 +363,8 @@ class VolyClient:
362
363
  def get_hd_surface(model_results: pd.DataFrame,
363
364
  df_hist: pd.DataFrame,
364
365
  domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
365
- return_domain: str = 'log_moneyness') -> Dict[str, Any]:
366
+ return_domain: str = 'log_moneyness',
367
+ centered: bool = False) -> Dict[str, Any]:
366
368
  """
367
369
  Generate historical density surface using normal distributions.
368
370
 
@@ -371,6 +373,7 @@ class VolyClient:
371
373
  - df_hist: DataFrame with historical price data
372
374
  - domain_params: Tuple of (min_log_moneyness, max_log_moneyness, num_points)
373
375
  - return_domain: Domain for results ('log_moneyness', 'moneyness', 'returns', 'strikes')
376
+ - centered: Whether to center distributions at their modes (peaks)
374
377
 
375
378
  Returns:
376
379
  - Dictionary with pdf_surface, cdf_surface, x_surface, and moments
@@ -381,5 +384,6 @@ class VolyClient:
381
384
  model_results=model_results,
382
385
  df_hist=df_hist,
383
386
  domain_params=domain_params,
384
- return_domain=return_domain
387
+ return_domain=return_domain,
388
+ centered=centered
385
389
  )
@@ -12,7 +12,13 @@ from scipy import stats
12
12
  from voly.utils.logger import logger, catch_exception
13
13
  from voly.exceptions import VolyError
14
14
  from voly.core.rnd import get_all_moments
15
- from voly.utils.density import prepare_domains, normalize_density, transform_to_domains, select_domain_results
15
+ from voly.utils.density import (
16
+ prepare_domains,
17
+ normalize_density,
18
+ transform_to_domains,
19
+ select_domain_results,
20
+ center_distributions
21
+ )
16
22
 
17
23
 
18
24
  @catch_exception
@@ -149,7 +155,8 @@ def calculate_normal_hd(df_hist: pd.DataFrame,
149
155
  def get_hd_surface(model_results: pd.DataFrame,
150
156
  df_hist: pd.DataFrame,
151
157
  domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
152
- return_domain: str = 'log_moneyness') -> Dict[str, Any]:
158
+ return_domain: str = 'log_moneyness',
159
+ centered: bool = False) -> Dict[str, Any]:
153
160
  """
154
161
  Generate historical density surface using normal distributions.
155
162
 
@@ -163,6 +170,8 @@ def get_hd_surface(model_results: pd.DataFrame,
163
170
  (min_log_moneyness, max_log_moneyness, num_points)
164
171
  return_domain : str
165
172
  Domain for results ('log_moneyness', 'moneyness', 'returns', 'strikes')
173
+ centered : bool
174
+ Whether to center distributions at their modes (peaks)
166
175
 
167
176
  Returns:
168
177
  --------
@@ -236,6 +245,11 @@ def get_hd_surface(model_results: pd.DataFrame,
236
245
  if not pdf_surface:
237
246
  raise VolyError("No valid densities could be calculated. Check your input data.")
238
247
 
248
+ # Center distributions if requested
249
+ if centered:
250
+ pdf_surface, cdf_surface = center_distributions(pdf_surface, cdf_surface, x_surface)
251
+ logger.info("Distributions have been centered at their modes")
252
+
239
253
  # Create DataFrame with moments
240
254
  moments = pd.DataFrame(all_moments).T
241
255
 
@@ -11,7 +11,13 @@ from voly.exceptions import VolyError
11
11
  from voly.models import SVIModel
12
12
  from voly.formulas import bs, d1, d2
13
13
  from scipy import stats
14
- from voly.utils.density import prepare_domains, normalize_density, transform_to_domains, select_domain_results
14
+ from voly.utils.density import (
15
+ prepare_domains,
16
+ normalize_density,
17
+ transform_to_domains,
18
+ select_domain_results,
19
+ center_distributions
20
+ )
15
21
 
16
22
 
17
23
  @catch_exception
@@ -299,7 +305,8 @@ def get_all_moments(x, pdf, model_params=None):
299
305
  def get_rnd_surface(model_results: pd.DataFrame,
300
306
  domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
301
307
  return_domain: str = 'log_moneyness',
302
- method: str = 'rookley') -> Dict[str, Any]:
308
+ method: str = 'rookley',
309
+ centered: bool = False) -> Dict[str, Any]:
303
310
  """
304
311
  Generate risk-neutral density surface from volatility surface parameters.
305
312
 
@@ -313,6 +320,8 @@ def get_rnd_surface(model_results: pd.DataFrame,
313
320
  Domain for results ('log_moneyness', 'moneyness', 'returns', 'strikes')
314
321
  method : str
315
322
  Method for RND estimation ('rookley' or 'breeden')
323
+ centered : bool
324
+ Whether to center distributions at their modes (peaks)
316
325
 
317
326
  Returns:
318
327
  --------
@@ -379,6 +388,11 @@ def get_rnd_surface(model_results: pd.DataFrame,
379
388
  if not pdf_surface:
380
389
  raise VolyError("No valid densities could be calculated. Check your input data.")
381
390
 
391
+ # Center distributions if requested
392
+ if centered:
393
+ pdf_surface, cdf_surface = center_distributions(pdf_surface, cdf_surface, x_surface)
394
+ logger.info("Distributions have been centered at their modes")
395
+
382
396
  # Create DataFrame with moments
383
397
  moments = pd.DataFrame(all_moments).T
384
398
 
@@ -7,7 +7,7 @@ densities across different domains, used by both RND and HD calculations.
7
7
 
8
8
  import numpy as np
9
9
  from typing import Dict, Tuple, List, Any
10
- from scipy import stats
10
+ from scipy import stats, interpolate
11
11
  from voly.utils.logger import catch_exception
12
12
  from voly.formulas import d1, d2
13
13
 
@@ -153,3 +153,52 @@ def select_domain_results(pdfs: Dict[str, np.ndarray],
153
153
  cdf = pdfs['cdf']
154
154
 
155
155
  return pdf, cdf, x
156
+
157
+
158
+ @catch_exception
159
+ def center_distributions(pdf_surface: Dict[str, np.ndarray],
160
+ cdf_surface: Dict[str, np.ndarray],
161
+ x_surface: Dict[str, np.ndarray]) -> Tuple[Dict[str, np.ndarray], Dict[str, np.ndarray]]:
162
+ """
163
+ Center distributions so their peaks (modes) are at x=0 while maintaining the same domain.
164
+
165
+ This function shifts each distribution so that its mode (peak) is at x=0,
166
+ without changing the domain range. It uses interpolation to recalculate
167
+ the PDF and CDF values on the original domain.
168
+
169
+ Parameters:
170
+ -----------
171
+ pdf_surface : Dict[str, np.ndarray]
172
+ Dictionary of PDFs by maturity
173
+ cdf_surface : Dict[str, np.ndarray]
174
+ Dictionary of CDFs by maturity
175
+ x_surface : Dict[str, np.ndarray]
176
+ Dictionary of x-domains by maturity
177
+
178
+ Returns:
179
+ --------
180
+ Tuple[Dict[str, np.ndarray], Dict[str, np.ndarray]]
181
+ (centered_pdf_surface, centered_cdf_surface)
182
+ """
183
+ centered_pdf_surface = {}
184
+ centered_cdf_surface = {}
185
+
186
+ for maturity in pdf_surface.keys():
187
+ pdf = pdf_surface[maturity]
188
+ cdf = cdf_surface[maturity]
189
+ x = x_surface[maturity]
190
+
191
+ # Find the mode (peak) of the distribution
192
+ mode_idx = np.argmax(pdf)
193
+ mode = x[mode_idx]
194
+
195
+ # Create interpolation functions for the original distributions
196
+ f_pdf = interpolate.interp1d(x, pdf, bounds_error=False, fill_value=0)
197
+ f_cdf = interpolate.interp1d(x, cdf, bounds_error=False, fill_value=0)
198
+
199
+ # Evaluate the centered distributions on the original domain
200
+ # g(x) = f(x + mode) shifts the distribution so that the peak is at x=0
201
+ centered_pdf_surface[maturity] = f_pdf(x + mode)
202
+ centered_cdf_surface[maturity] = f_cdf(x + mode)
203
+
204
+ return centered_pdf_surface, centered_cdf_surface
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voly
3
- Version: 0.0.154
3
+ Version: 0.0.156
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
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