voly 0.0.184__py3-none-any.whl → 0.0.186__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/core/fit.py CHANGED
@@ -13,7 +13,9 @@ from voly.utils.logger import logger, catch_exception
13
13
  from voly.formulas import get_domain
14
14
  from voly.exceptions import VolyError
15
15
  from voly.models import SVIModel
16
+ from concurrent.futures import ThreadPoolExecutor
16
17
  import warnings
18
+ import time
17
19
 
18
20
  warnings.filterwarnings("ignore")
19
21
 
@@ -21,14 +23,17 @@ warnings.filterwarnings("ignore")
21
23
  @catch_exception
22
24
  def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
23
25
  """
24
- Fit a volatility model to market data.
26
+ Fit a volatility model to market data with parallel processing.
25
27
 
26
28
  Parameters:
27
29
  - option_chain: DataFrame with market data
28
30
 
29
31
  Returns:
30
- - DataFrame with all fit results and performance metrics as columns, maturity_names as index
32
+ - DataFrame with all fit results and performance metrics as columns, maturity_dates as index
31
33
  """
34
+ # Start overall timer
35
+ start_total = time.time()
36
+
32
37
  # Define column names and their data types
33
38
  column_dtypes = {
34
39
  's': float,
@@ -61,39 +66,48 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
61
66
  'n_points': int
62
67
  }
63
68
 
64
- # Get unique maturities and sort them
65
- groups = option_chain.groupby('maturity_date')
66
- unique_ts = sorted(option_chain['t'].unique())
69
+ s = option_chain['index_price'].iloc[0]
67
70
  maturity_names = [option_chain[option_chain['t'] == t]['maturity_name'].iloc[0] for t in unique_ts]
68
-
69
- # Store results in a dictionary first
71
+ groups = option_chain.groupby('maturity_date')
72
+ params_dict = {}
70
73
  results_data = {col: [] for col in column_dtypes.keys()}
74
+ num_points = 2000 # Number of points for k_grid
75
+
76
+ def process_maturity(maturity, group):
77
+ """Process single maturity for SVI calibration."""
78
+ group = group[group['option_type'] == 'C']
79
+ duplicated_iv = group[group.duplicated('mark_iv', keep=False)]
80
+
81
+ # For each duplicated IV, keep the row closest to log_moneyness=0
82
+ def keep_closest_to_zero(subgroup):
83
+ idx = (subgroup['log_moneyness'].abs()).idxmin()
84
+ return subgroup.loc[[idx]]
85
+
86
+ # Apply the function to each duplicated mark_iv group
87
+ cleaned_duplicated_iv = (
88
+ duplicated_iv.groupby('mark_iv', group_keys=False)
89
+ .apply(keep_closest_to_zero)
90
+ )
71
91
 
72
- # ANSI color codes for terminal output
73
- GREEN, RED, RESET = '\033[32m', '\033[31m', '\033[0m'
74
-
75
- s = option_chain['index_price'].iloc[-1]
76
-
77
- # Dictionary to track fit results by maturity for arbitrage checks
78
- params_dict = {}
92
+ # Get rows with unique mark_iv (no duplicates)
93
+ unique_iv = group.drop_duplicates('mark_iv', keep=False)
79
94
 
80
- # Calibrate and check arbitrage
81
- for t in unique_ts:
82
- # Get data for this maturity
83
- maturity_data = option_chain[option_chain['t'] == t]
84
- maturity_name = maturity_data['maturity_name'].iloc[0]
95
+ # Combine cleaned duplicates and unique rows
96
+ maturity_data = pd.concat([unique_iv, cleaned_duplicated_iv])
97
+ #maturity_name = maturity_data['maturity_name'].iloc[0]
85
98
  maturity_date = maturity_data['maturity_date'].iloc[0]
86
99
 
