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.
- {voly-0.0.154/src/voly.egg-info → voly-0.0.156}/PKG-INFO +1 -1
- {voly-0.0.154 → voly-0.0.156}/pyproject.toml +2 -2
- {voly-0.0.154 → voly-0.0.156}/src/voly/client.py +10 -6
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/hd.py +16 -2
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/rnd.py +16 -2
- {voly-0.0.154 → voly-0.0.156}/src/voly/utils/density.py +50 -1
- {voly-0.0.154 → voly-0.0.156/src/voly.egg-info}/PKG-INFO +1 -1
- {voly-0.0.154 → voly-0.0.156}/LICENSE +0 -0
- {voly-0.0.154 → voly-0.0.156}/README.md +0 -0
- {voly-0.0.154 → voly-0.0.156}/setup.cfg +0 -0
- {voly-0.0.154 → voly-0.0.156}/setup.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/__init__.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/__init__.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/charts.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/data.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/fit.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/core/interpolate.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/exceptions.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/formulas.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/models.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/utils/__init__.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly/utils/logger.py +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/SOURCES.txt +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/dependency_links.txt +0 -0
- {voly-0.0.154 → voly-0.0.156}/src/voly.egg-info/requires.txt +0 -0
- {voly-0.0.154 → voly-0.0.156}/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.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.
|
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'
|
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'
|
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
|
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'
|
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
|
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'
|
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
|
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
|