You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
from libs.intraday.features import (
|
|
compute_average_range,
|
|
compute_average_true_range,
|
|
compute_entropy_approx,
|
|
compute_gap_zscore,
|
|
enrich_daily_bars,
|
|
)
|
|
|
|
|
|
def _daily_bar(day: str, open_: float, high: float, low: float, close: float, volume: float = 1_000_000.0) -> dict:
|
|
return {
|
|
"date": day,
|
|
"open": open_,
|
|
"high": high,
|
|
"low": low,
|
|
"close": close,
|
|
"volume": volume,
|
|
}
|
|
|
|
|
|
def test_entropy_approx_is_bounded() -> None:
|
|
bars = []
|
|
close = 100.0
|
|
for idx in range(30):
|
|
close *= 1.0 + (0.01 if idx % 2 == 0 else -0.008)
|
|
bars.append(_daily_bar(f"2024-01-{idx+1:02d}", close * 0.99, close * 1.01, close * 0.98, close))
|
|
|
|
entropy = compute_entropy_approx(bars, lookback=20)
|
|
assert entropy is not None
|
|
assert 0.0 <= entropy <= 1.0
|
|
|
|
|
|
def test_enrich_daily_bars_populates_new_research_features() -> None:
|
|
ticker = "AAA"
|
|
bars = []
|
|
close = 100.0
|
|
for idx in range(70):
|
|
date_str = f"2024-03-{idx+1:02d}" if idx < 31 else f"2024-04-{idx-30:02d}"
|
|
gap = 0.002 if idx % 3 == 0 else -0.001
|
|
open_ = close * (1.0 + gap)
|
|
high = open_ * 1.02
|
|
low = open_ * 0.99
|
|
close = open_ * (1.0 + (0.004 if idx % 2 == 0 else -0.003))
|
|
bars.append(_daily_bar(date_str, open_, high, low, close))
|
|
|
|
trading_day = bars[-1]["date"]
|
|
enriched = enrich_daily_bars({ticker: bars}, [trading_day])
|
|
features = enriched[ticker][trading_day]
|
|
|
|
assert features["entropy_20d"] is not None
|
|
assert 0.0 <= features["entropy_20d"] <= 1.0
|
|
assert features["atr_ratio_10_60"] is not None
|
|
assert features["range_compression_10_60"] is not None
|
|
assert features["gap_zscore_20d"] is not None
|
|
|
|
|
|
def test_gap_zscore_and_range_helpers_return_values() -> None:
|
|
bars = []
|
|
close = 50.0
|
|
for idx in range(65):
|
|
open_ = close * (1.0 + 0.002)
|
|
high = open_ * 1.03
|
|
low = open_ * 0.98
|
|
close = open_ * 1.001
|
|
bars.append(_daily_bar(f"2024-05-{idx+1:02d}", open_, high, low, close))
|
|
|
|
assert compute_average_true_range(bars, 10) is not None
|
|
assert compute_average_range(bars, 10) is not None
|
|
assert compute_gap_zscore(bars[:-1], today_open=bars[-1]["open"], lookback=20) is not None
|