Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

on:
push:
pull_request:

jobs:
test-and-build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Upgrade pip
run: python -m pip install --upgrade pip

- name: Install package
run: pip install -e .

- name: Install test and build tools
run: pip install pytest build

- name: Run tests
run: pytest -q

- name: Build package
run: python -m build
46 changes: 43 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,40 @@ print(outcome.multiple_testing["battery_summary"])
> Warning: this battery provides evidence against compatibility with weak-form efficiency **under this battery**.
> It is **not** definitive proof of market inefficiency.
>
> Note: `rolling = None` in this version.
Rolling mode is available by setting `BatteryConfig(rolling_window=..., rolling_step=...)`.
In this version, rolling results are provided only for:
- variance ratio multi-q
- Ljung-Box returns
- Ljung-Box squared returns

Rolling mode is **not** implemented in this version for:
- Holm summary
- runs test
- ARCH LM

```python
import numpy as np
from variance_test import BatteryConfig, run_weak_form_battery

rng = np.random.default_rng(123)
returns = rng.normal(0.0, 0.01, size=1200)

config = BatteryConfig(
input_kind="returns",
q_list=(2, 4),
ljung_box_lags=(5, 10),
rolling_window=120,
rolling_step=20,
)
outcome = run_weak_form_battery(returns, config=config)
print(outcome.rolling["n_windows"])
```

Offline runnable examples are available under `examples/`:
- `examples/basic_battery.py`
- `examples/rolling_battery.py`
- `examples/variance_ratio_only.py`
- `examples/empirical_application.py`

### Running Simulations

Expand Down Expand Up @@ -237,11 +270,18 @@ variance-test/
│ └── variance_test/
│ ├── __init__.py
│ ├── core.py # Core VRT implementation (EMH class)
│ ├── battery.py # Weak-form battery v1
│ ├── rolling.py # Internal rolling helpers
│ ├── data.py # Input normalization
│ ├── models.py # Dataclass contracts
│ ├── price_paths.py # Stochastic process generators
│ ├── simulation.py # Simulation framework and CLI
│ └── visuals.py # Plotting utilities
├── examples/
│ └── empirical_application.py # Optional external example (uses external deps)
│ ├── basic_battery.py # Offline battery example (full sample)
│ ├── rolling_battery.py # Offline battery example (rolling mode)
│ ├── variance_ratio_only.py # Offline EMH.vrt usage example
│ └── empirical_application.py # Optional external example
├── tests/
├── pyproject.toml
├── README.md
Expand Down Expand Up @@ -296,4 +336,4 @@ If you use this implementation in your research, please cite:
url = {https://github.com/LautaroParada/variance-test},
note = {Implementation following Lo \& MacKinlay (1988)}
}
```
```
17 changes: 17 additions & 0 deletions examples/basic_battery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
"""Basic offline example for the weak-form battery without rolling windows."""

from __future__ import annotations

import numpy as np

from variance_test import BatteryConfig, run_weak_form_battery


rng = np.random.default_rng(42)
returns = rng.normal(0.0, 0.01, size=1500)
config = BatteryConfig(input_kind="returns", q_list=(2, 4, 8), ljung_box_lags=(5, 10, 20))
outcome = run_weak_form_battery(returns, config=config)

print("Battery summary:")
print(outcome.multiple_testing["battery_summary"])
28 changes: 28 additions & 0 deletions examples/rolling_battery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Offline rolling-window example for the weak-form battery."""

from __future__ import annotations

import numpy as np

from variance_test import BatteryConfig, run_weak_form_battery


rng = np.random.default_rng(123)
returns = rng.normal(0.0, 0.01, size=1200)

config = BatteryConfig(
input_kind="returns",
q_list=(2, 4),
ljung_box_lags=(5, 10),
rolling_window=120,
rolling_step=20,
)
outcome = run_weak_form_battery(returns, config=config)

rolling = outcome.rolling or {}
vr_q2 = rolling.get("tests", {}).get("variance_ratio_q2", [])

print(f"n_windows={rolling.get('n_windows')}")
print("first_two_variance_ratio_q2_entries=")
print(vr_q2[:2])
21 changes: 21 additions & 0 deletions examples/variance_ratio_only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""Minimal EMH.vrt usage example for log-prices and returns inputs."""

from __future__ import annotations

import numpy as np

from variance_test import EMH


rng = np.random.default_rng(7)
returns = rng.normal(0.0, 0.01, size=1000)
log_prices = np.concatenate(([0.0], np.cumsum(returns)))

emh = EMH()

z_log, p_log = emh.vrt(X=log_prices, q=4, input_kind="log_prices")
z_ret, p_ret = emh.vrt(X=returns, q=4, input_kind="returns")

print(f"log_prices mode -> z={z_log:.6f}, p={p_log:.6f}")
print(f"returns mode -> z={z_ret:.6f}, p={p_ret:.6f}")
Loading
Loading