mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-28 21:12:16 +00:00
* merge branch develop with main * bump desktop version * fix the merge * sync Cargo.lock to desktop 2.0.0 * fix cli conftest.py --------- Co-authored-by: deeleeramone <>
154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
import asyncio
|
|
|
|
import pytest
|
|
|
|
from openbb_cftc.models.cot_movers import (
|
|
CftcCotMoversData,
|
|
CftcCotMoversFetcher,
|
|
CftcCotMoversQueryParams,
|
|
build_movers,
|
|
describe_move,
|
|
mover_score,
|
|
)
|
|
|
|
SCORED = [
|
|
{
|
|
"market": "10Y Treasury",
|
|
"report_date": "2026-07-21",
|
|
"large_specs": 18.9,
|
|
"large_specs_change": 2.0,
|
|
"small_specs": 0.0,
|
|
"small_specs_change": -1.0,
|
|
"commercials": 0.0,
|
|
"commercials_change": -22.0,
|
|
},
|
|
{
|
|
"market": "Bean Meal",
|
|
"report_date": "2026-07-21",
|
|
"large_specs": 40.0,
|
|
"large_specs_change": 3.0,
|
|
"small_specs": 66.0,
|
|
"small_specs_change": -20.0,
|
|
"commercials": None,
|
|
"commercials_change": None,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("group", "score", "change", "expected"),
|
|
[
|
|
("commercials", 95.0, 4.0, "Moved to max hedged"),
|
|
("commercials", 0.0, -22.0, "Moved to max exposed"),
|
|
("commercials", 50.0, 11.0, "Eased off max exposed"),
|
|
("commercials", 50.0, -11.0, "Eased off max hedged"),
|
|
("large_specs", 92.0, 17.0, "Pushed into one-year highs"),
|
|
("large_specs", 92.0, -2.0, "Held near one-year highs"),
|
|
("small_specs", 14.0, -11.0, "Dropped to one-year lows"),
|
|
("small_specs", 14.0, 1.0, "Held near one-year lows"),
|
|
("small_specs", 21.0, 10.0, "Lifted off one-year lows"),
|
|
("small_specs", 80.0, -9.0, "Pulled back from highs"),
|
|
],
|
|
)
|
|
def test_describe_move(group, score, change, expected):
|
|
assert describe_move(group, score, change) == expected
|
|
|
|
|
|
def test_build_movers_ranks_by_absolute_change():
|
|
movers = build_movers(SCORED, 8)
|
|
|
|
assert [m["market"] for m in movers] == ["10Y Treasury", "Bean Meal"]
|
|
assert movers[0]["change"] == -22.0
|
|
assert movers[0]["trader_group"] == "Commercials"
|
|
assert movers[0]["signal"] == "Moved to max exposed"
|
|
assert movers[1]["trader_group"] == "Small Specs"
|
|
assert movers[1]["signal"] == "Pulled back from highs"
|
|
|
|
|
|
def test_build_movers_scores_each_group_in_its_own_column():
|
|
movers = build_movers(SCORED, 8)
|
|
|
|
assert movers[0]["commercial_score"] == 0.0
|
|
assert movers[0]["spec_score"] is None
|
|
assert movers[1]["spec_score"] == 66.0
|
|
assert movers[1]["commercial_score"] is None
|
|
assert [mover_score(m) for m in movers] == [0.0, 66.0]
|
|
|
|
|
|
def test_each_score_column_carries_its_own_ramp():
|
|
fields = CftcCotMoversData.model_fields
|
|
ramp = lambda name: [ # noqa: E731
|
|
rule["color"]
|
|
for rule in fields[name].json_schema_extra["x-widget_config"]["renderFnParams"][
|
|
"colorRules"
|
|
]
|
|
]
|
|
|
|
assert ramp("spec_score") == list(reversed(ramp("commercial_score")))
|
|
assert ramp("spec_score")[-1] == "#15803d"
|
|
|
|
|
|
def test_build_movers_keeps_one_row_per_market():
|
|
movers = build_movers(SCORED, 8)
|
|
|
|
assert len({m["market"] for m in movers}) == len(movers)
|
|
|
|
|
|
def test_build_movers_skips_groups_without_a_change():
|
|
movers = build_movers(SCORED, 0)
|
|
|
|
assert all(
|
|
m["trader_group"] != "Commercials" or m["market"] != "Bean Meal" for m in movers
|
|
)
|
|
|
|
|
|
def test_build_movers_honours_the_limit():
|
|
assert len(build_movers(SCORED, 1)) == 1
|
|
|
|
|
|
def test_transform_query():
|
|
query = CftcCotMoversFetcher.transform_query({"limit": 4})
|
|
|
|
assert isinstance(query, CftcCotMoversQueryParams)
|
|
assert query.limit == 4
|
|
|
|
|
|
def test_aextract_delegates_to_the_index_fetcher(monkeypatch):
|
|
async def _extract(query, credentials, **kwargs):
|
|
return SCORED
|
|
|
|
monkeypatch.setattr(
|
|
"openbb_cftc.models.cot_index.CftcCotIndexFetcher.aextract_data", _extract
|
|
)
|
|
|
|
assert (
|
|
asyncio.run(
|
|
CftcCotMoversFetcher.aextract_data(CftcCotMoversQueryParams(), None)
|
|
)
|
|
== SCORED
|
|
)
|
|
|
|
|
|
def test_transform_data_ranks_and_summarizes():
|
|
result = CftcCotMoversFetcher.transform_data(CftcCotMoversQueryParams(), SCORED)
|
|
|
|
assert all(isinstance(r, CftcCotMoversData) for r in result.result)
|
|
assert result.result[0].market == "10Y Treasury"
|
|
assert result.result[0].trader_group == "Commercials"
|
|
assert result.result[0].signal == "Moved to max exposed"
|
|
|
|
meta = result.metadata
|
|
|
|
assert meta["report_date"] == "2026-07-21"
|
|
assert meta["markets"] == 2
|
|
assert meta["largest_move"] == -22.0
|
|
assert meta["extremes"] == 1
|
|
|
|
|
|
def test_transform_data_without_markets():
|
|
result = CftcCotMoversFetcher.transform_data(CftcCotMoversQueryParams(), [])
|
|
|
|
assert result.result == []
|
|
assert result.metadata["report_date"] is None
|
|
assert result.metadata["largest_move"] is None
|