mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 20:42:26 +00:00
* refactor openbb-technical for v5 * forward refs * linting * no python 3.15 in test matrix * reame * ty * move all params to body --------- Co-authored-by: deeleeramone <>
127 lines
3.9 KiB
Python
127 lines
3.9 KiB
Python
"""Tests for ``openbb_technical.multi.compose``."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from openbb_technical.multi.compose import (
|
|
MultiIndicatorRequest,
|
|
MultiQueryParams,
|
|
MultiResultRow,
|
|
_numeric_columns,
|
|
_resolve_indicator,
|
|
_row_key,
|
|
_to_records,
|
|
multi,
|
|
)
|
|
|
|
|
|
class TestResolveIndicator:
|
|
def test_resolves_known(self):
|
|
fn = _resolve_indicator("rsi")
|
|
assert callable(fn)
|
|
|
|
def test_skip_on_missing(self):
|
|
assert _resolve_indicator("___definitely_not_a_real_indicator___") is None
|
|
|
|
|
|
class TestHelpers:
|
|
def test_to_records_passthrough_dict(self):
|
|
rows = _to_records([{"a": 1}])
|
|
assert rows == [{"a": 1}]
|
|
|
|
def test_to_records_from_pydantic(self):
|
|
class _Row:
|
|
def model_dump(self):
|
|
return {"a": 2}
|
|
|
|
assert _to_records([_Row()]) == [{"a": 2}]
|
|
|
|
def test_to_records_unwraps_obbject(self):
|
|
class _OBB:
|
|
results = [{"a": 3}]
|
|
|
|
assert _to_records(_OBB()) == [{"a": 3}]
|
|
|
|
def test_row_key_uses_index(self):
|
|
assert _row_key({"date": "2021-01-01", "x": 1}, "date") == "2021-01-01"
|
|
|
|
def test_numeric_columns_drops_ohlc(self):
|
|
row = {
|
|
"date": "2021-01-01",
|
|
"open": 1.0,
|
|
"high": 1.0,
|
|
"low": 1.0,
|
|
"close": 1.0,
|
|
"volume": 1.0,
|
|
"rsi": 55.0,
|
|
"label": "tag",
|
|
}
|
|
out = _numeric_columns(row, target="close")
|
|
assert "rsi" in out
|
|
assert "label" not in out
|
|
assert "open" not in out
|
|
|
|
def test_numeric_columns_passes_none(self):
|
|
row = {"rsi": None}
|
|
out = _numeric_columns(row, target="close")
|
|
assert out == {"rsi": None}
|
|
|
|
|
|
class TestMultiEndpoint:
|
|
def test_single_indicator(self, single_symbol_records):
|
|
out = multi(
|
|
MultiQueryParams(
|
|
data=single_symbol_records,
|
|
indicators=[
|
|
MultiIndicatorRequest(indicator="rsi", params={"length": 14})
|
|
],
|
|
)
|
|
)
|
|
assert out.results
|
|
assert all(isinstance(r, MultiResultRow) for r in out.results)
|
|
assert any(r.values.get("rsi.rsi") is not None for r in out.results)
|
|
|
|
def test_two_indicators_merge(self, single_symbol_records):
|
|
out = multi(
|
|
MultiQueryParams(
|
|
data=single_symbol_records,
|
|
indicators=[
|
|
MultiIndicatorRequest(indicator="rsi", params={"length": 14}),
|
|
MultiIndicatorRequest(indicator="atr", params={"length": 14}),
|
|
],
|
|
)
|
|
)
|
|
assert out.results
|
|
for row in out.results:
|
|
if "rsi.rsi" in row.values and "atr.atr" in row.values:
|
|
assert row.values["rsi.rsi"] is not None
|
|
assert row.values["atr.atr"] is not None
|
|
break
|
|
else: # pragma: no cover - sanity guard, fixture has 300 bars
|
|
pytest.fail("Never found a row where both indicators warmed up.")
|
|
|
|
def test_unknown_indicator_skipped(self, single_symbol_records):
|
|
out = multi(
|
|
MultiQueryParams(
|
|
data=single_symbol_records,
|
|
indicators=[
|
|
MultiIndicatorRequest(indicator="not_a_real_indicator", params={}),
|
|
MultiIndicatorRequest(indicator="rsi", params={"length": 14}),
|
|
],
|
|
)
|
|
)
|
|
assert out.results
|
|
assert all(
|
|
all(not k.startswith("not_a_real_indicator.") for k in r.values)
|
|
for r in out.results
|
|
)
|
|
|
|
def test_query_params_defaults(self, single_symbol_records):
|
|
params = MultiQueryParams(
|
|
data=single_symbol_records,
|
|
indicators=[MultiIndicatorRequest(indicator="rsi")],
|
|
)
|
|
assert params.index == "date"
|
|
assert params.target == "close"
|