DockerSpace / tests /test_institutional_flow_cache.py
DennisChan0909's picture
Backup current stock predictor strategies
ee37d63
Raw
History Blame Contribute Delete
5.26 kB
from collections import namedtuple
from data.institutional_flow_cache import (
fetch_daily_rows,
fetch_recent_rows_by_code,
get_fetch_status,
init_db,
upsert_daily_rows,
upsert_fetch_status,
)
from data.tw_universe import build_universe_records
def test_build_universe_records_from_synthetic_twstock_codes():
Code = namedtuple("Code", "type code name ISIN start market group CFI")
records = build_universe_records(
{
"2330": Code("股票", "2330", "台積電", "", "", "上市", "半導體業", ""),
"006201": Code("ETF", "006201", "元大富櫃50", "", "", "上櫃", "", ""),
"01001T": Code("受益證券", "01001T", "測試受益證券", "", "", "上市", "", ""),
}
)
assert records == [
{
"code": "006201",
"name": "元大富櫃50",
"market": "上櫃",
"exchange": "TPEX",
"type": "ETF",
"group": "",
"subsector": "未分類",
"subsector_path": "未分類",
"subsector_source": "industry_fallback",
"subsector_refined": False,
},
{
"code": "01001T",
"name": "測試受益證券",
"market": "上市",
"exchange": "TWSE",
"type": "受益證券",
"group": "",
"subsector": "未分類",
"subsector_path": "未分類",
"subsector_source": "industry_fallback",
"subsector_refined": False,
},
{
"code": "2330",
"name": "台積電",
"market": "上市",
"exchange": "TWSE",
"type": "股票",
"group": "半導體業",
"subsector": "晶圓代工",
"subsector_path": "半導體業 > 晶圓代工",
"subsector_source": "code_override",
"subsector_refined": True,
},
]
def test_upsert_and_fetch_daily_rows_round_trip(tmp_path):
db_path = tmp_path / "flow.sqlite3"
init_db(db_path).close()
written = upsert_daily_rows(
db_path,
[
{
"date": "2026-05-18",
"stock_no": "2330.TW",
"foreign_buy": 100,
"foreign_sell": 80,
"foreign_net": 20,
"trust_net": 3,
"dealer_net": -2,
"institutional_net": 21,
"institutional_source": "TWSE_T86",
"institutional_as_of": "2026-05-18",
},
{
"date": "2026-05-19",
"stock_no": "2330",
"foreign_net": 30,
"trust_net": 4,
"dealer_net": 5,
"institutional_net": 39,
"institutional_available": False,
"institutional_carry_forward": True,
},
],
)
rows = fetch_daily_rows(db_path, "2330.TW", "2026-05-01", "2026-05-31")
assert written == 2
assert [row["date"] for row in rows] == ["2026-05-18", "2026-05-19"]
assert rows[0]["stock_no"] == "2330"
assert rows[0]["foreign_buy"] == 100
assert rows[0]["trust_buy"] == 0
assert rows[0]["institutional_source"] == "TWSE_T86"
assert rows[0]["institutional_available"] is True
assert rows[1]["institutional_available"] is False
assert rows[1]["institutional_carry_forward"] is True
def test_upsert_daily_rows_replaces_existing_row(tmp_path):
db_path = tmp_path / "flow.sqlite3"
upsert_daily_rows(
db_path,
[{"date": "2026-05-18", "stock_no": "2330", "institutional_net": 10}],
)
upsert_daily_rows(
db_path,
[{"date": "2026-05-18", "stock_no": "2330", "institutional_net": 99}],
)
rows = fetch_daily_rows(db_path, "2330", "2026-05-18", "2026-05-18")
assert len(rows) == 1
assert rows[0]["institutional_net"] == 99
def test_fetch_status_round_trip(tmp_path):
db_path = tmp_path / "flow.sqlite3"
upsert_fetch_status(
db_path,
"TWSE_T86",
"2026-05-18",
"ok",
row_count=123,
fetched_at="2026-05-18T09:10:00+00:00",
)
assert get_fetch_status(db_path, "TWSE_T86", "2026-05-18") == {
"source": "TWSE_T86",
"date": "2026-05-18",
"status": "ok",
"fetched_at": "2026-05-18T09:10:00+00:00",
"row_count": 123,
"error": "",
}
def test_fetch_recent_rows_by_code_uses_latest_trading_rows(tmp_path):
db_path = tmp_path / "flow.sqlite3"
upsert_daily_rows(
db_path,
[
{"date": "2026-05-16", "stock_no": "2330", "institutional_net": 1},
{"date": "2026-05-17", "stock_no": "2330", "institutional_net": 2},
{"date": "2026-05-18", "stock_no": "2330", "institutional_net": 3},
{"date": "2026-05-18", "stock_no": "2454", "institutional_net": 9},
],
)
grouped = fetch_recent_rows_by_code(db_path, codes=["2330"], lookback_days=2)
assert list(grouped) == ["2330"]
assert [row["date"] for row in grouped["2330"]] == ["2026-05-17", "2026-05-18"]
assert [row["institutional_net"] for row in grouped["2330"]] == [2, 3]