87
- logger.info(f"Processing maturity {maturity_date}, t={t:.4f}")
88
-
89
- K = maturity_data['strikes'].values
90
- iv = maturity_data['mark_iv'].values
91
- vega = maturity_data['vega'].values if 'vega' in maturity_data.columns else np.ones_like(iv)
100
+ t = group['t'].iloc[0]
101
+ K = group['strikes'].values
102
+ iv = group['mark_iv'].values
103
+ vega = group['vega'].values if 'vega' in group.columns else np.ones_like(iv)
92
104
  k = np.log(K / s)
93
105
  w = (iv ** 2) * t
94
106
  mask = ~np.isnan(w) & ~np.isnan(vega) & ~np.isnan(k) & (iv >= 0)
95
107
  k, w, vega, iv = k[mask], w[mask], vega[mask], iv[mask]
96
108
 
109
+ logger.info(f"Processing maturity {maturity}, points after filtering: {len(k)}")
110
+
97
111
  params = [np.nan] * 5
98
112
  loss = np.inf
99
113
  nu = psi = p = c = nu_tilde = np.nan
@@ -112,6 +126,7 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
112
126
  a, b, m, rho, sigma = params
113
127
  a_scaled, b_scaled = a * t, b * t
114
128
 
129
+ # Transform to Jump-Wing parameters
115
130
  nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a_scaled, b_scaled, m, rho, sigma, t)
116
131
 
117
132
  # Compute fit statistics
@@ -128,7 +143,7 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
128
143
  usd_min_strike = np.exp(log_min_strike) * s
129
144
 
130
145
  # Butterfly arbitrage check
131
- k_range = np.linspace(min(k), max(k), 200)
146
+ k_range = np.linspace(min(k), max(k), num_points)
132
147
  w_k = lambda k: SVIModel.svi(k, a_scaled, b_scaled, m, rho, sigma)
133
148
  w_prime = lambda k: b_scaled * (rho + (k - m) / np.sqrt((k - m) ** 2 + sigma ** 2))
134
149
  w_double_prime = lambda k: b_scaled * sigma ** 2 / ((k - m) ** 2 + sigma ** 2) ** (3 / 2)
@@ -143,10 +158,12 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
143
158
  break
144
159
 
145
160
  # Log result
161
+ GREEN, RED, RESET = '\033[32m', '\033[31m', '\033[0m'
146
162
  status = f'{GREEN}SUCCESS{RESET}' if not np.isnan(params[0]) else f'{RED}FAILED{RESET}'
147
- logger.info(f'Optimization for {maturity_name}: {status}')
163
+ logger.info(f'Optimization for {maturity}: {status}')
148
164
  logger.info("================================================")
149
165
 
166
+ # Store results
150
167
  results_data['s'].append(float(s))
151
168
  results_data['u'].append(float(u))
152
169
  results_data['t'].append(float(t))
@@ -176,6 +193,18 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
176
193
  results_data['loss'].append(float(loss))
177
194
  results_data['n_points'].append(int(len(k)))
178
195
 
196
+ return maturity
197
+
198
+ # Parallel processing of maturities with timer
199
+ start_parallel = time.time()
200
+ with ThreadPoolExecutor() as executor:
201
+ futures = [executor.submit(process_maturity, maturity, group)
202
+ for maturity, group in groups]
203
+ for future in futures:
204
+ future.result()
205
+ end_parallel = time.time()
206
+ logger.info(f"Processing completed in {end_parallel - start_parallel:.4f} seconds")
207
+
179
208
  # Create results DataFrame
180
209
  results_df = pd.DataFrame(results_data, index=maturity_names)
181
210
 
@@ -187,12 +216,14 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
187
216
  except (ValueError, TypeError) as e:
188
217
  logger.warning(f"Could not convert column {col} to {dtype}: {e}")
189
218
 
190
- # Calendar arbitrage check (pre-correction)
219
+ # Sort by time to maturity
220
+ results_df = results_df.sort_values(by='t')
221
+
222
+ # Calendar arbitrage check (pre-correction) with timer
191
223
  logger.info("\nChecking calendar arbitrage (pre-correction)...")
192
- k_grid = np.linspace(-2, 2, 200)
224
+ k_grid = np.linspace(-2, 2, num_points)
193
225
  sorted_maturities = sorted(params_dict.keys(), key=lambda x: params_dict[x][0])
