CashFlow / tests /conftest.py
lhllamlam's picture
feat: wire 4 institutional flow factors (L2, options, ticks, intraday); stub data committed
6b66ac0 verified
Raw
History Blame Contribute Delete
4.04 kB
"""Shared pytest fixtures.
Every test gets an isolated ``tmp_path`` and we monkeypatch every path
attribute in :mod:`scanner.paths` to point inside that directory. Since
every consuming module reads paths via ``paths.X`` (rather than
``from .paths import X``) this is sufficient to isolate disk I/O per
test - no module reload tricks required.
"""
from __future__ import annotations
import os
from datetime import datetime, timedelta
import numpy as np
import pandas as pd
import pytest
from scanner import paths
@pytest.fixture(autouse=True)
def isolated_paths(tmp_path, monkeypatch):
"""Redirect every persistent path under :mod:`scanner.paths` into ``tmp_path``."""
history_dir = tmp_path / "history"
history_dir.mkdir()
monkeypatch.setattr(paths, "TMP_DIR", str(tmp_path))
monkeypatch.setattr(paths, "HISTORY_DIR", str(history_dir))
monkeypatch.setattr(paths, "CACHE_PATH", str(tmp_path / "ohlcv.parquet"))
monkeypatch.setattr(paths, "SECTOR_CACHE_PATH",
str(tmp_path / "sectors.parquet"))
monkeypatch.setattr(paths, "WATCHLIST_PATH",
str(tmp_path / "watchlist.json"))
monkeypatch.setattr(paths, "LEARNED_WEIGHTS_PATH",
str(tmp_path / "learned.json"))
monkeypatch.setattr(paths, "PERFORMANCE_LOG_PATH",
str(tmp_path / "perf.parquet"))
monkeypatch.setattr(paths, "RESULTS_CSV_PATH",
str(tmp_path / "results.csv"))
monkeypatch.setattr(paths, "L2_CACHE_PATH",
str(tmp_path / "l2.parquet"))
monkeypatch.setattr(paths, "OPTIONS_CACHE_PATH",
str(tmp_path / "options.parquet"))
monkeypatch.setattr(paths, "TICK_CACHE_DIR",
str(tmp_path / "ticks"))
monkeypatch.setattr(paths, "INTRADAY_CACHE_DIR",
str(tmp_path / "intraday"))
monkeypatch.setattr(paths, "STUB_DIR",
str(tmp_path / "stubs"))
yield
# ---------------------------------------------------------------------------
# Synthetic OHLCV helpers
# ---------------------------------------------------------------------------
def _business_days(n: int, end: datetime | None = None) -> list[datetime]:
end = end or datetime(2026, 1, 30)
out = []
d = end
while len(out) < n:
if d.weekday() < 5:
out.append(d)
d -= timedelta(days=1)
return list(reversed(out))
def make_uptrend(n: int = 120, start_price: float = 50.0,
daily_drift: float = 0.003,
vol_base: int = 1_000_000,
seed: int = 1) -> pd.DataFrame:
"""Generate a synthetic OHLCV frame with a clear up-trend and increasing
on-balance volume (close > prev_close most days)."""
rng = np.random.default_rng(seed)
dates = _business_days(n)
close = [start_price]
for _ in range(1, n):
ret = daily_drift + rng.normal(0, 0.008)
close.append(close[-1] * (1.0 + ret))
close = np.array(close)
open_ = close * (1 + rng.normal(0, 0.002, size=n))
high = np.maximum(open_, close) * (1 + np.abs(rng.normal(0, 0.005, size=n)))
low = np.minimum(open_, close) * (1 - np.abs(rng.normal(0, 0.005, size=n)))
vol = (vol_base * (1 + rng.normal(0, 0.2, size=n))).clip(1e4).astype(int)
df = pd.DataFrame({"Date": dates, "Open": open_, "High": high,
"Low": low, "Close": close, "Volume": vol})
return df
def make_downtrend(**kwargs) -> pd.DataFrame:
kwargs.setdefault("daily_drift", -0.003)
kwargs.setdefault("seed", 2)
return make_uptrend(**kwargs)
def make_flat(**kwargs) -> pd.DataFrame:
kwargs.setdefault("daily_drift", 0.0)
kwargs.setdefault("seed", 3)
return make_uptrend(**kwargs)
@pytest.fixture
def uptrend_frame() -> pd.DataFrame:
return make_uptrend()
@pytest.fixture
def downtrend_frame() -> pd.DataFrame:
return make_downtrend()
@pytest.fixture
def flat_frame() -> pd.DataFrame:
return make_flat()