voly 0.0.226__py3-none-any.whl → 0.0.228__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
@@ -79,7 +79,7 @@ class VolyClient:
79
79
 
80
80
  try:
81
81
  option_chain = loop.run_until_complete(
82
- fetch_option_chain(exchange, currency, depth)
82
+ fetch_option_chain(exchange, currency)
83
83
  )
84
84
  return option_chain
85
85
  except VolyError as e:
@@ -97,68 +97,68 @@ class VolyClient:
97
97
 
98
98
  @staticmethod
99
99
  def d1(s: float, K: float, r: float, o: float, t: float,
100
- option_type: str = 'call') -> float:
101
- return d1(s, K, r, o, t, option_type)
100
+ flag: str = 'call') -> float:
101
+ return d1(s, K, r, o, t, flag)
102
102
 
103
103
  @staticmethod
104
104
  def d2(s: float, K: float, r: float, o: float, t: float,
105
- option_type: str = 'call') -> float:
106
- return d2(s, K, r, o, t, option_type)
105
+ flag: str = 'call') -> float:
106
+ return d2(s, K, r, o, t, flag)
107
107
 
108
108
  @staticmethod
109
109
  def bs(s: float, K: float, r: float, o: float, t: float,
110
- option_type: str = 'call') -> float:
111
- return bs(s, K, r, o, t, option_type)
110
+ flag: str = 'call') -> float:
111
+ return bs(s, K, r, o, t, flag)
112
112
 
113
113
  @staticmethod
114
114
  def delta(s: float, K: float, r: float, o: float, t: float,
115
- option_type: str = 'call') -> float:
116
- return delta(s, K, r, o, t, option_type)
115
+ flag: str = 'call') -> float:
116
+ return delta(s, K, r, o, t, flag)
117
117
 
118
118
  @staticmethod
119
119
  def gamma(s: float, K: float, r: float, o: float, t: float,
120
- option_type: str = 'call') -> float:
121
- return gamma(s, K, r, o, t, option_type)
120
+ flag: str = 'call') -> float:
121
+ return gamma(s, K, r, o, t, flag)
122
122
 
123
123
  @staticmethod
124
124
  def vega(s: float, K: float, r: float, o: float, t: float,
125
- option_type: str = 'call') -> float:
126
- return vega(s, K, r, o, t, option_type)
125
+ flag: str = 'call') -> float:
126
+ return vega(s, K, r, o, t, flag)
127
127
 
128
128
  @staticmethod
129
129
  def theta(s: float, K: float, r: float, o: float, t: float,
130
- option_type: str = 'call') -> float:
131
- return theta(s, K, r, o, t, option_type)
130
+ flag: str = 'call') -> float:
131
+ return theta(s, K, r, o, t, flag)
132
132
 
133
133
  @staticmethod
134
134
  def rho(s: float, K: float, r: float, o: float, t: float,
135
- option_type: str = 'call') -> float:
136
- return rho(s, K, r, o, t, option_type)
135
+ flag: str = 'call') -> float:
136
+ return rho(s, K, r, o, t, flag)
137
137
 
138
138
  @staticmethod
139
139
  def vanna(s: float, K: float, r: float, o: float, t: float,
140
- option_type: str = 'call') -> float:
141
- return vanna(s, K, r, o, t, option_type)
140
+ flag: str = 'call') -> float:
141
+ return vanna(s, K, r, o, t, flag)
142
142
 
143
143
  @staticmethod
144
144
  def volga(s: float, K: float, r: float, o: float, t: float,
145
- option_type: str = 'call') -> float:
146
- return volga(s, K, r, o, t, option_type)
145
+ flag: str = 'call') -> float:
146
+ return volga(s, K, r, o, t, flag)
147
147
 
148
148
  @staticmethod
149
149
  def charm(s: float, K: float, r: float, o: float, t: float,
150
- option_type: str = 'call') -> float:
151
- return charm(s, K, r, o, t, option_type)
150
+ flag: str = 'call') -> float:
151
+ return charm(s, K, r, o, t, flag)
152
152
 
153
153
  @staticmethod
154
154
  def greeks(s: float, K: float, r: float, o: float, t: float,
155
- option_type: str = 'call') -> Dict[str, float]:
156
- return greeks(s, K, r, o, t, option_type)
155
+ flag: str = 'call') -> Dict[str, float]:
156
+ return greeks(s, K, r, o, t, flag)
157
157
 
158
158
  @staticmethod
159
159
  def iv(option_price: float, s: float, K: float, r: float, t: float,
160
- option_type: str = 'call') -> float:
161
- return iv(option_price, s, K, r, t, option_type)
160
+ flag: str = 'call') -> float:
161
+ return iv(option_price, s, K, r, t, flag)
162
162
 
163
163
  # -------------------------------------------------------------------------
164
164
  # Model Fitting
@@ -235,6 +235,12 @@ class VolyClient:
235
235
  # Generate IV surface and domain
236
236
  iv_surface, x_surface = get_iv_surface(fit_results, domain_params, return_domain)
237
237
 
238
+ if option_chain:
239
+ # Create missing domains
240
+ option_chain['log_moneyness'] = np.log(option_chain['strikes'] / option_chain['spot_price'].iloc[0])
241
+ option_chain['moneyness'] = np.exp(option_chain['log_moneyness'])
242
+ option_chain['returns'] = df['moneyness'] - 1
243
+
238
244
  # Plot volatility smiles
