voly 0.0.1__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 ADDED
@@ -0,0 +1,243 @@
1
+ """
2
+ Option pricing formulas and general calculations.
3
+ """
4
+
5
+ import numpy as np
6
+ from scipy.stats import norm
7
+ from typing import Tuple, Dict, Union, List, Optional
8
+ from voly.utils.logger import catch_exception
9
+ from voly.models import SVIModel
10
+ from math import exp
11
+
12
+
13
+ def d1(s: float, k: float, r: float, vol: float, t: float) -> float:
14
+ if vol <= 0 or t <= 0:
15
+ return np.nan
16
+ return (np.log(s / k) + (r + vol * vol / 2) * t) / (vol * np.sqrt(t))
17
+
18
+
19
+ def d2(s: float, k: float, r: float, vol: float, t: float) -> float:
20
+ if vol <= 0 or t <= 0:
21
+ return np.nan
22
+ return d1(s, k, r, vol, t) - vol * np.sqrt(t)
23
+
24
+
25
+ @catch_exception
26
+ def bs(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
27
+ if vol <= 0 or t <= 0:
28
+ # Intrinsic value at expiry
29
+ if option_type.lower() in ["call", "c"]:
30
+ return max(0, s - k)
31
+ else:
32
+ return max(0, k - s)
33
+
34
+ d1_val = d1(s, k, r, vol, t)
35
+ d2_val = d2(s, k, r, vol, t)
36
+
37
+ if option_type.lower() in ["call", "c"]:
38
+ return s * norm.cdf(d1_val) - k * np.exp(-r * t) * norm.cdf(d2_val)
39
+ else: # put
40
+ return k * np.exp(-r * t) * norm.cdf(-d2_val) - s * norm.cdf(-d1_val)
41
+
42
+
43
+ @catch_exception
44
+ def delta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
45
+ if vol <= 0 or t <= 0:
46
+ # At expiry, delta is either 0 or 1 for call, 0 or -1 for put
47
+ if option_type.lower() in ["call", "c"]:
48
+ return 1.0 if s > k else 0.0
49
+ else:
50
+ return -1.0 if s < k else 0.0
51
+
52
+ d1_val = d1(s, k, r, vol, t)
53
+
54
+ if option_type.lower() in ["call", "c"]:
55
+ return norm.cdf(d1_val)
56
+ else: # put
57
+ return norm.cdf(d1_val) - 1.0
58
+
59
+
60
+ @catch_exception
61
+ def gamma(s: float, k: float, r: float, vol: float, t: float) -> float:
62
+ if vol <= 0 or t <= 0:
63
+ return 0.0
64
+
65
+ d1_val = d1(s, k, r, vol, t)
66
+ return norm.pdf(d1_val) / (s * vol * np.sqrt(t))
67
+
68
+
69
+ @catch_exception
70
+ def vega(s: float, k: float, r: float, vol: float, t: float) -> float:
71
+ if vol <= 0 or t <= 0:
72
+ return 0.0
73
+
74
+ d1_val = d1(s, k, r, vol, t)
75
+ return s * norm.pdf(d1_val) * np.sqrt(t) / 100 # Divided by 100 for 1% change
76
+
77
+
78
+ @catch_exception
79
+ def theta(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
80
+ if vol <= 0 or t <= 0:
81
+ return 0.0
82
+
83
+ d1_val = d1(s, k, r, vol, t)
84
+ d2_val = d2(s, k, r, vol, t)
85
+
86
+ # First part of theta (same for both call and put)
87
+ theta_part1 = -s * norm.pdf(d1_val) * vol / (2 * np.sqrt(t))
88
+
89
+ # Second part depends on option type
90
+ if option_type.lower() in ["call", "c"]:
91
+ theta_part2 = -r * k * np.exp(-r * t) * norm.cdf(d2_val)
92
+ else: # put
93
+ theta_part2 = r * k * np.exp(-r * t) * norm.cdf(-d2_val)
94
+
95
+ # Return theta per day (t is in years)
96
+ return (theta_part1 + theta_part2) / 365.0
97
+
98
+
99
+ @catch_exception
100
+ def rho(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
101
+ if vol <= 0 or t <= 0:
102
+ return 0.0
103
+
104
+ d2_val = d2(s, k, r, vol, t)
105
+
106
+ if option_type.lower() in ["call", "c"]:
107
+ return k * t * np.exp(-r * t) * norm.cdf(d2_val) / 100
108
+ else: # put
109
+ return -k * t * np.exp(-r * t) * norm.cdf(-d2_val) / 100
110
+
111
+
112
+ @catch_exception
113
+ def vanna(s: float, k: float, r: float, vol: float, t: float) -> float:
114
+ if vol <= 0 or t <= 0:
115
+ return 0.0
116
+
117
+ d1_val = d1(s, k, r, vol, t)
118
+ d2_val = d2(s, k, r, vol, t)
119
+
120
+ return -norm.pdf(d1_val) * d2_val / vol
121
+
122
+
123
+ @catch_exception
124
+ def volga(s: float, k: float, r: float, vol: float, t: float) -> float:
125
+ if vol <= 0 or t <= 0:
126
+ return 0.0
127
+
128
+ d1_val = d1(s, k, r, vol, t)
129
+ d2_val = d2(s, k, r, vol, t)
130
+
131
+ return s * norm.pdf(d1_val) * np.sqrt(t) * d1_val * d2_val / vol
132
+
133
+
134
+ @catch_exception
135
+ def charm(s: float, k: float, r: float, vol: float, t: float, option_type: str = 'call') -> float:
136
+ if vol <= 0 or t <= 0:
137
+ return 0.0
138
+
139
+ d1_val = d1(s, k, r, vol, t)
140
+ d2_val = d2(s, k, r, vol, t)
141
+
142
+ # First term is the same for calls and puts
143
+ term1 = -norm.pdf(d1_val) * d1_val / (2 * t)
144
+
145
+ # Second term depends on option type
146
+ if option_type.lower() in ["call", "c"]:
147
+ term2 = -r * np.exp(-r * t) * norm.cdf(d2_val)
148
+ else: # put
149
+ term2 = r * np.exp(-r * t) * norm.cdf(-d2_val)
150
+
151
+ # Return charm per day (t is in years)
152
+ return (term1 + term2) / 365.0
153
+
154
+
155
+ @catch_exception
156
+ def greeks(s: float, k: float, r: float, vol: float, t: float,
157
+ option_type: str = 'call') -> Dict[str, float]:
158
+ return {
159
+ 'price': bs(s, k, r, vol, t, option_type),
160
+ 'delta': delta(s, k, r, vol, t, option_type),
161
+ 'gamma': gamma(s, k, r, vol, t),
162
+ 'vega': vega(s, k, r, vol, t),
163
+ 'theta': theta(s, k, r, vol, t, option_type),
164
+ 'rho': rho(s, k, r, vol, t, option_type),
165
+ 'vanna': vanna(s, k, r, vol, t),
166
+ 'volga': volga(s, k, r, vol, t),
167
+ 'charm': charm(s, k, r, vol, t, option_type)
168
+ }
169
+
170
+
171
+ @catch_exception
172
+ def iv(option_price: float, s: float, k: float, r: float, t: float,
173
+ option_type: str = 'call', precision: float = 1e-8,
174
+ max_iterations: int = 100) -> float:
175
+ """
176
+ Calculate implied volatility using Newton-Raphson method.
177
+
178
+ Parameters:
179
+ - option_price: Market price of the option
180
+ - s: Underlying price
181
+ - k: Strike price
182
+ - r: Risk-free rate
183
+ - t: Time to expiry in years
184
+ - option_type: 'call' or 'put'
185
+ - precision: Desired precision
186
+ - max_iterations: Maximum number of iterations
187
+
188
+ Returns:
189
+ - Implied volatility
190
+ """
191
+ if t <= 0:
192
+ return np.nan
193
+
194
+ # Check if option price is within theoretical bounds
195
+ if option_type.lower() in ["call", "c"]:
196
+ intrinsic = max(0, s - k * np.exp(-r * t))
197
+ if option_price < intrinsic:
198
+ return np.nan # Price below intrinsic value
199
+ if option_price >= s:
200
+ return np.inf # Price exceeds underlying
201
+ else: # put
202
+ intrinsic = max(0, k * np.exp(-r * t) - s)
203
+ if option_price < intrinsic:
204
+ return np.nan # Price below intrinsic value
205
+ if option_price >= k:
206
+ return np.inf # Price exceeds strike
207
+
208
+ # Initial guess - Manaster and Koehler (1982) method
209
+ vol = np.sqrt(2 * np.pi / t) * option_price / s
210
+
211
+ # Ensure initial guess is reasonable
212
+ vol = max(0.001, min(vol, 5.0))
213
+
214
+ for _ in range(max_iterations):
215
+ # Calculate option price and vega with current volatility
216
+ price = bs(s, k, r, vol, t, option_type)
217
+ v = vega(s, k, r, vol, t)
218
+
219
+ # Calculate price difference
220
+ price_diff = price - option_price
221
+
222
+ # Check if precision reached
223
+ if abs(price_diff) < precision:
224
+ return vol
225
+
226
+ # Avoid division by zero
227
+ if abs(v) < 1e-10:
228
+ # Change direction based on whether price is too high or too low
229
+ vol = vol * 1.5 if price_diff < 0 else vol * 0.5
230
+ else:
231
+ # Newton-Raphson update
232
+ vol = vol - price_diff / (v * 100) # Vega is for 1% change
233
+
234
+ # Ensure volatility stays in reasonable bounds
235
+ vol = max(0.001, min(vol, 5.0))
236
+
237
+ # If we reach here, we didn't converge
238
+ return np.nan
239
+
240
+
241
+ @catch_exception
242
+ def rnd(log_moneyness: float, total_var: float) -> float:
243
+ return np.exp(-(log_moneyness ** 2) / (2 * total_var)) / (np.sqrt(2 * np.pi * total_var))
voly/models.py ADDED
@@ -0,0 +1,86 @@
1
+ """
2
+ Volatility models for the Voly package.
3
+ """
4
+
5
+ import numpy as np
6
+ from typing import Tuple, Dict, List, Optional, Union
7
+
8
+ # Configuration settings
9
+ DEFAULT_MONEYNESS_RANGE = (-2, 2)
10
+ DEFAULT_MONEYNESS_POINTS = 500
11
+ MIN_DTE = 2.0 # Minimum days to expiry to include
12
+
13
+
14
+ class SVIModel:
15
+ """
16
+ Stochastic Volatility Inspired (SVI) model.
17
+
18
+ This class provides methods for calculating implied volatility using the
19
+ SVI parameterization, as well as its derivatives and related functions.
20
+ """
21
+
22
+ # Default initial parameters and bounds
23
+ DEFAULT_INITIAL_PARAMS = [0.04, 0.1, 0.2, -0.5, 0.01]
24
+ DEFAULT_PARAM_BOUNDS = ([-np.inf, 0, 0, -1, -np.inf], [np.inf, np.inf, np.inf, 1, np.inf])
25
+
26
+ # Parameter names for reference
27
+ PARAM_NAMES = ['a', 'b', 'sigma', 'rho', 'm']
28
+ JW_PARAM_NAMES = ['nu', 'psi', 'p', 'c', 'nu_tilde']
29
+
30
+ # Parameter descriptions for documentation
31
+ PARAM_DESCRIPTIONS = {
32
+ 'a': 'Base level of total implied variance',
33
+ 'b': 'Volatility skewness/smile modulation (controls wing slopes)',
34
+ 'sigma': 'Convexity control of the volatility smile (reduces ATM curvature)',
35
+ 'rho': 'Skewness/slope of the volatility smile (-1 to 1, rotates smile)',
36
+ 'm': 'Horizontal shift of the smile peak',
37
+ 'nu': 'ATM variance (level of ATM volatility)',
38
+ 'psi': 'ATM volatility skew (affects the gradient of the curve at ATM point)',
39
+ 'p': 'Slope of put wing (left side of curve)',
40
+ 'c': 'Slope of call wing (right side of curve)',
41
+ 'nu_tilde': 'Minimum implied total variance',
42
+ }
43
+
44
+ @staticmethod
45
+ def svi(moneyness: float, a: float, b: float, sigma: float, rho: float, m: float) -> float:
46
+ return a + b * (rho * (moneyness - m) + np.sqrt((moneyness - m) ** 2 + sigma ** 2))
47
+
48
+ @staticmethod
49
+ def svi_d(moneyness: float, a: float, b: float, sigma: float, rho: float, m: float) -> float:
50
+ return b * (rho + ((moneyness - m) / np.sqrt((moneyness - m) ** 2 + sigma ** 2)))
51
+
52
+ @staticmethod
53
+ def svi_dd(moneyness: float, a: float, b: float, sigma: float, rho: float, m: float) -> float:
54
+ return b * sigma ** 2 / ((moneyness - m) ** 2 + sigma ** 2) ** 1.5
55
+
56
+ @staticmethod
57
+ def svi_min_strike(sigma: float, rho: float, m: float) -> float:
58
+ return m - ((sigma * rho) / np.sqrt(1 - rho ** 2))
59
+
60
+ @staticmethod
61
+ def raw_to_jw_params(a: float, b: float, sigma: float, rho: float, m: float, t: float) -> Tuple[float, float, float, float, float]:
62
+ nu = (a + b * ((-rho) * m + np.sqrt(m ** 2 + sigma ** 2))) / t
63
+ psi = (1 / np.sqrt(nu * t)) * (b / 2) * (rho - (m / np.sqrt(m ** 2 + sigma ** 2)))
64
+ p = (1 / np.sqrt(nu * t)) * b * (1 - rho)
65
+ c = (1 / np.sqrt(nu * t)) * b * (1 + rho)
66
+ nu_tilde = (1 / t) * (a + b * sigma * np.sqrt(1 - rho ** 2))
67
+ return nu, psi, p, c, nu_tilde
68
+
69
+ @staticmethod
70
+ def jw_to_raw_params(nu: float, phi: float, p: float, c: float, nu_tilde: float, t: float) -> Tuple[float, float, float, float, float]:
71
+ w = nu * t
72
+ b = (c + p) / 2
73
+ rho = (c - p) / (c + p)
74
+ beta = rho - ((2 * w * phi) / b)
75
+ alpha = np.sign(beta) * (np.sqrt((1 / (beta ** 2)) - 1))
76
+ m = (((nu ** 2) - (nu_tilde ** 2)) * t) / (
77
+ b * ((-rho) + (np.sign(alpha) * np.sqrt(1 + alpha ** 2)) - (alpha * np.sqrt(1 - rho ** 2))))
78
+ sigma = alpha * m
79
+ a = ((nu_tilde ** 2) * t) - (b * sigma * np.sqrt(1 - rho ** 2))
80
+ return a, b, sigma, rho, m
81
+
82
+
83
+ # Models dictionary for easy access
84
+ MODELS = {
85
+ 'svi': SVIModel,
86
+ }
voly/utils/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Utility functions for the Voly package.
3
+
4
+ This module contains utility functions for logging, error handling,
5
+ and other helper functions.
6
+ """
7
+
8
+ from voly.utils.logger import logger, catch_exception
voly/utils/logger.py ADDED
@@ -0,0 +1,72 @@
1
+ """
2
+ Logger module for the Voly package.
3
+
4
+ This module provides a centralized logger configuration and
5
+ a decorator for error catching.
6
+ """
7
+
8
+ import os
9
+ from datetime import datetime
10
+ import sys
11
+ from functools import wraps
12
+ import asyncio
13
+ from loguru import logger
14
+
15
+ # Remove the default handler first
16
+ logger.remove()
17
+
18
+ # Handler for console output - less verbose output
19
+ logger.add(
20
+ sys.stderr,
21
+ level="INFO",
22
+ backtrace=False, # Don't show traceback
23
+ diagnose=False # Don't show variables
24
+ )
25
+
26
+
27
+ def setup_file_logging(logs_dir="logs/"):
28
+ """
29
+ Set up file-based logging. This is optional and can be called
30
+ by the end user if they want file-based logging.
31
+
32
+ Parameters:
33
+ logs_dir (str): Directory to store log files
34
+ """
35
+ os.makedirs(logs_dir, exist_ok=True)
36
+ logger.add(
37
+ os.path.join(logs_dir, "voly_{time:YYYY-MM-DD}.log"),
38
+ level="INFO",
39
+ rotation="00:00",
40
+ retention="7 days",
41
+ backtrace=True, # Keep full traceback in file
42
+ diagnose=True # Keep variables in file
43
+ )
44
+ logger.info("File logging initialized")
45
+
46
+
47
+ # Decorator for error catching (supports both sync and async functions)
48
+ def catch_exception(func):
49
+ @wraps(func)
50
+ def sync_wrapper(*args, **kwargs):
51
+ try:
52
+ return func(*args, **kwargs)
53
+ except Exception as e:
54
+ logger.error(f"{func.__name__}: {str(e)}")
55
+ raise
56
+
57
+ @wraps(func)
58
+ async def async_wrapper(*args, **kwargs):
59
+ try:
60
+ return await func(*args, **kwargs)
61
+ except Exception as e:
62
+ logger.error(f"{func.__name__}: {str(e)}")
63
+ raise
64
+
65
+ if asyncio.iscoroutinefunction(func):
66
+ return async_wrapper
67
+ else:
68
+ return sync_wrapper
69
+
70
+
71
+ # Initialize logger with basic info
72
+ logger.info("Voly logger initialized")
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 manudc22
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.2
2
+ Name: voly
3
+ Version: 0.0.1
4
+ Summary: Options & volatility research package
5
+ Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Financial and Insurance Industry
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Topic :: Office/Business :: Financial
16
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
17
+ Classifier: Operating System :: OS Independent
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pandas>=1.3.0
22
+ Requires-Dist: numpy>=1.20.0
23
+ Requires-Dist: scipy>=1.7.0
24
+ Requires-Dist: plotly>=5.3.0
25
+ Requires-Dist: scikit-learn>=1.0.0
26
+ Requires-Dist: websockets>=10.0
27
+ Requires-Dist: requests>=2.26.0
28
+ Requires-Dist: loguru>=0.5.3
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
31
+ Requires-Dist: black>=22.1.0; extra == "dev"
32
+ Requires-Dist: isort>=5.10.1; extra == "dev"
33
+ Requires-Dist: mypy>=0.931; extra == "dev"
34
+ Requires-Dist: flake8>=4.0.1; extra == "dev"
35
+ Requires-Dist: twine>=4.0.0; extra == "dev"
36
+ Requires-Dist: build>=0.8.0; extra == "dev"
37
+
38
+ # Voly - Options & Volatility Research Package
39
+
40
+ Voly is a Python package for options data analysis, volatility surface modeling, and risk-neutral density estimation. It provides a simple interface for handling common options research tasks, including data collection, model fitting, and visualization.
41
+
42
+ ## Features
43
+
44
+ - **Data Collection**: Fetch options data from exchanges (currently supports Deribit)
45
+ - **Volatility Surface Modeling**: Fit SVI (Stochastic Volatility Inspired) model to market data
46
+ - **Risk-Neutral Density**: Calculate and analyze risk-neutral density distributions
47
+ - **Surface Interpolation**: Interpolate volatility surfaces across different expiries
48
+ - **Options Pricing & Greeks**: Calculate Black-Scholes prices and all major Greeks
49
+ - **Visualizations**: Generate interactive plots using Plotly
50
+
51
+ ## Installation
52
+
53
+ You can install Voly using pip:
54
+
55
+ ```bash
56
+ pip install voly
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ```python
62
+ import pandas as pd
63
+ from voly import VolyClient
64
+
65
+ # Initialize the client
66
+ voly = VolyClient()
67
+
68
+ # Fetch options data (or load your own data)
69
+ option_chain = voly.get_option_chain(exchange='deribit', currency='BTC')
70
+
71
+ # Fit an SVI model to the data with visualization
72
+ fit_results = voly.fit_model(option_chain, plot=True)
73
+
74
+ # Calculate risk-neutral density
75
+ rnd_results = voly.rnd(fit_results, spot_price=option_chain['underlying_price'].iloc[0], plot=True)
76
+
77
+ # Calculate probability of price above a target
78
+ probability = voly.probability(rnd_results, target_price=32000, direction='above')
79
+ print(f"Probability of price above 32000: {probability:.2%}")
80
+
81
+ # Calculate option Greeks
82
+ greeks = voly.greeks(s=30000, k=32000, r=0.05, vol=0.6, t=0.25, option_type='call')
83
+ print(f"Option Greeks: {greeks}")
84
+ ```
85
+
86
+ ## Example: Visualizing the Volatility Surface
87
+
88
+ ```python
89
+ import pandas as pd
90
+ from voly import VolyClient
91
+
92
+ # Initialize the client
93
+ voly = VolyClient()
94
+
95
+ # Load your own data or fetch from exchange
96
+ # The DataFrame should have columns for:
97
+ # - log_moneyness
98
+ # - strike
99
+ # - mark_iv (implied volatility)
100
+ # - yte (years to expiry)
101
+ # - dte (days to expiry)
102
+ # - maturity_name (identifier for the expiry)
103
+ data = pd.read_csv('your_options_data.csv')
104
+
105
+ # Fit the SVI model
106
+ fit_results = voly.fit_model(data, plot=True)
107
+
108
+ # Display the 3D volatility surface
109
+ surface_fig = fit_results['plots']['surface_3d']
110
+ surface_fig.show()
111
+ ```
112
+
113
+ ## Documentation
114
+
115
+ For full documentation, visit [https://docs.voly.io](https://docs.voly.io)
116
+
117
+ ## Development
118
+
119
+ ### Setting up the development environment
120
+
121
+ ```bash
122
+ # Clone the repository
123
+ git clone https://github.com/manudc22/voly.git
124
+ cd voly
125
+
126
+ # Install development dependencies & activate venv
127
+ ./env_setup.sh --all
128
+ ```
129
+
130
+ ## License
131
+
132
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,18 @@
1
+ voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
+ voly/client.py,sha256=BBoMURx5nlmwXpH__eQMjNEKy5WMQi3nJyUeVPStHVc,17589
3
+ voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
+ voly/formulas.py,sha256=aG_HSq_a4j7TcuKiINlHSpmNdmfZa_fzYbAk8EGt954,7427
5
+ voly/models.py,sha256=YJ12aamLz_-aOni4Qm0_XV9u4bjKK3vfJz0J2gc1h0o,3565
6
+ voly/core/__init__.py,sha256=GU6l7hpxJfitPx9jnmBtcb_QIeqOO8liZsSbLXXSbq8,384
7
+ voly/core/charts.py,sha256=GF55IS-aZfcc_0yoSPRPIPBPcJhFD1El18wNCo_mI_A,29918
8
+ voly/core/data.py,sha256=4nQuinjqG0vOY6peN2p00-1_MUck6zo7Hz0saikGneQ,11355
9
+ voly/core/fit.py,sha256=oDNS0Rb7o1g8ioigQ-iqJLNLy45bzARcgETiP9jBi0E,11305
10
+ voly/core/interpolate.py,sha256=fi_OQNdedDrdgawXCwhlO79HLGUvJ-nWjWxqFLd9ijU,8212
11
+ voly/core/rnd.py,sha256=Qlk1Ylo0KEz-BQ6VaGZ-0qsgFyTjoMX1DqCoGXAS6kk,12163
12
+ voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
13
+ voly/utils/logger.py,sha256=mujudi610WveZoGJJzoBtGbraFNJUzwWFTza645j08U,1821
14
+ voly-0.0.1.dist-info/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
15
+ voly-0.0.1.dist-info/METADATA,sha256=4AkfO7MvVz_fB5GZV68zvk4hv1ETQd0Z16IB0ZQhi-A,4136
16
+ voly-0.0.1.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
17
+ voly-0.0.1.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
18
+ voly-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (76.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ voly