194
226
  calendar_arbitrage_free = True
195
-
196
227
  for i in range(len(sorted_maturities) - 1):
197
228
  mat1, mat2 = sorted_maturities[i], sorted_maturities[i + 1]
198
229
  t1, params1 = params_dict[mat1]
@@ -205,33 +236,24 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
205
236
 
206
237
  group = groups.get_group(mat2)
207
238
  K = group['strikes'].values
208
- s = group['index_price'].iloc[0]
209
239
  k_market = np.log(K / s)
210
240
  mask = ~np.isnan(k_market)
211
- k_check = np.unique(
212
- np.concatenate([k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), 200)]))
241
+ k_check = np.unique(np.concatenate([k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), num_points)]))
213
242
 
214
243
  for k_val in k_check:
215
244
  w1 = SVIModel.svi(k_val, a1 * t1, b1 * t1, m1, rho1, sigma1)
216
245
  w2 = SVIModel.svi(k_val, a2 * t2, b2 * t2, m2, rho2, sigma2)
217
246
  if w2 < w1 - 1e-6:
218
- logger.warning(f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: ")
219
- logger.warning(f"w1={w1:.6f}, w2={w2:.6f}")
247
+ logger.warning(f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: w1={w1:.6f}, w2={w2:.6f}")
220
248
  calendar_arbitrage_free = False
221
249
  break
222
250
  if not calendar_arbitrage_free:
223
251
  break
224
252
 
225
253
  for mat in sorted_maturities:
226
- idx = None
227
- for i, maturity_name in enumerate(maturity_names):
228
- if results_df.iloc[i]['maturity_date'] == mat:
229
- idx = results_df.index[i]
230
- break
231
- if idx is not None:
232
- results_df.at[idx, 'calendar_arbitrage_free'] = calendar_arbitrage_free
254
+ results_df.at[mat, 'calendar_arbitrage_free'] = calendar_arbitrage_free
233
255
 
234
- # Calendar arbitrage correction
256
+ # Calendar arbitrage correction with timer
235
257
  logger.info("\nPerforming calendar arbitrage correction...")
236
258
  for i in range(1, len(sorted_maturities)):
237
259
  mat2 = sorted_maturities[i]
@@ -243,7 +265,6 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
243
265
  continue
244
266
 
245
267
  group = groups.get_group(mat2)
246
- s = group['index_price'].iloc[0]
247
268
  K = group['strikes'].values
248
269
  iv = group['mark_iv'].values
249
270
  vega = group['vega'].values if 'vega' in group.columns else np.ones_like(iv)
@@ -278,7 +299,7 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
278
299
 
279
300
  # Update butterfly arbitrage check
280
301
  butterfly_arbitrage_free = True
281
- k_range = np.linspace(min(k), max(k), 200)
302
+ k_range = np.linspace(min(k), max(k), num_points)
282
303
  w_k = lambda k: SVIModel.svi(k, a_scaled, b_scaled, m, rho, sigma)
283
304
  w_prime = lambda k: b_scaled * (rho + (k - m) / np.sqrt((k - m) ** 2 + sigma ** 2))
284
305
  w_double_prime = lambda k: b_scaled * sigma ** 2 / ((k - m) ** 2 + sigma ** 2) ** (3 / 2)
@@ -292,35 +313,26 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
292
313
  butterfly_arbitrage_free = False
293
314
  break
294
315
 
295
- # Find the correct index to update
296
- idx = None
297
- for j, maturity_name in enumerate(maturity_names):
298
- if results_df.iloc[j]['maturity_date'] == mat2:
299
- idx = results_df.index[j]
300
- break
301
-
302
- if idx is not None:
303
- results_df.at[idx, 'a'] = float(a_scaled)
304
- results_df.at[idx, 'b'] = float(b_scaled)
305
- results_df.at[idx, 'm'] = float(m)
306
- results_df.at[idx, 'rho'] = float(rho)
307
- results_df.at[idx, 'sigma'] = float(sigma)
308
- results_df.at[idx, 'nu'] = float(nu)
309
- results_df.at[idx, 'psi'] = float(psi)
310
- results_df.at[idx, 'p'] = float(p)
311
- results_df.at[idx, 'c'] = float(c)
312
- results_df.at[idx, 'nu_tilde'] = float(nu_tilde)
313
- results_df.at[idx, 'rmse'] = float(rmse)
314
- results_df.at[idx, 'mae'] = float(mae)
315
- results_df.at[idx, 'r2'] = float(r2)
316
- results_df.at[idx, 'max_error'] = float(max_error)
317
- results_df.at[idx, 'log_min_strike'] = float(log_min_strike)
318
- results_df.at[idx, 'usd_min_strike'] = float(usd_min_strike)
319
- results_df.at[idx, 'butterfly_arbitrage_free'] = butterfly_arbitrage_free
320
- results_df.at[idx, 'fit_success'] = bool(not np.isnan(a))
316
+ results_df.at[mat2, 'a'] = float(a_scaled)
317
+ results_df.at[mat2, 'b'] = float(b_scaled)
318
+ results_df.at[mat2, 'm'] = float(m)
319
+ results_df.at[mat2, 'rho'] = float(rho)
320
+ results_df.at[mat2, 'sigma'] = float(sigma)
321
+ results_df.at[mat2, 'nu'] = float(nu)
322
+ results_df.at[mat2, 'psi'] = float(psi)
323
+ results_df.at[mat2, 'p'] = float(p)
324
+ results_df.at[mat2, 'c'] = float(c)
325
+ results_df.at[mat2, 'nu_tilde'] = float(nu_tilde)
326
+ results_df.at[mat2, 'rmse'] = float(rmse)
327
+ results_df.at[mat2, 'mae'] = float(mae)
328
+ results_df.at[mat2, 'r2'] = float(r2)
329
+ results_df.at[mat2, 'max_error'] = float(max_error)
330
+ results_df.at[mat2, 'log_min_strike'] = float(log_min_strike)
331
+ results_df.at[mat2, 'usd_min_strike'] = float(usd_min_strike)
332
+ results_df.at[mat2, 'butterfly_arbitrage_free'] = butterfly_arbitrage_free
333
+ results_df.at[mat2, 'fit_success'] = bool(not np.isnan(a))
321
334
 
322
335
  # Calendar arbitrage check (post-correction)
323
- logger.info("\nChecking calendar arbitrage (post-correction)...")
324
336
  calendar_arbitrage_free = True
325
337
  for i in range(len(sorted_maturities) - 1):
326
338
  mat1, mat2 = sorted_maturities[i], sorted_maturities[i + 1]
@@ -334,88 +346,27 @@ def fit_model(option_chain: pd.DataFrame) -> pd.DataFrame:
334
346
 
335
347
  group = groups.get_group(mat2)
336
348
  K = group['strikes'].values
337
- s = group['index_price'].iloc[0]
338
349
  k_market = np.log(K / s)
339
350
  mask = ~np.isnan(k_market)
340
351
  k_check = np.unique(np.concatenate(
341
- [k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), 200)]))
352
+ [k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), num_points)]))
342
353
 
