voly 0.0.178__py3-none-any.whl → 0.0.180__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 +2 -2
- voly/core/charts.py +1 -1
- voly/core/fit.py +10 -15
- voly/models.py +6 -6
- {voly-0.0.178.dist-info → voly-0.0.180.dist-info}/METADATA +1 -1
- {voly-0.0.178.dist-info → voly-0.0.180.dist-info}/RECORD +9 -9
- {voly-0.0.178.dist-info → voly-0.0.180.dist-info}/WHEEL +0 -0
- {voly-0.0.178.dist-info → voly-0.0.180.dist-info}/licenses/LICENSE +0 -0
- {voly-0.0.178.dist-info → voly-0.0.180.dist-info}/top_level.txt +0 -0
voly/client.py
CHANGED
@@ -91,9 +91,9 @@ class VolyClient:
|
|
91
91
|
# -------------------------------------------------------------------------
|
92
92
|
|
93
93
|
@staticmethod
|
94
|
-
def
|
94
|
+
def svi(k: float, a: float, b: float, m: float, rho: float, sigma: float) -> float:
|
95
95
|
"""Calculate SVI total implied variance using raw parameterization."""
|
96
|
-
return SVIModel.
|
96
|
+
return SVIModel.svi(k, a, b, m, rho, sigma)
|
97
97
|
|
98
98
|
@staticmethod
|
99
99
|
def d1(s: float, K: float, r: float, o: float, t: float,
|
voly/core/charts.py
CHANGED
voly/core/fit.py
CHANGED
@@ -96,8 +96,6 @@ def fit_model(option_chain: pd.DataFrame,
|
|
96
96
|
mask = ~np.isnan(w) & ~np.isnan(vega) & ~np.isnan(k) & (iv >= 0)
|
97
97
|
k, w, vega, iv = k[mask], w[mask], vega[mask], iv[mask]
|
98
98
|
|
99
|
-
logger.info(f"Points after filtering: {len(k)}")
|
100
|
-
|
101
99
|
params = [np.nan] * 5
|
102
100
|
loss = np.inf
|
103
101
|
nu = psi = p = c = nu_tilde = np.nan
|
@@ -119,7 +117,7 @@ def fit_model(option_chain: pd.DataFrame,
|
|
119
117
|
nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a_scaled, b_scaled, m, rho, sigma, t)
|
120
118
|
|
121
119
|
# Compute fit statistics
|
122
|
-
w_model = np.array([SVIModel.
|
120
|
+
w_model = np.array([SVIModel.svi(x, a_scaled, b_scaled, m, rho, sigma) for x in k])
|
123
121
|
iv_model = np.sqrt(w_model / t)
|
124
122
|
iv_market = iv
|
125
123
|
rmse = np.sqrt(mean_squared_error(iv_market, iv_model))
|
@@ -133,7 +131,7 @@ def fit_model(option_chain: pd.DataFrame,
|
|
133
131
|
|
134
132
|
# Butterfly arbitrage check
|
135
133
|
k_range = np.linspace(min(k), max(k), domain_params[2])
|
136
|
-
w_k = lambda k: SVIModel.
|
134
|
+
w_k = lambda k: SVIModel.svi(k, a_scaled, b_scaled, m, rho, sigma)
|
137
135
|
w_prime = lambda k: b_scaled * (rho + (k - m) / np.sqrt((k - m) ** 2 + sigma ** 2))
|
138
136
|
w_double_prime = lambda k: b_scaled * sigma ** 2 / ((k - m) ** 2 + sigma ** 2) ** (3 / 2)
|
139
137
|
|
@@ -149,10 +147,7 @@ def fit_model(option_chain: pd.DataFrame,
|
|
149
147
|
# Log result
|
150
148
|
status = f'{GREEN}SUCCESS{RESET}' if not np.isnan(params[0]) else f'{RED}FAILED{RESET}'
|
151
149
|
logger.info(f'Optimization for {maturity_name}: {status}')
|
152
|
-
|
153
|
-
logger.info(
|
154
|
-
f"Maturity {maturity_name}: a={a_scaled:.4f}, b={b_scaled:.4f}, m={m:.4f}, rho={rho:.4f}, sigma={sigma:.4f}")
|
155
|
-
logger.info("=================================================================")
|
150
|
+
logger.info("=============================================")
|
156
151
|
|
157
152
|
results_data['s'].append(float(s))
|
158
153
|
results_data['u'].append(float(u))
|
@@ -219,8 +214,8 @@ def fit_model(option_chain: pd.DataFrame,
|
|
219
214
|
np.concatenate([k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), domain_params[2])]))
|
220
215
|
|
221
216
|
for k_val in k_check:
|
222
|
-
w1 = SVIModel.
|
223
|
-
w2 = SVIModel.
|
217
|
+
w1 = SVIModel.svi(k_val, a1 * t1, b1 * t1, m1, rho1, sigma1)
|
218
|
+
w2 = SVIModel.svi(k_val, a2 * t2, b2 * t2, m2, rho2, sigma2)
|
224
219
|
if w2 < w1 - 1e-6:
|
225
220
|
logger.warning(
|
226
221
|
f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: w1={w1:.6f}, w2={w2:.6f}")
|
@@ -272,7 +267,7 @@ def fit_model(option_chain: pd.DataFrame,
|
|
272
267
|
nu, psi, p, c, nu_tilde = SVIModel.raw_to_jw_params(a_scaled, b_scaled, m, rho, sigma, t2)
|
273
268
|
|
274
269
|
# Recompute fit statistics
|
275
|
-
w_model = np.array([SVIModel.
|
270
|
+
w_model = np.array([SVIModel.svi(x, a_scaled, b_scaled, m, rho, sigma) for x in k])
|
276
271
|
iv_model = np.sqrt(w_model / t2)
|
277
272
|
iv_market = iv
|
278
273
|
rmse = np.sqrt(mean_squared_error(iv_market, iv_model))
|
@@ -287,7 +282,7 @@ def fit_model(option_chain: pd.DataFrame,
|
|
287
282
|
# Update butterfly arbitrage check
|
288
283
|
butterfly_arbitrage_free = True
|
289
284
|
k_range = np.linspace(min(k), max(k), domain_params[2])
|
290
|
-
w_k = lambda k: SVIModel.
|
285
|
+
w_k = lambda k: SVIModel.svi(k, a_scaled, b_scaled, m, rho, sigma)
|
291
286
|
w_prime = lambda k: b_scaled * (rho + (k - m) / np.sqrt((k - m) ** 2 + sigma ** 2))
|
292
287
|
w_double_prime = lambda k: b_scaled * sigma ** 2 / ((k - m) ** 2 + sigma ** 2) ** (3 / 2)
|
293
288
|
|
@@ -349,8 +344,8 @@ def fit_model(option_chain: pd.DataFrame,
|
|
349
344
|
[k_market[mask], np.linspace(min(k_market[mask]), max(k_market[mask]), domain_params[2])]))
|
350
345
|
|
351
346
|
for k_val in k_check:
|
352
|
-
w1 = SVIModel.
|
353
|
-
w2 = SVIModel.
|
347
|
+
w1 = SVIModel.svi(k_val, a1 * t1, b1 * t1, m1, rho1, sigma1)
|
348
|
+
w2 = SVIModel.svi(k_val, a2 * t2, b2 * t2, m2, rho2, sigma2)
|
354
349
|
if w2 < w1 - 1e-6:
|
355
350
|
logger.warning(
|
356
351
|
f"Calendar arbitrage violation at t1={t1:.4f}, t2={t2:.4f}, k={k_val:.4f}: w1={w1:.6f}, w2={w2:.6f}")
|
@@ -418,7 +413,7 @@ def get_iv_surface(model_results: pd.DataFrame,
|
|
418
413
|
t = model_results.loc[i, 't']
|
419
414
|
|
420
415
|
# Calculate implied volatility
|
421
|
-
w = np.array([SVIModel.
|
416
|
+
w = np.array([SVIModel.svi(x, *params) for x in LM])
|
422
417
|
o = np.sqrt(w / t)
|
423
418
|
iv_surface[i] = o
|
424
419
|
|
voly/models.py
CHANGED
@@ -31,7 +31,7 @@ class SVIModel:
|
|
31
31
|
}
|
32
32
|
|
33
33
|
@staticmethod
|
34
|
-
def
|
34
|
+
def svi(k, a, b, m, rho, sigma):
|
35
35
|
assert b >= 0 and abs(rho) <= 1 and sigma >= 0 and a + b * sigma * np.sqrt(1 - rho ** 2) >= 0
|
36
36
|
return a + b * (rho * (k - m) + np.sqrt((k - m) ** 2 + sigma ** 2))
|
37
37
|
|
@@ -128,7 +128,7 @@ class SVIModel:
|
|
128
128
|
|
129
129
|
def objective(x):
|
130
130
|
a, b, m, rho, sigma = x
|
131
|
-
w_model = cls.
|
131
|
+
w_model = cls.svi(k, a * t, b * t, m, rho, sigma)
|
132
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
|
@@ -137,8 +137,8 @@ class SVIModel:
|
|
137
137
|
|
138
138
|
def calendar_constraint(x):
|
139
139
|
a, b, m, rho, sigma = x
|
140
|
-
w_current = cls.
|
141
|
-
w_prev = cls.
|
140
|
+
w_current = cls.svi(k_constraint, a * t, b * t, m, rho, sigma)
|
141
|
+
w_prev = cls.svi(k_constraint, a_prev * prev_t, b_prev * prev_t, m_prev, rho_prev, sigma_prev)
|
142
142
|
return w_current - w_prev
|
143
143
|
|
144
144
|
bounds = [
|
@@ -162,8 +162,8 @@ class SVIModel:
|
|
162
162
|
|
163
163
|
if result.success:
|
164
164
|
new_params = result.x
|
165
|
-
w_current = cls.
|
166
|
-
w_prev = cls.
|
165
|
+
w_current = cls.svi(k_constraint, new_params[0] * t, new_params[1] * t, *new_params[2:])
|
166
|
+
w_prev = cls.svi(k_constraint, a_prev * prev_t, b_prev * prev_t, m_prev, rho_prev, sigma_prev)
|
167
167
|
violation = np.min(w_current - w_prev)
|
168
168
|
print(f"Calendar arbitrage correction {'successful' if violation >= -1e-6 else 'failed'} for t={t:.4f}, "
|
169
169
|
f"min margin={violation:.6f}")
|
@@ -1,20 +1,20 @@
|
|
1
1
|
voly/__init__.py,sha256=8xyDk7rFCn_MOD5hxuv5cxxKZvBVRiSIM7TgaMPpwpw,211
|
2
|
-
voly/client.py,sha256=
|
2
|
+
voly/client.py,sha256=M_-d3ibPp2Uc8ucEkWfhKbqKOQ7xSuUO6dGHTIqmG3E,14587
|
3
3
|
voly/exceptions.py,sha256=PBsbn1vNMvKcCJwwJ4lBO6glD85jo1h2qiEmD7ArAjs,92
|
4
4
|
voly/formulas.py,sha256=JnEs6G0wlfRNH6X_YEJMe2RtLH-ryhzufjsim73Bj3c,11176
|
5
|
-
voly/models.py,sha256=
|
5
|
+
voly/models.py,sha256=wxqf9T4D2ORO7g3KcwxUL0E78fG69W29xqer3ccoUXo,6994
|
6
6
|
voly/core/__init__.py,sha256=bu6fS2I1Pj9fPPnl-zY3L7NqrZSY5Zy6NY2uMUvdhKs,183
|
7
|
-
voly/core/charts.py,sha256=
|
7
|
+
voly/core/charts.py,sha256=MFYVT2UZenfffuatFOUHrzBtl-kMXLJogXLh6pAto-I,12719
|
8
8
|
voly/core/data.py,sha256=9v9iuE2XdIIlzoRAB7q1ol7YghBzBsPGAiwZ11oDuis,13650
|
9
|
-
voly/core/fit.py,sha256=
|
9
|
+
voly/core/fit.py,sha256=DmQSoXE7SDHK-Oj2dCoybYJoVcqT8azvdA72X9aW_6E,17917
|
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.
|
17
|
-
voly-0.0.
|
18
|
-
voly-0.0.
|
19
|
-
voly-0.0.
|
20
|
-
voly-0.0.
|
16
|
+
voly-0.0.180.dist-info/licenses/LICENSE,sha256=wcHIVbE12jfcBOai_wqBKY6xvNQU5E909xL1zZNq_2Q,1065
|
17
|
+
voly-0.0.180.dist-info/METADATA,sha256=2tvqtAMOonoYRaRcPMQeB0dXQILyawWPzSciInOBce0,4115
|
18
|
+
voly-0.0.180.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
|
19
|
+
voly-0.0.180.dist-info/top_level.txt,sha256=ZfLw2sSxF-LrKAkgGjOmeTcw6_gD-30zvtdEY5W4B7c,5
|
20
|
+
voly-0.0.180.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|