Files
2026-07-09 05:08:16 +08:00

69 lines
2.1 KiB
Python

"""
Benchmark data generator for cross-library comparison.
Produces C-contiguous float64 NumPy arrays that work correctly with all
six libraries (ferro-ta, TA-Lib, pandas-ta, ta, Tulipy, finta).
Critical: every array is np.ascontiguousarray(..., dtype=np.float64) to
prevent memory segmentation faults in C-extension libraries.
"""
from __future__ import annotations
import numpy as np
import pandas as pd
_RNG = np.random.default_rng(42)
def generate_ohlcv(size: int = 10_000) -> dict[str, np.ndarray]:
"""Return a dict of C-contiguous float64 OHLCV arrays.
Uses a geometric Brownian motion walk so values are realistic (no
negatives, bounded intraday spread). Every array satisfies:
high >= close >= low > 0
open > 0
volume > 0
"""
# Geometric random walk for close
returns = _RNG.normal(0.0002, 0.01, size)
close = 100.0 * np.exp(np.cumsum(returns))
noise_hi = np.abs(_RNG.normal(0, 0.005, size)) * close
noise_lo = np.abs(_RNG.normal(0, 0.005, size)) * close
high = close + noise_hi
low = np.maximum(close - noise_lo, 0.01) # never negative
open_ = low + _RNG.random(size) * (high - low)
volume = _RNG.uniform(1e5, 1e7, size)
def _c(arr: np.ndarray) -> np.ndarray:
return np.ascontiguousarray(arr, dtype=np.float64)
return {
"open": _c(open_),
"high": _c(high),
"low": _c(low),
"close": _c(close),
"volume": _c(volume),
}
def get_pandas_ohlcv(data: dict[str, np.ndarray]) -> pd.DataFrame:
"""Convert an OHLCV dict to a DataFrame with a DatetimeIndex.
pandas-ta and finta both require a datetime-indexed DataFrame with
lowercase column names (open/high/low/close/volume).
"""
idx = pd.date_range("2015-01-01", periods=len(data["close"]), freq="D")
return pd.DataFrame(data, index=idx)
# Pre-built datasets at several scales so benchmarks can import them directly
SMALL = generate_ohlcv(1_000)
MEDIUM = generate_ohlcv(10_000)
LARGE = generate_ohlcv(100_000)
SMALL_DF = get_pandas_ohlcv(SMALL)
MEDIUM_DF = get_pandas_ohlcv(MEDIUM)
LARGE_DF = get_pandas_ohlcv(LARGE)