343
354
  for k_val in k_check:
344
355
  w1 = SVIModel.svi(k_val, a1 * t1, b1 * t1, m1, rho1, sigma1)
345
356
  w2 = SVIModel.svi(k_val, a2 * t2, b2 * t2, m2, rho2, sigma2)
346
357
  if w2 < w1 - 1e-6:
347
- logger.warning(
348
- f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: w1={w1:.6f}, w2={w2:.6f}")
358
+ logger.warning(f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: w1={w1:.6f}, w2={w2:.6f}")
349
359
  calendar_arbitrage_free = False
350
360
  break
351
361
  if not calendar_arbitrage_free:
352
362
  break
353
363
 
354
364
  for mat in sorted_maturities:
355
- idx = None
356
- for j, maturity_name in enumerate(maturity_names):
357
- if results_df.iloc[j]['maturity_date'] == mat:
358
- idx = results_df.index[j]
359
- break
360
- if idx is not None:
361
- results_df.at[idx, 'calendar_arbitrage_free'] = calendar_arbitrage_free
365
+ results_df.at[mat, 'calendar_arbitrage_free'] = calendar_arbitrage_free
366
+
367
+ # End overall timer and print total time
368
+ end_total = time.time()
369
+ logger.info(f"\nTotal execution time for SVI fit: {end_total - start_total:.4f} seconds")
362
370
 
363
371
  logger.info("Model fitting complete.")
364
372
  return results_df
365
-
366
-
367
- @catch_exception
368
- def get_iv_surface(model_results: pd.DataFrame,
369
- domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
370
- return_domain: str = 'log_moneyness') -> Tuple[Dict[str, np.ndarray], Dict[str, np.ndarray]]:
371
- """
372
- Generate implied volatility surface using optimized SVI parameters.
373
-
374
- Works with both regular fit_results and interpolated_results dataframes.
375
-
376
- Parameters:
377
- - model_results: DataFrame from fit_model() or interpolate_model(). Maturity names or DTM as Index
378
- - domain_params: Tuple of (min, max, num_points) for the log-moneyness array
379
- - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'returns', 'strikes', 'delta')
380
-
381
- Returns:
382
- - Tuple of (iv_surface, x_surface)
383
- iv_surface: Dictionary mapping maturity/dtm names to IV arrays
384
- x_surface: Dictionary mapping maturity/dtm names to requested x domain arrays
385
- """
386
- # Check if required columns are present
387
- required_columns = ['a', 'b', 'm', 'rho', 'sigma', 't']
388
- missing_columns = [col for col in required_columns if col not in model_results.columns]
389
- if missing_columns:
390
- raise VolyError(f"Required columns missing in model_results: {missing_columns}")
391
-
392
- # Generate implied volatility surface in log-moneyness domain
393
- LM = np.linspace(domain_params[0], domain_params[1], domain_params[2])
394
-
395
- iv_surface = {}
396
- x_surface = {}
397
-
398
- # Process each maturity/dtm
399
- for i in model_results.index:
400
- # Calculate SVI total implied variance and convert to IV
401
- params = [
402
- model_results.loc[i, 'a'],
403
- model_results.loc[i, 'b'],
404
- model_results.loc[i, 'm'],
405
- model_results.loc[i, 'rho'],
406
- model_results.loc[i, 'sigma']
407
- ]
408
- s = model_results.loc[i, 's']
409
- r = model_results.loc[i, 'r']
410
- t = model_results.loc[i, 't']
411
-
412
- # Calculate implied volatility
413
- w = np.array([SVIModel.svi(x, *params) for x in LM])
414
- o = np.sqrt(w / t)
415
- iv_surface[i] = o
416
-
417
- # Calculate x domain for this maturity/dtm
418
- x = get_domain(domain_params, s, r, o, t, return_domain)
419
- x_surface[i] = x
420
-
421
- return iv_surface, x_surface
voly/models.py CHANGED
@@ -5,6 +5,9 @@ Volatility models for the Voly package.
5
5
  import numpy as np
6
6
  from numpy.linalg import solve
7
7
  from typing import Tuple, Dict, List, Optional, Union
8
+ from voly.utils.logger import logger
9
+ from scipy.optimize import minimize
10
+ from sklearn.metrics import mean_squared_error
8
11
 
9
12
 
10
13
  class SVIModel:
@@ -99,7 +102,7 @@ class SVIModel:
99
102
  return loss
100
103
 
101
104
  result = minimize(score, [sigma_init, m_init], bounds=[(0.001, None), (None, None)],
102
- tol=1e-16, method="Nelder-Mead", options={'maxfun': 5000})
105
+ tol=1e-16, method="SLSQP", options={'maxfun': 5000})
103
106
 
104
107
  sigma, m = result.x
105
108
  c, d, a_calib, loss = cls.calibration(tiv, vega, k, m, sigma)
@@ -120,8 +123,6 @@ class SVIModel:
120
123
  if np.any(np.isnan(params)) or np.any(np.isnan(prev_params)):
121
124
  return params
122
125
 
123
- from scipy.optimize import minimize
124
-
125
126
  a_init, b_init, m_init, rho_init, sigma_init = params
126
127
  a_prev, b_prev, m_prev, rho_prev, sigma_prev = prev_params
127
128
  k_constraint = np.unique(np.concatenate([k, np.linspace(min(k), max(k), len(k_grid))]))
@@ -129,7 +130,6 @@ class SVIModel:
129
130
  def objective(x):
130
131
  a, b, m, rho, sigma = x
131
132
  w_model = cls.svi(k, a * t, b * t, m, rho, sigma)
132
- from sklearn.metrics import mean_squared_error
133
133
  fit_loss = mean_squared_error(tiv, w_model, sample_weight=vega)
134
134
  param_deviation = sum(((x[i] - x_init) / max(abs(x_init), 1e-6)) ** 2
135
135
  for i, x_init in enumerate([a_init, b_init, m_init, rho_init, sigma_init]))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voly
3
- Version: 0.0.184
3
+ Version: 0.0.186
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -2,19 +2,19 @@ voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
2
  voly/client.py,sha256=dPyRRmZ_Gvo1zCZMo9eFOx2oaYocmkOt71fzdmOXFyM,14387
3
3
  voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
4
  voly/formulas.py,sha256=JnEs6G0wlfRNH6X_YEJMe2RtLH-ryhzufjsim73Bj3c,11176
5
- voly/models.py,sha256=A7zIUnIoH2rwb2_kp96xfFuOQ8CkGQ-XCx1cOGJSlco,7009
5
+ voly/models.py,sha256=2aNGsF3joCx4jGbiF8m0zxEW_nvcSBERSYPSKCXV3us,7019
6
6
  voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
7
7
  voly/core/charts.py,sha256=2S-BfCo30aj1_xlNLqF-za5rQWxF_mWKIdtdOe5bgbw,12735
8
8
  voly/core/data.py,sha256=9v9iuE2XdIIlzoRAB7q1ol7YghBzBsPGAiwZ11oDuis,13650
9
- voly/core/fit.py,sha256=CIayT6HKd-8pc8P76bc2vNivNmF_Uu4fHRNRqNup2R8,17210
9
+ voly/core/fit.py,sha256=pfGRzycJ9WERHc-nIowpWQQ8nZbGHlOlRDUxgkkfvnY,15448
10
10
  voly/core/hd.py,sha256=UFAyLncNUHivpPAcko6IK1bC55mudVtdlRFfXp63HXE,14771
11
11
  voly/core/interpolate.py,sha256=JkK172-FXyhesW3hY4pEeuJWG3Bugq7QZXbeKoRpLuo,5305
12
12
  voly/core/rnd.py,sha256=GoC3m1Q46Wnk5tV_mstr-3_aktHeue6BBLh4DQTciW0,13307
13
13
  voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
14
14
  voly/utils/density.py,sha256=q0fX4im9TGwMCZ32Hzdv8CNh56KnJo8bmG5w0gVWZH8,5879
15
15
  voly/utils/logger.py,sha256=4-_2bVJmq17Q0d7Rd2mPg1AeR8gxv6EPvcmBDMFWcSM,1744
16
- voly-0.0.184.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
17
- voly-0.0.184.dist-info/METADATA,sha256=RoKiGHZQ4w4M5KsFMq6hCDmCe8KkTAKCKhcnEDc9C98,4115
18
- voly-0.0.184.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
19
- voly-0.0.184.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
20
- voly-0.0.184.dist-info/RECORD,,
16
+ voly-0.0.186.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
17
+ voly-0.0.186.dist-info/METADATA,sha256=GUbTWWjdupPDNj3wHCYQ5b5qu0hmHWNsO8KPXRKpAW8,4115
18
+ voly-0.0.186.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
19
+ voly-0.0.186.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
20
+ voly-0.0.186.dist-info/RECORD,,
File without changes