Files
QuanTAlib/python/tests/test_pandas_ta_parity_batch_01.py
T
2026-02-28 14:14:35 -08:00

81 lines
2.7 KiB
Python

from __future__ import annotations
import numpy as np
import pandas as pd
import pandas_ta as ta
import pytest
from quantalib import indicators as q
SEED = 42
N = 10_000
VERIFY_COUNT = 100
def _generate_gbm(
n: int,
seed: int = SEED,
start_price: float = 100.0,
mu: float = 0.05,
sigma: float = 0.2,
dt: float = 1 / 252,
) -> np.ndarray:
rng = np.random.default_rng(seed)
z = rng.standard_normal(n - 1)
drift = (mu - 0.5 * sigma**2) * dt
diffusion = sigma * np.sqrt(dt) * z
log_returns = drift + diffusion
prices = np.empty(n, dtype=np.float64)
prices[0] = start_price
np.cumsum(log_returns, out=prices[1:])
prices[1:] += np.log(start_price)
np.exp(prices[1:], out=prices[1:])
prices[0] = start_price
return prices
CLOSE = _generate_gbm(N)
SERIES = pd.Series(CLOSE, name="close")
def _verify_last_n(
qtl_arr: np.ndarray,
pta_arr: np.ndarray,
*,
verify_count: int = VERIFY_COUNT,
tolerance: float = 1e-6,
label: str,
) -> None:
assert len(qtl_arr) == len(pta_arr), f"{label}: length mismatch"
start = max(0, len(qtl_arr) - verify_count)
q_tail = qtl_arr[start:]
p_tail = pta_arr[start:]
finite = np.isfinite(q_tail) & np.isfinite(p_tail)
assert int(np.sum(finite)) > 0, f"{label}: no finite overlap in tail"
diff = np.abs(q_tail[finite] - p_tail[finite])
max_diff = float(np.max(diff))
assert max_diff <= tolerance, f"{label}: max_diff={max_diff:.3e} > tol={tolerance:.1e}"
@pytest.mark.parametrize(
"name,qtl,pta,tol",
[
("rsi_14", q.rsi(CLOSE, length=14), ta.rsi(SERIES, length=14).to_numpy(), 1e-6),
("mom_10", q.mom(CLOSE, length=10), ta.mom(SERIES, length=10).to_numpy(), 1e-9),
("cmo_14", q.cmo(CLOSE, length=14), ta.cmo(SERIES, length=14, talib=False).to_numpy(), 1e-6),
("apo_12_26", q.apo(CLOSE, fast=12, slow=26), ta.apo(SERIES, fast=12, slow=26, mamode="ema", talib=False).to_numpy(), 1e-6),
("bias_26", q.bias(CLOSE, length=26), ta.bias(SERIES, length=26).to_numpy(), 1e-6),
("cfo_14", q.cfo(CLOSE, length=14), (100.0 * (SERIES - ta.linreg(SERIES, length=14, tsf=False, talib=False)) / SERIES).to_numpy(), 1e-6),
("dpo_20", q.dpo(CLOSE, length=20), ta.dpo(SERIES, length=20, centered=False).to_numpy(), 1e-6),
("trix_18", q.trix(CLOSE, length=18), ta.trix(SERIES, length=18).iloc[:, 0].to_numpy(), 1e-6),
("er_10", q.er(CLOSE, length=10), ta.er(SERIES, length=10).to_numpy(), 1e-6),
("cti_12", q.cti(CLOSE, length=12), ta.cti(SERIES, length=12).to_numpy(), 1e-6),
],
)
def test_pandas_ta_parity_batch_01(name: str, qtl: np.ndarray, pta: np.ndarray, tol: float) -> None:
_verify_last_n(qtl, pta, tolerance=tol, label=name)