variance-test 0.1.0__tar.gz

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.
Files changed (28) hide show
  1. variance_test-0.1.0/LICENSE +21 -0
  2. variance_test-0.1.0/PKG-INFO +417 -0
  3. variance_test-0.1.0/README.md +374 -0
  4. variance_test-0.1.0/pyproject.toml +40 -0
  5. variance_test-0.1.0/setup.cfg +4 -0
  6. variance_test-0.1.0/src/variance_test/__init__.py +32 -0
  7. variance_test-0.1.0/src/variance_test/battery.py +445 -0
  8. variance_test-0.1.0/src/variance_test/core.py +291 -0
  9. variance_test-0.1.0/src/variance_test/data.py +80 -0
  10. variance_test-0.1.0/src/variance_test/models.py +101 -0
  11. variance_test-0.1.0/src/variance_test/price_paths.py +268 -0
  12. variance_test-0.1.0/src/variance_test/rolling.py +208 -0
  13. variance_test-0.1.0/src/variance_test/simulation.py +315 -0
  14. variance_test-0.1.0/src/variance_test/visuals.py +166 -0
  15. variance_test-0.1.0/src/variance_test.egg-info/PKG-INFO +417 -0
  16. variance_test-0.1.0/src/variance_test.egg-info/SOURCES.txt +26 -0
  17. variance_test-0.1.0/src/variance_test.egg-info/dependency_links.txt +1 -0
  18. variance_test-0.1.0/src/variance_test.egg-info/requires.txt +4 -0
  19. variance_test-0.1.0/src/variance_test.egg-info/top_level.txt +1 -0
  20. variance_test-0.1.0/tests/test_battery.py +318 -0
  21. variance_test-0.1.0/tests/test_build_metadata.py +34 -0
  22. variance_test-0.1.0/tests/test_data_normalization.py +67 -0
  23. variance_test-0.1.0/tests/test_models.py +103 -0
  24. variance_test-0.1.0/tests/test_package_imports.py +31 -0
  25. variance_test-0.1.0/tests/test_public_api.py +78 -0
  26. variance_test-0.1.0/tests/test_rolling.py +258 -0
  27. variance_test-0.1.0/tests/test_simulation_compat.py +63 -0
  28. variance_test-0.1.0/tests/test_vrt_hardening.py +85 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 LautaroParada
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,417 @@
1
+ Metadata-Version: 2.4
2
+ Name: variance-test
3
+ Version: 0.1.0
4
+ Summary: Variance Ratio Test utilities for stochastic price processes.
5
+ Author: SeraFlow
6
+ License: MIT License
7
+
8
+ Copyright (c) 2020 LautaroParada
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/LautaroParada/variance-test
29
+ Project-URL: Repository, https://github.com/LautaroParada/variance-test
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: Programming Language :: Python :: 3.11
32
+ Classifier: Programming Language :: Python :: 3.12
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Requires-Python: >=3.11
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: numpy
39
+ Requires-Dist: scipy
40
+ Requires-Dist: matplotlib
41
+ Requires-Dist: statsmodels
42
+ Dynamic: license-file
43
+
44
+ # variance-test
45
+
46
+ [![PyPI version](https://img.shields.io/pypi/v/variance-test.svg)](https://pypi.org/project/variance-test/)
47
+ [![Python versions](https://img.shields.io/pypi/pyversions/variance-test.svg)](https://pypi.org/project/variance-test/)
48
+ [![CI](https://github.com/LautaroParada/variance-test/actions/workflows/ci.yml/badge.svg)](https://github.com/LautaroParada/variance-test/actions/workflows/ci.yml)
49
+ [![License](https://img.shields.io/github/license/LautaroParada/variance-test.svg)](https://github.com/LautaroParada/variance-test/blob/master/LICENSE)
50
+
51
+ Testing whether a return series is compatible with a random walk usually means stitching together a variance ratio implementation, a Ljung-Box call, an ARCH-LM check, a runs test, and some ad-hoc glue code to normalize inputs and interpret results. Then you repeat it for rolling windows. Then you do it again for the next asset.
52
+
53
+ `variance-test` replaces that workflow with a single structured call.
54
+
55
+ ```python
56
+ from variance_test import BatteryConfig, run_weak_form_battery
57
+
58
+ outcome = run_weak_form_battery(returns, config=BatteryConfig(input_kind="returns"))
59
+
60
+ print(outcome.multiple_testing["battery_summary"])
61
+ ```
62
+
63
+ One call. Structured output. Mean dependence, sign dependence, volatility dependence, and multiple testing correction included.
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ pip install variance-test
69
+ ```
70
+
71
+ For development:
72
+
73
+ ```bash
74
+ pip install -e .
75
+ ```
76
+
77
+ ## Quick start
78
+
79
+ ### Variance ratio test only
80
+
81
+ ```python
82
+ import numpy as np
83
+ from variance_test import EMH
84
+
85
+ rng = np.random.default_rng(42)
86
+ returns = rng.normal(0.0, 0.01, size=2000)
87
+
88
+ emh = EMH()
89
+ z_score, p_value = emh.vrt(
90
+ X=returns,
91
+ q=4,
92
+ input_kind="returns",
93
+ heteroskedastic=True,
94
+ centered=True,
95
+ unbiased=True,
96
+ annualize=False,
97
+ alternative="two-sided",
98
+ )
99
+
100
+ print("z_score:", z_score)
101
+ print("p_value:", p_value)
102
+ ```
103
+
104
+ ### Full weak-form battery
105
+
106
+ ```python
107
+ import numpy as np
108
+ from variance_test import BatteryConfig, run_weak_form_battery
109
+
110
+ rng = np.random.default_rng(42)
111
+ returns = rng.normal(0.0, 0.01, size=2000)
112
+
113
+ config = BatteryConfig(
114
+ input_kind="returns",
115
+ alpha=0.05,
116
+ q_list=(2, 4, 8),
117
+ ljung_box_lags=(5, 10, 20),
118
+ runs_test=True,
119
+ arch_lm_lags=5,
120
+ )
121
+
122
+ outcome = run_weak_form_battery(returns, config=config)
123
+
124
+ # Holm-corrected VR family decision
125
+ print("VR family rejects:", outcome.tests["variance_ratio_holm"].reject_null)
126
+
127
+ # Full battery summary
128
+ print(outcome.multiple_testing["battery_summary"])
129
+ ```
130
+
131
+ ### Battery with rolling windows
132
+
133
+ ```python
134
+ import numpy as np
135
+ from variance_test import BatteryConfig, run_weak_form_battery
136
+
137
+ rng = np.random.default_rng(123)
138
+ returns = rng.normal(0.0, 0.01, size=1500)
139
+
140
+ config = BatteryConfig(
141
+ input_kind="returns",
142
+ q_list=(2, 4),
143
+ ljung_box_lags=(5, 10),
144
+ runs_test=True,
145
+ arch_lm_lags=5,
146
+ rolling_window=120,
147
+ rolling_step=20,
148
+ )
149
+
150
+ outcome = run_weak_form_battery(returns, config=config)
151
+
152
+ print("n_windows:", outcome.rolling["n_windows"])
153
+ print("first_vr_windows:", outcome.rolling["tests"]["variance_ratio_q2"][:2])
154
+ ```
155
+
156
+ ### Empirical application
157
+
158
+ ```python
159
+ import numpy as np
160
+ from variance_test import EMH, BatteryConfig, run_weak_form_battery
161
+
162
+ # Replace with your own data source
163
+ prices = np.array([100.0, 101.5, 100.8, 102.2, 103.1, 101.9, ...])
164
+ log_prices = np.log(prices)
165
+
166
+ # Single test
167
+ emh = EMH()
168
+ z, p = emh.vrt(X=log_prices, q=5, input_kind="log_prices", heteroskedastic=True)
169
+ print(f"VR test: z={z:.4f}, p={p:.4f}")
170
+
171
+ # Full battery on returns
172
+ returns = np.diff(log_prices)
173
+ config = BatteryConfig(
174
+ input_kind="returns",
175
+ q_list=(2, 4, 8, 16),
176
+ ljung_box_lags=(5, 10, 20),
177
+ rolling_window=252,
178
+ rolling_step=21,
179
+ )
180
+ outcome = run_weak_form_battery(returns, config=config)
181
+ print(outcome.multiple_testing["battery_summary"])
182
+ ```
183
+
184
+ ## What the battery includes
185
+
186
+ | Test | Diagnostic target | Rolling support |
187
+ |---|---|---|
188
+ | Variance ratio multi-q | Mean dependence at multiple horizons | Yes |
189
+ | Holm-Bonferroni correction | Family-wise error control for VR tests | No |
190
+ | Ljung-Box on returns | Serial dependence in returns | Yes |
191
+ | Ljung-Box on squared returns | Serial dependence in volatility | Yes |
192
+ | Runs test on signs | Non-random sign patterns | No |
193
+ | ARCH LM | Conditional heteroskedasticity | No |
194
+
195
+ The battery output classifies rejections into three categories: **mean dependence**, **sign dependence**, and **volatility dependence**. The summary field aggregates these into a single `weak_form_evidence_against_null` flag.
196
+
197
+ ## Input formats
198
+
199
+ The package accepts two input modes:
200
+
201
+ * `input_kind="returns"` for one-period log-returns
202
+ * `input_kind="log_prices"` for cumulative log-price series
203
+
204
+ Both modes produce identical test statistics for equivalent data. This is tested and enforced in the test suite.
205
+
206
+ If you have raw prices:
207
+
208
+ ```python
209
+ import numpy as np
210
+
211
+ prices = np.array([100.0, 101.5, 100.8, 102.2])
212
+ log_prices = np.log(prices)
213
+ returns = np.diff(log_prices)
214
+ ```
215
+
216
+ ## Main interfaces
217
+
218
+ ### `EMH().vrt(...)`
219
+
220
+ Low-level variance ratio test.
221
+
222
+ | Parameter | Description |
223
+ |---|---|
224
+ | `X` | 1-D input series |
225
+ | `q` | Aggregation horizon |
226
+ | `input_kind` | `"returns"` or `"log_prices"` |
227
+ | `heteroskedastic` | Use heteroskedastic asymptotic variance (Lo & MacKinlay Z2) |
228
+ | `centered` | Centered statistic |
229
+ | `unbiased` | Unbiased variance/autocovariance estimators |
230
+ | `annualize` | Scaling flag for intermediate volatility estimators |
231
+ | `alternative` | `"two-sided"`, `"greater"`, or `"less"` |
232
+
233
+ Returns `(z_score, p_value)`.
234
+
235
+ ### `normalize_series(...)`
236
+
237
+ Shared normalization used internally. Exposed for users who want direct access to the canonical representation.
238
+
239
+ ```python
240
+ from variance_test import normalize_series
241
+
242
+ ns = normalize_series([0.01, -0.02, 0.015], input_kind="returns")
243
+ # ns.log_prices, ns.returns, ns.squared_returns, ns.signs, ns.n_returns
244
+ ```
245
+
246
+ ### `run_weak_form_battery(...)`
247
+
248
+ Full diagnostic pass. Returns a `BatteryOutcome` with:
249
+
250
+ * `tests` -- individual `TestOutcome` objects per test
251
+ * `multiple_testing` -- Holm summary and battery summary
252
+ * `rolling` -- rolling-window results (when configured)
253
+ * `warnings` -- non-computable test warnings
254
+
255
+ ## How to read the results
256
+
257
+ **Variance ratio:** `VR(q) > 1` suggests positive serial dependence. `VR(q) < 1` suggests mean reversion. `VR(q) ≈ 1` is compatible with the random walk null.
258
+
259
+ **p-values:** Low p-value means the data is less compatible with the null hypothesis.
260
+
261
+ **Battery summary:** The battery provides statistical evidence against compatibility with weak-form efficiency under its test design. It does not constitute proof of market inefficiency. A rejection means the series shows detectable structure under the selected diagnostics.
262
+
263
+ ## Implementation details
264
+
265
+ ### Variance ratio estimator
266
+
267
+ The implementation uses **overlapping q-period increments** for the q-period variance estimator:
268
+
269
+ ```
270
+ σ²_a(q) = (1 / ((n_q - 1) · q)) · Σ (X_{t} - X_{t-q} - q·μ̂)²
271
+ ```
272
+
273
+ where the sum runs over all overlapping windows `t = q, q+1, ..., T`, and `μ̂ = (X_T - X_0) / T`.
274
+
275
+ The one-period variance estimator uses standard first differences:
276
+
277
+ ```
278
+ σ²_b = (1 / (n₁ - 1)) · Σ (X_{t} - X_{t-1} - μ̂)²
279
+ ```
280
+
281
+ The centered ratio is `M_r(q) = (σ²_a / σ²_b) - 1`.
282
+
283
+ Under the **homoskedastic null** (Lo & MacKinlay Z1), the asymptotic variance is `2(2q-1)(q-1) / (3q)`.
284
+
285
+ Under the **heteroskedastic null** (Lo & MacKinlay Z2), the asymptotic variance uses the `δ̂(j)` estimator:
286
+
287
+ ```
288
+ δ̂(j) = (nq · Σ (ΔX_{t} - μ̂)² · (ΔX_{t-j} - μ̂)²) / (Σ (ΔX_{t} - μ̂)²)²
289
+ ```
290
+
291
+ with weights `(2(q-j)/q)²` for `j = 1, ..., q-1`.
292
+
293
+ Edge cases where `q < 2`, sample sizes are insufficient, or variance estimates are non-positive raise explicit `ValueError` exceptions instead of producing silent NaN or incorrect results.
294
+
295
+ ### References
296
+
297
+ * Lo, A.W. and MacKinlay, A.C. "Stock Market Prices Do Not Follow Random Walks: Evidence from a Simple Specification Test." *Review of Financial Studies*, 1(1), 1988.
298
+ * Lo, A.W. and MacKinlay, A.C. "The Size and Power of the Variance Ratio Test in Finite Samples: A Monte Carlo Investigation." *Journal of Econometrics*, 40(2), 1989.
299
+
300
+ ## Correctness and testing
301
+
302
+ The implementation was [audited for statistical correctness](AUDIT.md). Three issues were identified and resolved:
303
+
304
+ 1. **σ²_a estimator ignoring q-aggregation** (severity: high) -- the original loop computed first differences regardless of `q`. Fixed with overlapping q-period differences using `X[t] - X[t-q]`.
305
+ 2. **Division by zero in unbiased σ²_b adjustment** (severity: medium) -- when `len(X) == q`, the denominator `m` collapsed to zero. Fixed with explicit validation that degrees of freedom are positive before division.
306
+ 3. **v̂ degenerate for q < 3** (severity: medium) -- for `q = 1` or `q = 2`, the summation range in the heteroskedastic estimator was empty, producing `v̂ = 0` and a subsequent division by zero. Fixed with an explicit guard requiring `q ≥ 2` and `v̂ > 0`.
307
+
308
+ The package is covered by an automated test suite that validates calibration, detection, edge cases, rolling-window behavior, and input-mode equivalence. Key coverage includes:
309
+
310
+ * IID Gaussian non-rejection at α=0.01 for the selected battery components covered by the test suite (`variance_ratio_holm`, `ljung_box_returns`, `runs_test_signs`, `arch_lm`)
311
+ * AR(1) detection of serial dependence with VR family and Ljung-Box rejection
312
+ * ARCH-like data detection of volatility clustering via Ljung-Box squared returns and ARCH LM
313
+ * Alternating sign rejection via runs test
314
+ * Exact equivalence between `returns` and `log_prices` input modes for all comparable test outcomes
315
+ * p-value/reject_null consistency enforcement in all TestOutcome instances
316
+ * Rolling window index monotonicity, count formulas, and non-computable window handling
317
+ * Edge cases: all-zero returns, insufficient samples, incompatible lag/horizon combinations
318
+
319
+ CI runs on Python 3.11 and 3.12 on every push and pull request.
320
+
321
+ ## Simulation support
322
+
323
+ The package includes simulation utilities for synthetic experimentation.
324
+
325
+ ```python
326
+ from variance_test import SimulationConfig, run_simulation
327
+
328
+ config = SimulationConfig(
329
+ num_series=5,
330
+ horizon=200,
331
+ initial_price=100.0,
332
+ mu=0.05,
333
+ sigma=0.2,
334
+ aggregation_horizon=2,
335
+ heteroskedastic=False,
336
+ seed=42,
337
+ )
338
+
339
+ results = run_simulation(config)
340
+ print(f"Mean z-score: {results.z_scores.mean():.4f}")
341
+ print(f"Mean p-value: {results.p_values.mean():.4f}")
342
+ ```
343
+
344
+ Included stochastic processes: Geometric Brownian Motion, Merton Jump Diffusion, Heston Stochastic Volatility, Vasicek, Cox-Ingersoll-Ross, Ornstein-Uhlenbeck.
345
+
346
+ CLI:
347
+
348
+ ```bash
349
+ python -m variance_test.simulation --series 10 --horizon 100 --aggregation 3 --seed 42 --no-plot
350
+ ```
351
+
352
+ ## Package architecture
353
+
354
+ ```mermaid
355
+ flowchart TD
356
+ A[User input series] --> B{input_kind}
357
+ B -->|returns| C[normalize_series]
358
+ B -->|log_prices| C
359
+
360
+ C --> D[EMH.vrt]
361
+ C --> E[run_weak_form_battery]
362
+
363
+ E --> F[Variance ratio family]
364
+ E --> G[Holm summary]
365
+ E --> H[Ljung-Box returns]
366
+ E --> I[Ljung-Box squared returns]
367
+ E --> J[Runs test on signs]
368
+ E --> K[ARCH LM]
369
+ E --> L[Rolling results for supported tests]
370
+ ```
371
+
372
+ ## Public API
373
+
374
+ ```mermaid
375
+ flowchart LR
376
+ A[variance_test] --> B[EMH]
377
+ A --> C[normalize_series]
378
+ A --> D[run_weak_form_battery]
379
+ A --> E[SimulationConfig]
380
+ A --> F[run_simulation]
381
+ A --> G[PricePaths]
382
+ A --> H[VRTVisuals]
383
+ ```
384
+
385
+ ## Examples
386
+
387
+ ### Offline examples (self-contained, no external dependencies)
388
+
389
+ * `examples/basic_battery.py` -- full-sample battery
390
+ * `examples/rolling_battery.py` -- rolling-window battery
391
+ * `examples/variance_ratio_only.py` -- standalone VRT
392
+
393
+ ### External-data example (requires API credentials)
394
+
395
+ * `examples/empirical_application.py` -- empirical VRT on market data via [EOD Historical Data](https://eodhistoricaldata.com/). Requires `eod` package and `API_EOD` environment variable.
396
+
397
+ ## Citation
398
+
399
+ ### BibTeX
400
+
401
+ ```bibtex
402
+ @software{parada_variance_test_2026,
403
+ title = {variance-test},
404
+ author = {Parada, Lautaro},
405
+ year = {2026},
406
+ url = {https://github.com/LautaroParada/variance-test},
407
+ note = {Python package for variance ratio testing and weak-form efficiency diagnostics}
408
+ }
409
+ ```
410
+
411
+ ### Plain text
412
+
413
+ Parada, Lautaro. `variance-test`. Python package for variance ratio testing and weak-form efficiency diagnostics. GitHub repository: [https://github.com/LautaroParada/variance-test](https://github.com/LautaroParada/variance-test)
414
+
415
+ ## License
416
+
417
+ MIT. See [LICENSE](LICENSE).