voly 0.0.85__py3-none-any.whl → 0.0.87__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
@@ -20,13 +20,13 @@ warnings.filterwarnings("ignore")
20
20
 
21
21
 
22
22
  @catch_exception
23
- def calculate_residuals(params: List[float], ytm: float, option_chain: pd.DataFrame,
23
+ def calculate_residuals(params: List[float], t: float, option_chain: pd.DataFrame,
24
24
  model: Any = SVIModel) -> np.ndarray:
25
25
  """Calculate residuals between market and model implied volatilities."""
26
- maturity_data = option_chain[option_chain['ytm'] == ytm]
26
+ maturity_data = option_chain[option_chain['t'] == t]
27
27
  w = np.array([model.svi(x, *params) for x in maturity_data['log_moneyness']])
28
28
  iv_actual = maturity_data['mark_iv'].values
29
- return iv_actual - np.sqrt(w / ytm)
29
+ return iv_actual - np.sqrt(w / t)
30
30
 
31
31
 
32
32
  @catch_exception
@@ -57,22 +57,21 @@ def fit_model(option_chain: pd.DataFrame,
57
57
  column_dtypes = {
58
58
  's': float,
59
59
  'u': float,
60
+ 't': float,
61
+ 'r': float,
62
+ 'oi': float,
63
+ 'volume': float,
60
64
  'maturity_date': 'datetime64[ns]',
61
- 'dtm': float,
62
- 'ytm': float,
63
65
  'a': float,
64
66
  'b': float,
67
+ 'sigma': float,
65
68
  'rho': float,
66
69
  'm': float,
67
- 'sigma': float,
68
70
  'nu': float,
69
71
  'psi': float,
70
72
  'p': float,
71
73
  'c': float,
72
74
  'nu_tilde': float,
73
- 'oi': float,
74
- 'volume': float,
75
- 'r': float,
76
75
  'fit_success': bool,
77
76
  'cost': float,
78
77
  'optimality': float,
@@ -84,8 +83,8 @@ def fit_model(option_chain: pd.DataFrame,
84
83
  }
85
84
 
86
85
  # Get unique maturities and sort them
87
- unique_ytms = sorted(option_chain['ytm'].unique())
88
- maturity_names = [option_chain[option_chain['ytm'] == ytm]['maturity_name'].iloc[0] for ytm in unique_ytms]
86
+ unique_ts = sorted(option_chain['t'].unique())
87
+ maturity_names = [option_chain[option_chain['t'] == t]['maturity_name'].iloc[0] for t in unique_ts]
89
88
 
90
89
  # Store results in a dictionary first
91
90
  results_data = {col: [] for col in column_dtypes.keys()}
@@ -95,11 +94,10 @@ def fit_model(option_chain: pd.DataFrame,
95
94
 
96
95
  s = option_chain['index_price'].iloc[-1]
97
96
 
98
- for ytm in unique_ytms:
97
+ for t in unique_ts:
99
98
  # Get data for this maturity
100
- maturity_data = option_chain[option_chain['ytm'] == ytm]
99
+ maturity_data = option_chain[option_chain['t'] == t]
101
100
  maturity_name = maturity_data['maturity_name'].iloc[0]
102
- dtm = maturity_data['dtm'].iloc[0]
103
101
 
104
102
  logger.info(f"Optimizing for {maturity_name}...")
105
103
 
@@ -108,7 +106,7 @@ def fit_model(option_chain: pd.DataFrame,
108
106
  result = least_squares(
109
107
  calculate_residuals,
110
108
  initial_params,
111
- args=(ytm, option_chain, SVIModel),
109
+ args=(t, option_chain, SVIModel),
112
110
  bounds=param_bounds,
113
111
  max_nfev=1000
114
112
  )
@@ -120,7 +118,7 @@ def fit_model(option_chain: pd.DataFrame,
120
118
 
121
119
  # Calculate model predictions for statistics
122
120
  w = np.array([SVIModel.svi(x, *result.x) for x in maturity_data['log_moneyness']])
123
- iv_model = np.sqrt(w / ytm)
121
+ iv_model = np.sqrt(w / t)
124
122
  iv_market = maturity_data['mark_iv'].values
125
123
 
126
124
  # Calculate statistics
@@ -138,27 +136,26 @@ def fit_model(option_chain: pd.DataFrame,
138
136
  r = maturity_data['interest_rate'].iloc[0] if 'interest_rate' in maturity_data.columns else 0
139
137
 
140
138
  # Calculate Jump-Wing parameters
141
- nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a, b, sigma, rho, m, ytm)
139
+ nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a, b, sigma, rho, m, t)
142
140
 
143
141
  # Store values in the results dictionary with proper types
144
142
  results_data['s'].append(float(s))
145
143
  results_data['u'].append(float(u))
144
+ results_data['t'].append(float(t))
145
+ results_data['r'].append(float(r))
146
+ results_data['oi'].append(float(oi))
147
+ results_data['volume'].append(float(volume))
146
148
  results_data['maturity_date'].append(maturity_data['maturity_date'].iloc[0])
147
- results_data['dtm'].append(float(dtm))
148
- results_data['ytm'].append(float(ytm))
149
149
  results_data['a'].append(float(a))
150
150
  results_data['b'].append(float(b))
151
- results_data['rho'].append(float(rho))
152
- results_data['m'].append(float(m))
153
151
  results_data['sigma'].append(float(sigma))
152
+ results_data['m'].append(float(m))
153
+ results_data['rho'].append(float(rho))
154
154
  results_data['nu'].append(float(nu))
155
155
  results_data['psi'].append(float(psi))
156
156
  results_data['p'].append(float(p))
157
157
  results_data['c'].append(float(c))
158
158
  results_data['nu_tilde'].append(float(nu_tilde))
159
- results_data['oi'].append(float(oi))
160
- results_data['volume'].append(float(volume))
161
- results_data['r'].append(float(r))
162
159
  results_data['fit_success'].append(bool(result.success))
163
160
  results_data['cost'].append(float(result.cost))
164
161
  results_data['optimality'].append(float(result.optimality))
@@ -189,7 +186,7 @@ def fit_model(option_chain: pd.DataFrame,
189
186
 
190
187
  @catch_exception
191
188
  def get_iv_surface(model_results: pd.DataFrame,
192
- log_moneyness_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
189
+ domain_params: Tuple[float, float, int] = (-1.5, 1.5, 1000),
193
190
  return_domain: str = 'log_moneyness') -> Tuple[Dict[str, np.ndarray], Dict[str, np.ndarray]]:
194
191
  """
195
192
  Generate implied volatility surface using optimized SVI parameters.
@@ -197,29 +194,28 @@ def get_iv_surface(model_results: pd.DataFrame,
197
194
  Works with both regular fit_results and interpolated_results dataframes.
198
195
 
199
196
  Parameters:
200
- - model_results: DataFrame from fit_model() or interpolate_model()
201
- - log_moneyness_params: Tuple of (min, max, num_points) for the moneyness grid
202
- - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'strikes', 'delta')
197
+ - model_results: DataFrame from fit_model() or interpolate_model(). Maturity names or DTM as Index
198
+ - domain_params: Tuple of (min, max, num_points) for the log-moneyness array
199
+ - return_domain: Domain for x-axis values ('log_moneyness', 'moneyness', 'returns', 'strikes', 'delta')
203
200
 
204
201
  Returns:
205
202
  - Tuple of (iv_surface, x_surface)
206
- iv_surface: Dictionary mapping maturity names to IV arrays
207
- x_surface: Dictionary mapping maturity names to requested x domain arrays
203
+ iv_surface: Dictionary mapping maturity/dtm names to IV arrays
204
+ x_surface: Dictionary mapping maturity/dtm names to requested x domain arrays
208
205
  """
209
206
  # Check if required columns are present
210
- required_columns = ['a', 'b', 'rho', 'm', 'sigma', 'ytm']
207
+ required_columns = ['a', 'b', 'sigma', 'rho', 'm', 't']
211
208
  missing_columns = [col for col in required_columns if col not in model_results.columns]
212
209
  if missing_columns:
213
210
  raise VolyError(f"Required columns missing in model_results: {missing_columns}")
214
211
 
215
212
  # Generate implied volatility surface in log-moneyness domain
216
- min_m, max_m, num_points = log_moneyness_params
217
- log_moneyness_array = np.linspace(min_m, max_m, num=num_points)
213
+ LM = np.linspace(domain_params[0], domain_params[1], domain_params[2])
218
214
 
219
215
  iv_surface = {}
220
216
  x_surface = {}
221
217
 
222
- # Process each maturity
218
+ # Process each maturity/dtm
223
219
  for i in model_results.index:
224
220
  # Calculate SVI total implied variance and convert to IV
225
221
  params = [
@@ -229,21 +225,17 @@ def get_iv_surface(model_results: pd.DataFrame,
229
225
  model_results.loc[i, 'rho'],
230
226
  model_results.loc[i, 'm']
231
227
  ]
232
- ytm = model_results.loc[i, 'ytm']
228
+ s = model_results.loc[i, 's'],
229
+ r = model_results.loc[i, 'r'],
230
+ t = model_results.loc[i, 't']
233
231
 
234
232
  # Calculate implied volatility
235
- w_svi = np.array([SVIModel.svi(x, *params) for x in log_moneyness_array])
236
- iv_array = np.sqrt(w_svi / ytm)
237
- iv_surface[maturity] = iv_array
238
-
239
- # Calculate x domain for this maturity
240
- x_surface[maturity] = get_x_domain(
241
- log_moneyness_params=log_moneyness_params,
242
- return_domain=return_domain,
243
- s=model_results.loc[i, 's'],
244
- r=model_results.loc[i, 'r'],
245
- iv_array=iv_array,
246
- ytm=ytm
247
- )
233
+ w = np.array([SVIModel.svi(x, *params) for x in LM])
234
+ o = np.sqrt(w / t)
235
+ iv_surface[i] = o
236
+
237
+ # Calculate x domain for this maturity/dtm
238
+ x = get_domain(domain_params, s, o, r, t, return_domain)
239
+ x_surface[i] = x
248
240
 
249
241
  return iv_surface, x_surface
voly/core/interpolate.py CHANGED
@@ -36,7 +36,7 @@ def interpolate_model(fit_results: pd.DataFrame,
36
36
  raise VolyError("Fit results DataFrame is empty or None")
37
37
 
38
38
  # Extract years to maturity from fit_results
39
- original_ytms = fit_results['ytm'].values
39
+ original_ytms = fit_results['t'].values
40
40
 
41
41
  if len(original_ytms) < 2:
42
42
  raise VolyError("Need at least two maturities in fit_results to interpolate")
@@ -68,14 +68,13 @@ def interpolate_model(fit_results: pd.DataFrame,
68
68
  "Extrapolation may give unreliable results.")
69
69
 
70
70
  # Columns to interpolate
71
- param_columns = ['u', 'a', 'b', 'rho', 'm', 'sigma', 'nu', 'psi', 'p', 'c', 'nu_tilde']
71
+ param_columns = ['u', 'a', 'b', 'sigma', 'rho', 'm', 'nu', 'psi', 'p', 'c', 'nu_tilde']
72
72
 
73
73
  # Create empty DataFrame for interpolated results
74
74
  interpolated_df = pd.DataFrame(index=[f"{day}d" for day in target_days])
75
75
 
76
76
  # Generate YTM and maturity dates for interpolated results
77
- interpolated_df['dtm'] = target_days
78
- interpolated_df['ytm'] = [day / 365.25 for day in target_days]
77
+ interpolated_df['t'] = target_years
79
78
 
80
79
  # Calculate maturity dates
81
80
  now = dt.datetime.now()
@@ -120,8 +119,8 @@ def interpolate_model(fit_results: pd.DataFrame,
120
119
  interpolated_df[param] = f(target_years)
121
120
 
122
121
  # Ensure consistent ordering of columns with expected structure
123
- expected_columns = ['s', 'u', 'maturity_date', 'dtm', 'ytm', 'a', 'b', 'rho', 'm', 'sigma',
124
- 'nu', 'psi', 'p', 'c', 'nu_tilde', 'r']
122
+ expected_columns = ['s', 'u', 't', 'r', 'maturity_date', 'a', 'b', 'sigma', 'rho', 'm',
123
+ 'nu', 'psi', 'p', 'c', 'nu_tilde']
125
124
 
126
125
  # Create final column order based on available columns
127
126
  column_order = [col for col in expected_columns if col in interpolated_df.columns]