239
245
  plots['smiles'] = plot_all_smiles(
240
246
  x_surface=x_surface,
voly/core/charts.py CHANGED
@@ -62,9 +62,9 @@ def plot_volatility_smile(x_array: np.ndarray,
62
62
 
63
63
  # Add market data if provided
64
64
  if option_chain is not None and maturity is not None:
65
- maturity_data = option_chain[option_chain['maturity_name'] == maturity]
65
+ maturity_data = option_chain[option_chain['maturity'] == maturity]
66
66
  if return_domain == 'delta':
67
- maturity_data = maturity_data[maturity_data['option_type'] == 'C']
67
+ maturity_data = maturity_data[maturity_data['flag'] == 'C']
68
68
 
69
69
  if not maturity_data.empty:
70
70
  # Add bid and ask IVs if available
@@ -149,10 +149,10 @@ def plot_raw_parameters(fit_results: pd.DataFrame) -> go.Figure:
149
149
  )
150
150
 
151
151
  # Get maturity names from index
152
- maturity_names = fit_results.index
152
+ maturities = fit_results.index
153
153
 
154
154
  # Create hover text with maturity info
155
- tick_labels = [f"{m}" for m in maturity_names]
155
+ tick_labels = [f"{m}" for m in maturities]
156
156
 
157
157
  # Plot each parameter
158
158
  for i, param in enumerate(param_names):
@@ -160,7 +160,7 @@ def plot_raw_parameters(fit_results: pd.DataFrame) -> go.Figure:
160
160
 
161
161
  fig.add_trace(
162
162
  go.Scatter(
163
- x=list(range(len(maturity_names))),
163
+ x=list(range(len(maturities))),
164
164
  y=fit_results[param],
165
165
  mode='lines+markers',
166
166
  name=param,
@@ -174,8 +174,8 @@ def plot_raw_parameters(fit_results: pd.DataFrame) -> go.Figure:
174
174
 
175
175
  # Set x-axis labels
176
176
  fig.update_xaxes(
177
- tickvals=list(range(len(maturity_names))),
178
- ticktext=maturity_names,
177
+ tickvals=list(range(len(maturities))),
178
+ ticktext=maturities,
179
179
  tickangle=45,
180
180
  row=row, col=col
181
181
  )
@@ -212,10 +212,10 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
212
212
  )
213
213
 
214
214
  # Get maturity names from index
215
- maturity_names = fit_results.index
215
+ maturities = fit_results.index
216
216
 
217
217
  # Create hover text with maturity info
218
- tick_labels = [f"{m}" for m in maturity_names]
218
+ tick_labels = [f"{m}" for m in maturities]
219
219
 
220
220
  # Plot each parameter
221
221
  for i, param in enumerate(param_names):
@@ -223,7 +223,7 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
223
223
 
224
224
  fig.add_trace(
225
225
  go.Scatter(
226
- x=list(range(len(maturity_names))),
226
+ x=list(range(len(maturities))),
227
227
  y=fit_results[param],
228
228
  mode='lines+markers',
229
229
  name=param,
@@ -237,8 +237,8 @@ def plot_jw_parameters(fit_results: pd.DataFrame) -> go.Figure:
237
237
 
238
238
  # Set x-axis labels
239
239
  fig.update_xaxes(
240
- tickvals=list(range(len(maturity_names))),
241
- ticktext=maturity_names,
240
+ tickvals=list(range(len(maturities))),
241
+ ticktext=maturities,
242
242
  tickangle=45,
243
243
  row=row, col=col
244
244
  )
@@ -279,11 +279,11 @@ def plot_fit_performance(fit_results: pd.DataFrame) -> go.Figure:
279
279
  )
280
280
 
281
281
  # Get maturity names from index and create x-axis indices
282
- maturity_names = fit_results.index
283
- x_indices = list(range(len(maturity_names)))
282
+ maturities = fit_results.index
283
+ x_indices = list(range(len(maturities)))
284
284
 
285
285
  # Create hover labels
286
- hover_labels = [f"{m}" for m in maturity_names]
286
+ hover_labels = [f"{m}" for m in maturities]
287
287
 
288
288
  # Plot each metric
289
289
  for metric, config in metrics.items():
@@ -309,7 +309,7 @@ def plot_fit_performance(fit_results: pd.DataFrame) -> go.Figure:
309
309
  for col in range(1, 3):
310
310
  fig.update_xaxes(
311
311
  tickvals=x_indices,
312
- ticktext=maturity_names,
312
+ ticktext=maturities,
313
313
  tickangle=45,
314
314
  row=row, col=col
315
315
  )
@@ -352,27 +352,27 @@ def plot_3d_surface(x_surface: Dict[str, np.ndarray],
352
352
  }
353
353
 
354
354
  # Get maturity names and sort by YTM
355
- maturity_names = list(iv_surface.keys())
355
+ maturities = list(iv_surface.keys())
356
356
  if fit_results is not None:
357
- maturity_values = [fit_results.loc[name, 't'] for name in maturity_names]
357
+ maturity_values = [fit_results.loc[name, 't'] for name in maturities]
358
358
  # Sort by maturity
359
359
  sorted_indices = np.argsort(maturity_values)
360
- maturity_names = [maturity_names[i] for i in sorted_indices]
360
+ maturities = [maturities[i] for i in sorted_indices]
361
361
  maturity_values = [maturity_values[i] for i in sorted_indices]
362
362
  else:
363
- maturity_values = list(range(len(maturity_names)))
363
+ maturity_values = list(range(len(maturities)))
364
364
 
365
365
  # Create a common x-grid for all maturities
366
366
  # Use 100 points between the min and max x-values across all maturities
367
- all_x = np.concatenate([x_surface[m] for m in maturity_names])
367
+ all_x = np.concatenate([x_surface[m] for m in maturities])
368
368
  x_min, x_max = np.min(all_x), np.max(all_x)
369
369
  x_grid = np.linspace(x_min, x_max, 400)
370
370
 
371
371
  # Create a matrix for the surface
372
- z_matrix = np.zeros((len(maturity_names), len(x_grid)))
372
+ z_matrix = np.zeros((len(maturities), len(x_grid)))
373
373
 
374
374
  # Fill the matrix with interpolated IV values
375
- for i, maturity in enumerate(maturity_names):
375
+ for i, maturity in enumerate(maturities):
376
376
  x_values = x_surface[maturity]
377
377
  iv_values = iv_surface[maturity] * 100 # Convert to percentage
378
378
 
voly/core/data.py CHANGED
@@ -181,7 +181,7 @@ async def get_deribit_data(currency: str = "BTC") -> pd.DataFrame:
181
181
 
182
182
  return pd.DataFrame(all_data)
183
183
 
184
-
184
+ '''
185
185
  @catch_exception
186
186
  def process_option_chain(df: pd.DataFrame, currency: str) -> pd.DataFrame:
187
187
  """
@@ -232,124 +232,100 @@ def process_option_chain(df: pd.DataFrame, currency: str) -> pd.DataFrame:
232
232
  logger.info(f"Processing complete!")
233
233
 
234
234
  return df
235
+ '''
235
236
 
236
237
 
237
238
  @catch_exception
238
- def process_order_book_depth(option_chain, max_depth=5):
239
+ def process_option_chain(df: pd.DataFrame, currency: str) -> pd.DataFrame:
239
240
  """
240
- Process the order book depth data to enhance option pricing and weighting.
241
+ Process raw option chain data into a standardized format.
241
242
 
242
- Args:
243
- option_chain: DataFrame containing option data with 'bids' and 'asks' columns
244
- max_depth: Maximum number of levels to consider from the order book
243
+ Parameters:
244
+ df (pd.DataFrame): Raw option chain data
245
+ currency (str): Currency code (e.g., 'BTC', 'ETH')
245
246
 
246
247
  Returns:
247
- Enhanced option_chain with additional columns for depth analysis
248
+ pd.DataFrame: Processed option chain data
248
249
  """
249
- # Create new columns for depth analysis
250
- option_chain['vwap_bid'] = float('nan')
251
- option_chain['vwap_ask'] = float('nan')
252
- option_chain['vwap_mid'] = float('nan')
253
- option_chain['depth_bid_qty'] = float('nan')
254
- option_chain['depth_ask_qty'] = float('nan')
255
- option_chain['vwap_bid_iv'] = float('nan')
256
- option_chain['vwap_ask_iv'] = float('nan')
257
- option_chain['vwap_mid_iv'] = float('nan')
258
- option_chain['depth_liquidity'] = float('nan')
259
-
260
- for idx, row in option_chain.iterrows():
261
- s = row['underlying_price']
262
- k = row['strikes']
263
- t = row['t']
264
- r = row['interest_rate'] if 'interest_rate' in row else 0.0
265
- option_type = 'C' if row['option_type'] == 'call' else 'P'
250
+ logger.info(f"Processing data for {currency}...")
251
+
252
+ # Apply extraction to create new columns
253
+ df['spot_price'] = df['index_price']
254
+ df['instrument'] = df['instrument_name']
255
+ splits = df['instrument'].str.split('-')
256
+ df['maturity'] = splits.str[1]
257
+ if currency == 'XRP':
258
+ df['strikes'] = splits.str[2].str.replace('d', '.', regex=False).astype(float)
259
+ else:
260
+ df['strikes'] = splits.str[2].astype(float)
261
+ df['flag'] = splits.str[3]
262
+
263
+ # Create maturity date at 8:00 AM UTC
264
+ df['expiry'] = pd.to_datetime(df['maturity'].apply(
265
+ lambda x: int(dt.datetime.strptime(x, "%d%b%y")
266
+ .replace(hour=8, minute=0, second=0, tzinfo=dt.timezone.utc)
267
+ .timestamp() * 1000)), unit='ms')
266
268
 
269
+ # Calculate implied volatility (convert from percentage)
270
+ df['mark_iv'] = df['mark_iv'] / 100
271
+ df['bid_iv'] = df['bid_iv'].replace({0: np.nan}) / 100
272
+ df['ask_iv'] = df['ask_iv'].replace({0: np.nan}) / 100
273
+
274
+ df['bid_price'] = df['best_bid_price']
275
+ df['ask_price'] = df['best_ask_price']
276
+ df['bid_amount'] = df['best_bid_amount']
277
+ df['ask_amount'] = df['best_ask_amount']
278
+
279
+ for idx, row in df.iterrows():
267
280
  # Process bid side
268
281
  if 'bids' in row and isinstance(row['bids'], list) and len(row['bids']) > 0:
269
- # Clean up the bid data
270
- clean_bids = []
271
- for bid in row['bids'][:max_depth]: # Limit to max_depth levels
282
+ clean_bids = [] # Clean up the bid data
283
+ for bid in row['bids']:
272
284
  if len(bid) >= 3:
273
285
  # Extract price and quantity, removing 'new' if present
274
286
  price = float(bid[1]) if bid[0] == 'new' else float(bid[0])
275
287
  qty = float(bid[2]) if bid[0] == 'new' else float(bid[1])
276
288
  clean_bids.append((price, qty))
277
-
278
289
  if clean_bids:
279
- # Calculate volume-weighted average price
280
- total_qty = sum(qty for _, qty in clean_bids)
281
- vwap_bid = sum(price * qty for price, qty in clean_bids) / total_qty if total_qty > 0 else float('nan')
282
-
283
- # Calculate IV for VWAP
284
- try:
285
- vwap_bid_iv = voly.iv(vwap_bid * s, s, k, r, t, option_type)
286
- except:
287
- vwap_bid_iv = float('nan')
288
-
289
- option_chain.at[idx, 'vwap_bid'] = vwap_bid
290
- option_chain.at[idx, 'depth_bid_qty'] = total_qty
291
- option_chain.at[idx, 'vwap_bid_iv'] = vwap_bid_iv
290
+ df.at[idx, 'bids'] = clean_bids
292
291
 
293
292
  # Process ask side
294
293
  if 'asks' in row and isinstance(row['asks'], list) and len(row['asks']) > 0:
295
- # Clean up the ask data
296
- clean_asks = []
297
- for ask in row['asks'][:max_depth]: # Limit to max_depth levels
294
+ clean_asks = [] # Clean up the ask data
295
+ for ask in row['asks']:
298
296
  if len(ask) >= 3:
299
297
  # Extract price and quantity, removing 'new' if present
300
298
  price = float(ask[1]) if ask[0] == 'new' else float(ask[0])
301
299
  qty = float(ask[2]) if ask[0] == 'new' else float(ask[1])
302
300
  clean_asks.append((price, qty))
303
-
304
301
  if clean_asks:
305
- # Calculate volume-weighted average price
306
- total_qty = sum(qty for _, qty in clean_asks)
307
- vwap_ask = sum(price * qty for price, qty in clean_asks) / total_qty if total_qty > 0 else float('nan')
308
-
309
- # Calculate IV for VWAP
310
- try:
311
- vwap_ask_iv = voly.iv(vwap_ask * s, s, k, r, t, option_type)
312
- except:
313
- vwap_ask_iv = float('nan')
314
-
315
- option_chain.at[idx, 'vwap_ask'] = vwap_ask
316
- option_chain.at[idx, 'depth_ask_qty'] = total_qty
317
- option_chain.at[idx, 'vwap_ask_iv'] = vwap_ask_iv
318
-
319
- # Calculate mid VWAP if both bid and ask are available
320
- if not np.isnan(option_chain.at[idx, 'vwap_bid']) and not np.isnan(option_chain.at[idx, 'vwap_ask']):
321
- vwap_mid = (option_chain.at[idx, 'vwap_bid'] + option_chain.at[idx, 'vwap_ask']) / 2
302
+ df.at[idx, 'asks'] = clean_asks
322
303
 
323
- # Calculate IV for mid VWAP
324
- try:
325
- vwap_mid_iv = voly.iv(vwap_mid * s, s, k, r, t, option_type)
326
- except:
327
- vwap_mid_iv = float('nan')
304
+ df['bid_depth'] = df['bids']
305
+ df['ask_depth'] = df['asks']
306
+ df['open_interest'] = df['open_interest'] * df['spot_price']
307
+ df['volume'] = df['volume_usd']
328
308
 
329
- option_chain.at[idx, 'vwap_mid'] = vwap_mid
330
- option_chain.at[idx, 'vwap_mid_iv'] = vwap_mid_iv
309
+ df = df[['currency', 'spot_price',
310
+ 'timestamp', 'instrument', 'maturity', 'strikes', 'flag', 'expiry',
311
+ 'mark_iv', 'bid_iv', 'ask_iv', 'mark_price', 'bid_price', 'ask_price', 'bid_amount', 'ask_amount', 'bid_depth', 'ask_depth',
312
+ 'delta', 'gamma', 'vega', 'theta', 'rho',
313
+ 'interest_rate', 'open_interest', 'volume']]
331
314
 
332
- # Calculate depth liquidity (sum of bid and ask quantities)
333
- bid_qty = option_chain.at[idx, 'depth_bid_qty'] if not np.isnan(option_chain.at[idx, 'depth_bid_qty']) else 0
334
- ask_qty = option_chain.at[idx, 'depth_ask_qty'] if not np.isnan(option_chain.at[idx, 'depth_ask_qty']) else 0
335
- option_chain.at[idx, 'depth_liquidity'] = bid_qty + ask_qty
336
-
337
- option_chain = option_chain.replace(float('inf'), float('nan'))
315
+ logger.info(f"Processing complete!")
338
316
 
339
- return option_chain
317
+ return df
340
318
 
341
319
 
342
320
  @catch_exception
343
321
  async def fetch_option_chain(exchange: str = 'deribit',
344
- currency: str = 'BTC',
345
- depth: bool = False) -> pd.DataFrame:
322
+ currency: str = 'BTC') -> pd.DataFrame:
346
323
  """
347
324
  Fetch option chain data from the specified exchange.
348
325
 
349
326
  Parameters:
350
327
  exchange (str): Exchange to fetch data from (currently only 'deribit' is supported)
351
328
  currency (str): Currency to fetch options for (e.g., 'BTC', 'ETH')
352
- depth (bool): Whether to include full order book depth. Else, just top of book.
353
329
 
354
330
  Returns:
355
331
  pd.DataFrame: Processed option chain data
@@ -370,8 +346,4 @@ async def fetch_option_chain(exchange: str = 'deribit',
370
346
  # Process data
371
347
  processed_data = process_option_chain(raw_data, currency)
372
348
 
373
- # Remove order book depth if not needed
374
- if not depth and 'bids' in processed_data.columns and 'asks' in processed_data.columns:
375
- processed_data = processed_data.drop(columns=['bids', 'asks'])
376
-
377
349
  return processed_data
voly/core/fit.py CHANGED
@@ -26,16 +26,16 @@ class SVICalibrator:
26
26
  def __init__(self, option_chain, currency, num_points=2000):
27
27
  self.option_chain = option_chain
28
28
  self.currency = currency
29
- self.s = option_chain['index_price'].iloc[0]
29
+ self.s = option_chain['spot_price'].iloc[0]
30
30
  self.r = option_chain['interest_rate'].iloc[0] if 'interest_rate' in option_chain else 0.0
31
- self.groups = option_chain.groupby('maturity_date')
31
+ self.groups = option_chain.groupby('expiry')
32
32
  self.params_dict = {}
33
33
  self.results_data = {}
34
34
  self.num_points = num_points
35
35
 
36
36
  # Initialize results data template
37
37
  self.field_names = [
38
- 's', 't', 'r', 'maturity_date', 'maturity_name', 'a', 'b', 'm', 'rho', 'sigma',
38
+ 's', 't', 'r', 'expiry', 'maturity', 'a', 'b', 'm', 'rho', 'sigma',
39
39
  'nu', 'psi', 'p', 'c', 'nu_tilde', 'log_min_strike', 'usd_min_strike',
40
40
  'fit_success', 'butterfly_arbitrage_free', 'calendar_arbitrage_free',
41
41
  'rmse', 'mae', 'r2', 'max_error', 'loss', 'n_points'
@@ -45,14 +45,14 @@ class SVICalibrator:
45
45
  for field in self.field_names:
46
46
  self.results_data[field] = []
47
47
 
48
- def failed_calibration(self, maturity, maturity_name, t, n_points):
48
+ def failed_calibration(self, expiry, maturity, t, n_points):
49
49
  """Create an empty result for failed calibration"""
50
50
  return {
51
51
  's': float(self.s),
52
52
  't': float(t),
53
53
  'r': float(self.r),
54
- 'maturity_date': maturity,
55
- 'maturity_name': maturity_name,
54
+ 'expiry': expiry,
55
+ 'maturity': maturity,
56
56
  'fit_success': False,
57
57
  'calendar_arbitrage_free': True, # Updated later
58
58
  'loss': float(np.inf),
@@ -67,7 +67,7 @@ class SVICalibrator:
67
67
  def filter_market_data(self, group):
68
68
  """Filter and prepare market data"""
69
69
  # Filter for call options only
70
- group = group[group['option_type'] == 'C']
70
+ group = group[group['flag'] == 'C']
71
71
 
72
72
  # Handle duplicated IVs by keeping the row closest to log_moneyness=0
73
73
  duplicated_iv = group[group.duplicated('mark_iv', keep=False)]
@@ -81,7 +81,7 @@ class SVICalibrator:
81
81
  group = pd.concat([unique_iv, cleaned_dupes])
82
82
 
83
83
  # Extract basic data
84
- maturity_name = group['maturity_name'].iloc[0]
84
+ maturity = group['maturity'].iloc[0]
85
85
  t = group['t'].iloc[0]
86
86
  K = group['strikes'].values
87
87
  iv = group['mark_iv'].values
@@ -93,7 +93,7 @@ class SVICalibrator:
93
93
  mask = ~np.isnan(w) & ~np.isnan(vega) & ~np.isnan(k) & (iv >= 0)
94
94
  k, w, vega, iv, K = k[mask], w[mask], vega[mask], iv[mask], K[mask]
95
95
 
96
- return maturity_name, t, k, w, vega, iv, K
96
+ return maturity, t, k, w, vega, iv, K
97
97
 
98
98
  def calculate_model_stats(self, params, t, k, iv):
99
99
  """Calculate all model statistics from parameters"""
@@ -139,14 +139,14 @@ class SVICalibrator:
139
139
  'max_error': float(max_error)
140
140
  }
141
141
 
142
- def process_maturity(self, maturity, group):
142
+ def process_maturity(self, expiry, group):
143
143
  """Process single maturity for SVI calibration"""
144
144
  # Clean and prepare market data
145
- maturity_name, t, k, w, vega, iv, K = self.filter_market_data(group)
145
+ maturity, t, k, w, vega, iv, K = self.filter_market_data(group)
146
146
 
147
147
  # Not enough data points for fitting
148
148
  if len(k) <= 5:
149
- result = self.failed_calibration(maturity, maturity_name, t, len(k))
149
+ result = self.failed_calibration(expiry, maturity, t, len(k))
150
150
  logger.error(f'\033[31mFAILED\033[0m for {maturity} (insufficient data points)')
151
151
  self.update_results(result)
152
152
  return maturity
@@ -156,7 +156,7 @@ class SVICalibrator:
156
156
 
157
157
  # If fitting failed
158
158
  if np.isnan(params[0]):
159
- result = self.failed_calibration(maturity, maturity_name, t, len(k))
159
+ result = self.failed_calibration(expiry, maturity, t, len(k))
160
160
  logger.error(f'\033[31mFAILED\033[0m for {maturity}')
161
161
  self.update_results(result)
162
162
  return maturity
@@ -172,8 +172,8 @@ class SVICalibrator:
172
172
  's': float(self.s),
173
173
  't': float(t),
174
174
  'r': float(self.r),
175
- 'maturity_date': maturity,
176
- 'maturity_name': maturity_name,
175
+ 'expiry': expiry,
176
+ 'maturity': maturity,
177
177
  'fit_success': True,
178
178
  'calendar_arbitrage_free': True, # Updated later
179
179
  'loss': float(loss),
@@ -201,16 +201,16 @@ class SVICalibrator:
201
201
  # Process all maturities in parallel
202
202
  with ThreadPoolExecutor() as executor:
203
203
  futures = [
204
- executor.submit(self.process_maturity, maturity, group)
205
- for maturity, group in self.groups
204
+ executor.submit(self.process_maturity, expiry, group)
205
+ for expiry, group in self.groups
206
206
  ]
207
207
  for future in futures:
208
208
  future.result()
209
209
 
210
210
  # Create results DataFrame and mapping for updates
211
- fit_results = pd.DataFrame(self.results_data, index=self.results_data['maturity_name'])
211
+ fit_results = pd.DataFrame(self.results_data, index=self.results_data['maturity'])
212
212
  fit_results = fit_results.sort_values(by='t')
213
- maturity_name_dict = {row['maturity_date']: idx for idx, row in fit_results.iterrows()}
213
+ maturity_dict = {row['expiry']: idx for idx, row in fit_results.iterrows()}
214
214
 
215
215
  # Check for calendar arbitrage
216
216
  sorted_maturities = sorted(self.params_dict.keys(), key=lambda x: self.params_dict[x][0])
@@ -220,20 +220,20 @@ class SVICalibrator:
220
220
 
221
221
  # Update calendar arbitrage status
222
222
  for mat in sorted_maturities:
223
- mat_name = maturity_name_dict[mat]
223
+ mat_name = maturity_dict[mat]
224
224
  fit_results.at[mat_name, 'calendar_arbitrage_free'] = calendar_arbitrage_free
225
225
 
226
226
  # Correct calendar arbitrage violations
227
- self.correct_calendar_arbitrage(sorted_maturities, fit_results, maturity_name_dict)
227
+ self.correct_calendar_arbitrage(sorted_maturities, fit_results, maturity_dict)
228
228
 
229
229
  # Clean up results and report execution time
230
- fit_results = fit_results.drop(columns='maturity_name')
230
+ fit_results = fit_results.drop(columns='maturity')
231
231
  end_time = time.time()
232
232
  logger.info(f"Total model execution time: {end_time - start_time:.4f} seconds")
233
233
 
234
234
  return fit_results
235
235
 
236
- def correct_calendar_arbitrage(self, sorted_maturities, fit_results, maturity_name_dict):
236
+ def correct_calendar_arbitrage(self, sorted_maturities, fit_results, maturity_dict):
237
237
  """Handle calendar arbitrage corrections"""
238
238
  for i in range(1, len(sorted_maturities)):
239
239
  mat2 = sorted_maturities[i]
@@ -259,7 +259,7 @@ class SVICalibrator:
259
259
 
260
260
  # Calculate new stats and update results
261
261
  stats = self.calculate_model_stats(new_params, t2, k, iv)
262
- mat2_name = maturity_name_dict[mat2]
262
+ mat2_name = maturity_dict[mat2]
263
263
 
264
264
  # Update all stats at once
265
265
  for key, value in stats.items():
@@ -273,7 +273,7 @@ class SVICalibrator:
273
273
 
274
274
  # Update final status
275
275
  for mat in sorted_maturities:
276
- mat_name = maturity_name_dict[mat]
276
+ mat_name = maturity_dict[mat]
277
277
  fit_results.at[mat_name, 'calendar_arbitrage_free'] = calendar_arbitrage_free
278
278
 
279
279
 
@@ -287,7 +287,7 @@ def fit_model(option_chain: pd.DataFrame, num_points: int = 2000) -> pd.DataFram
287
287
  - num_points: Number of points for k_grid and plotting
288
288
 
289
289
  Returns:
290
- - fit_results: DataFrame with all fit results and performance metrics as columns, maturity_names as index
290
+ - fit_results: DataFrame with all fit results and performance metrics as columns, maturities as index
291
291
  """
292
292
  currency = option_chain['currency'].iloc[0] if 'currency' in option_chain.columns else 'Unknown'
293
293
 
voly/core/interpolate.py CHANGED
@@ -77,12 +77,12 @@ def interpolate_model(fit_results: pd.DataFrame,
77
77
 
78
78
  # Calculate maturity dates
79
79
  now = dt.datetime.now()
80
- maturity_dates = []
80
+ expiries = []
81
81
  for days in target_days:
82
- maturity_date = now + dt.timedelta(days=days)
83
- maturity_dates.append(maturity_date)
82
+ expiry = now + dt.timedelta(days=days)
83
+ expiries.append(expiry)
84
84
 
85
- interpolated_df['maturity_date'] = maturity_dates
85
+ interpolated_df['expiry'] = expiries
86
86
 
87
87
  # Sort fit_results by ytm
88
88
  sorted_fit_results = fit_results.iloc[sorted_indices]
@@ -118,7 +118,7 @@ def interpolate_model(fit_results: pd.DataFrame,
118
118
  interpolated_df[param] = f(target_years)
119
119
 
120
120
  # Ensure consistent ordering of columns with expected structure
121
- expected_columns = ['s', 't', 'r', 'maturity_date', 'a', 'b', 'm', 'rho', 'sigma',
121
+ expected_columns = ['s', 't', 'r', 'expiry', 'a', 'b', 'm', 'rho', 'sigma',
122
122
  'nu', 'psi', 'p', 'c', 'nu_tilde']
123
123
 
124
124
  # Create final column order based on available columns
voly/formulas.py CHANGED
@@ -18,7 +18,7 @@ def vectorize_inputs(func):
18
18
  For invalid inputs (K <= 0, o <= 0, or t <= 0), returns np.nan instead of trying to compute.
19
19
  """
20
20
 
21
- def wrapper(s, K, r, o, t, option_type='call'):
21
+ def wrapper(s, K, r, o, t, flag='call'):
22
22
  # Check if inputs are scalar
23
23
  K_scalar = np.isscalar(K)
24
24
  o_scalar = np.isscalar(o)
@@ -34,7 +34,7 @@ def vectorize_inputs(func):
34
34
 
35
35
  # If all inputs are scalar, use the original function directly
36
36
  if K_scalar and o_scalar and t_scalar:
37
- return func(s, K, r, o, t, option_type)
37
+ return func(s, K, r, o, t, flag)
38
38
 
39
39
  # For arrays, we need to apply the function element-wise
40
40
  # Instead of using np.vectorize directly, we'll create a custom vectorized function
@@ -45,7 +45,7 @@ def vectorize_inputs(func):
45
45
  return np.nan # Return NaN for invalid inputs
46
46
  else:
47
47
  # Only call the function if inputs are valid
48
- return func(s, K_val, r, o_val, t_val, option_type)
48
+ return func(s, K_val, r, o_val, t_val, flag)
49
49
 
50
50
  # Now use numpy's vectorize on our safe function
51
51
  vectorized_func = np.vectorize(safe_vectorized_func)
@@ -58,24 +58,24 @@ def vectorize_inputs(func):
58
58
 
59
59
  @catch_exception
60
60
  @vectorize_inputs
61
- def d1(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
62
- # option_type is ignored in this function but included for compatibility
61
+ def d1(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
62
+ # flag is ignored in this function but included for compatibility
63
63
  return (np.log(s / K) + (r + o ** 2 / 2) * t) / (o * np.sqrt(t))
64
64
 
65
65
 
66
66
  @catch_exception
67
67
  @vectorize_inputs
68
- def d2(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
69
- # option_type is ignored in this function but included for compatibility
70
- return d1(s, K, r, o, t, option_type) - o * np.sqrt(t)
68
+ def d2(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
69
+ # flag is ignored in this function but included for compatibility
70
+ return d1(s, K, r, o, t, flag) - o * np.sqrt(t)
71
71
 
72
72
 
73
73
  @catch_exception
74
74
  @vectorize_inputs
75
- def bs(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
75
+ def bs(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
76
76
  if o <= 0 or t <= 0:
77
77
  # Intrinsic value at expiry
78
- if option_type.lower() in ["call", "c"]:
78
+ if flag.lower() in ["call", "c"]:
79
79
  return max(0, s - K)
80
80
  else:
81
81
  return max(0, K - s)
@@ -83,7 +83,7 @@ def bs(s: float, K: float, r: float, o: float, t: float, option_type: str = 'cal
83
83
  d1_val = d1(s, K, r, o, t)
84
84
  d2_val = d2(s, K, r, o, t)
85
85
 
86
- if option_type.lower() in ["call", "c"]:
86
+ if flag.lower() in ["call", "c"]:
87
87
  return s * norm.cdf(d1_val) - K * np.exp(-r * t) * norm.cdf(d2_val)
88
88
  else: # put
89
89
  return K * np.exp(-r * t) * norm.cdf(-d2_val) - s * norm.cdf(-d1_val)
@@ -91,17 +91,17 @@ def bs(s: float, K: float, r: float, o: float, t: float, option_type: str = 'cal
91
91
 
92
92
  @catch_exception
93
93
  @vectorize_inputs
94
- def delta(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
94
+ def delta(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
95
95
  if o <= 0 or t <= 0:
96
96
  # At expiry, delta is either 0 or 1 for call, 0 or -1 for put
97
- if option_type.lower() in ["call", "c"]:
97
+ if flag.lower() in ["call", "c"]:
98
98
  return 1.0 if s > K else 0.0
99
99
  else:
100
100
  return -1.0 if s < K else 0.0
101
101
 
102
102
  d1_val = d1(s, K, r, o, t)
103
103
 
104
- if option_type.lower() in ["call", "c"]:
104
+ if flag.lower() in ["call", "c"]:
105
105
  return norm.cdf(d1_val)
106
106
  else: # put
107
107
  return norm.cdf(d1_val) - 1.0
@@ -109,29 +109,29 @@ def delta(s: float, K: float, r: float, o: float, t: float, option_type: str = '
109
109
 
110
110
  @catch_exception
111
111
  @vectorize_inputs
112
- def gamma(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
113
- d1_val = d1(s, K, r, o, t, option_type)
112
+ def gamma(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
113
+ d1_val = d1(s, K, r, o, t, flag)
114
114
  return norm.pdf(d1_val) / (s * o * np.sqrt(t))
115
115
 
116
116
 
117
117
  @catch_exception
118
118
  @vectorize_inputs
119
- def vega(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
120
- d1_val = d1(s, K, r, o, t, option_type)
119
+ def vega(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
120
+ d1_val = d1(s, K, r, o, t, flag)
121
121
  return s * norm.pdf(d1_val) * np.sqrt(t) / 100 # Divided by 100 for 1% change
122
122
 
123
123
 
124
124
  @catch_exception
125
125
  @vectorize_inputs
126
- def theta(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
127
- d1_val = d1(s, K, r, o, t, option_type)
128
- d2_val = d2(s, K, r, o, t, option_type)
126
+ def theta(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
127
+ d1_val = d1(s, K, r, o, t, flag)
128
+ d2_val = d2(s, K, r, o, t, flag)
129
129
 
130
130
  # First part of theta (same for both call and put)
131
131
  theta_part1 = -s * norm.pdf(d1_val) * o / (2 * np.sqrt(t))
132
132
 
133
133
  # Second part depends on option type
134
- if option_type.lower() in ["call", "c"]:
134
+ if flag.lower() in ["call", "c"]:
135
135
  theta_part2 = -r * K * np.exp(-r * t) * norm.cdf(d2_val)
136
136
  else: # put
137
137
  theta_part2 = r * K * np.exp(-r * t) * norm.cdf(-d2_val)
@@ -142,10 +142,10 @@ def theta(s: float, K: float, r: float, o: float, t: float, option_type: str = '
142
142
 
143
143
  @catch_exception
144
144
  @vectorize_inputs
145
- def rho(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
146
- d2_val = d2(s, K, r, o, t, option_type)
145
+ def rho(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
146
+ d2_val = d2(s, K, r, o, t, flag)
147
147
 
148
- if option_type.lower() in ["call", "c"]:
148
+ if flag.lower() in ["call", "c"]:
149
149
  return K * t * np.exp(-r * t) * norm.cdf(d2_val) / 100
150
150
  else: # put
151
151
  return -K * t * np.exp(-r * t) * norm.cdf(-d2_val) / 100
@@ -153,33 +153,33 @@ def rho(s: float, K: float, r: float, o: float, t: float, option_type: str = 'ca
153
153
 
154
154
  @catch_exception
155
155
  @vectorize_inputs
156
- def vanna(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
157
- d1_val = d1(s, K, r, o, t, option_type)
158
- d2_val = d2(s, K, r, o, t, option_type)
156
+ def vanna(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
157
+ d1_val = d1(s, K, r, o, t, flag)
158
+ d2_val = d2(s, K, r, o, t, flag)
159
159
 
160
160
  return -norm.pdf(d1_val) * d2_val / o
161
161
 
162
162
 
163
163
  @catch_exception
164
164
  @vectorize_inputs
165
- def volga(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
166
- d1_val = d1(s, K, r, o, t, option_type)
167
- d2_val = d2(s, K, r, o, t, option_type)
165
+ def volga(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
166
+ d1_val = d1(s, K, r, o, t, flag)
167
+ d2_val = d2(s, K, r, o, t, flag)
168
168
 
169
169
  return s * norm.pdf(d1_val) * np.sqrt(t) * d1_val * d2_val / o
170
170
 
171
171
 
172
172
  @catch_exception
173
173
  @vectorize_inputs
174
- def charm(s: float, K: float, r: float, o: float, t: float, option_type: str = 'call') -> float:
175
- d1_val = d1(s, K, r, o, t, option_type)
176
- d2_val = d2(s, K, r, o, t, option_type)
174
+ def charm(s: float, K: float, r: float, o: float, t: float, flag: str = 'call') -> float:
175
+ d1_val = d1(s, K, r, o, t, flag)
176
+ d2_val = d2(s, K, r, o, t, flag)
177
177
 
178
178
  # First term is the same for calls and puts
179
179
  term1 = -norm.pdf(d1_val) * d1_val / (2 * t)
180
180
 
181
181
  # Second term depends on option type
182
- if option_type.lower() in ["call", "c"]:
182
+ if flag.lower() in ["call", "c"]:
183
183
  term2 = -r * np.exp(-r * t) * norm.cdf(d2_val)
184
184
  else: # put
185
185
  term2 = r * np.exp(-r * t) * norm.cdf(-d2_val)
@@ -191,24 +191,24 @@ def charm(s: float, K: float, r: float, o: float, t: float, option_type: str = '
191
191
  @catch_exception
192
192
  @vectorize_inputs
193
193
  def greeks(s: float, K: float, r: float, o: float, t: float,
194
- option_type: str = 'call') -> Dict[str, float]:
194
+ flag: str = 'call') -> Dict[str, float]:
195
195
  return {
196
- 'price': bs(s, K, r, o, t, option_type),
197
- 'delta': delta(s, K, r, o, t, option_type),
198
- 'gamma': gamma(s, K, r, o, t, option_type),
199
- 'vega': vega(s, K, r, o, t, option_type),
200
- 'theta': theta(s, K, r, o, t, option_type),
201
- 'rho': rho(s, K, r, o, t, option_type),
202
- 'vanna': vanna(s, K, r, o, t, option_type),
203
- 'volga': volga(s, K, r, o, t, option_type),
204
- 'charm': charm(s, K, r, o, t, option_type)
196
+ 'price': bs(s, K, r, o, t, flag),
197
+ 'delta': delta(s, K, r, o, t, flag),
198
+ 'gamma': gamma(s, K, r, o, t, flag),
199
+ 'vega': vega(s, K, r, o, t, flag),
200
+ 'theta': theta(s, K, r, o, t, flag),
201
+ 'rho': rho(s, K, r, o, t, flag),
202
+ 'vanna': vanna(s, K, r, o, t, flag),
203
+ 'volga': volga(s, K, r, o, t, flag),
204
+ 'charm': charm(s, K, r, o, t, flag)
205
205
  }
206
206
 
207
207
 
208
208
  @catch_exception
209
209
  @vectorize_inputs
210
210
  def iv(option_price: float, s: float, K: float, r: float, t: float,
211
- option_type: str = 'call') -> float:
211
+ flag: str = 'call') -> float:
212
212
  """
213
213
  Calculate implied volatility using py_volib for vectorized computation.
214
214
 
@@ -218,14 +218,14 @@ def iv(option_price: float, s: float, K: float, r: float, t: float,
218
218
  - K: Strike price
219
219
  - r: Risk-free rate
220
220
  - t: Time to expiry in years
221
- - option_type: 'call' or 'put'
221
+ - flag: 'call' or 'put'
222
222
 
223
223
  Returns:
224
224
  - Implied volatility
225
225
  """
226
226
 
227
227
  # Check if option price is within theoretical bounds
228
- if option_type.lower() in ["call", "c"]:
228
+ if flag.lower() in ["call", "c"]:
229
229
  intrinsic = max(0, s - K * np.exp(-r * t))
230
230
  if option_price < intrinsic:
231
231
  return np.nan # Price below intrinsic value
@@ -238,7 +238,7 @@ def iv(option_price: float, s: float, K: float, r: float, t: float,
238
238
  if option_price >= K:
239
239
  return np.inf # Price exceeds strike
240
240
 
241
- flag = 'c' if option_type.lower() in ["call", "c"] else 'p'
241
+ flag = 'c' if flag.lower() in ["call", "c"] else 'p'
242
242
 
243
243
  iv_value = implied_volatility(
244
244
  price=option_price,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voly
3
- Version: 0.0.226
3
+ Version: 0.0.228
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,20 @@
1
+ voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
+ voly/client.py,sha256=H4BY5n1isHOqMYX-Koe8-WKBKE6zxbdDYopDF58BPBI,14533
3
+ voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
+ voly/formulas.py,sha256=Jx2QSTkN3S_X1YWYXN3T0gBcxMKB_VLsqgDPg32ApmM,10833
5
+ voly/models.py,sha256=CGJQr13Uie7iwtx2hjViN9lMXeRN_uOqzp4u8NPaTlA,9282
6
+ voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
7
+ voly/core/charts.py,sha256=6MSU0z01fPOVSssodxdnFqchzDfupSmXq_e71WwDmVQ,12635
8
+ voly/core/data.py,sha256=4E-zF2wJwumbWcrGVOS59ebApRKnxyyUXCTTriUk45Y,12389
9
+ voly/core/fit.py,sha256=R3aDgCjvZ30PujoE9pgnGQXPQOTTMbk7_-RQ0ZMx5ig,13918
10
+ voly/core/hd.py,sha256=dSv197RmSWFWbRRdoBzMrD_poT7ZiJ8hdD_wKE-Li_M,13559
11
+ voly/core/interpolate.py,sha256=GAkrqaar7A0D6UMipXQUp4vSHRfb44TmCG5Xiv9elXg,5176
12
+ voly/core/rnd.py,sha256=wiZ5OIjPDf1Th5_sQ9CZG5JgAo3EL8f63T_Rj1_VP-0,13214
13
+ voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
14
+ voly/utils/density.py,sha256=ONpRli-IaJDgOZ2sb27HHFc9_tkkGSATKl94JODd86A,5879
15
+ voly/utils/logger.py,sha256=4-_2bVJmq17Q0d7Rd2mPg1AeR8gxv6EPvcmBDMFWcSM,1744
16
+ voly-0.0.228.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
17
+ voly-0.0.228.dist-info/METADATA,sha256=_21vNdPGISj2HC7IKCjC47LHjMq2F1qPbKOEF9oFbb0,4115
18
+ voly-0.0.228.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
19
+ voly-0.0.228.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
20
+ voly-0.0.228.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
2
- voly/client.py,sha256=F5jRdmEfxoE2RGHryCntRFrKLlyS7W974jEtEcBz8Co,14410
3
- voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
4
- voly/formulas.py,sha256=Jn9hBoIx6PGv9k4lm8PeGM4lxFJkrLau8LpnXatdQPM,11176
5
- voly/models.py,sha256=CGJQr13Uie7iwtx2hjViN9lMXeRN_uOqzp4u8NPaTlA,9282
6
- voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
7
- voly/core/charts.py,sha256=2S-BfCo30aj1_xlNLqF-za5rQWxF_mWKIdtdOe5bgbw,12735
8
- voly/core/data.py,sha256=SNF87C7-r-1IbKwf7rAhXkJ6X305yo7fCDJDdkwz3NM,14103
9
- voly/core/fit.py,sha256=bVyx7qMgFFpTUjgoCUs58ppmeNN2CORnqPKbGUpV9xw,14081
10
- voly/core/hd.py,sha256=dSv197RmSWFWbRRdoBzMrD_poT7ZiJ8hdD_wKE-Li_M,13559
11
- voly/core/interpolate.py,sha256=-cNChFpuLnCSMOmfW2ldXxePgQXi-pxcjJvF2yImD1w,5222
12
- voly/core/rnd.py,sha256=wiZ5OIjPDf1Th5_sQ9CZG5JgAo3EL8f63T_Rj1_VP-0,13214
13
- voly/utils/__init__.py,sha256=E05mWatyC-PDOsCxQV1p5Xi1IgpOomxrNURyCx_gB-w,200
14
- voly/utils/density.py,sha256=ONpRli-IaJDgOZ2sb27HHFc9_tkkGSATKl94JODd86A,5879
15
- voly/utils/logger.py,sha256=4-_2bVJmq17Q0d7Rd2mPg1AeR8gxv6EPvcmBDMFWcSM,1744
16
- voly-0.0.226.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
17
- voly-0.0.226.dist-info/METADATA,sha256=rLI85wsZs9RYkI2B-4-87qXHiKoarN-PEFrSfD6OdJE,4115
18
- voly-0.0.226.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
19
- voly-0.0.226.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
20
- voly-0.0.226.dist-info/RECORD,,
File without changes