Files
xiaobai-review/xiaobai-datahub/tests/test_normalize.py
T
3498dd7a4b feat(HEL-382): 搭建 datahub 底座和盘后正式数据链路
新增独立 xiaobai-datahub 服务(SQLite WAL、Tushare 盘后发布、/v1 契约和管理后台),不改现站页面与数据链路。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-02 12:05:26 +08:00

79 lines
3.0 KiB
Python

from __future__ import annotations
import unittest
from datahub.normalize import (
AMOUNT_THOUSAND_YUAN,
AMOUNT_WAN_YUAN,
VOLUME_LOT,
apply_qfq,
normalize_auction,
normalize_daily,
normalize_index_daily,
normalize_moneyflow,
normalize_valuation,
review_daily_to_canonical,
)
from tests.fixtures import RAW
class NormalizeTests(unittest.TestCase):
def test_daily_matches_architecture_and_review_native_conversion(self) -> None:
raw = RAW["daily"][0]
hub = normalize_daily(raw, adj_factor=1.1)
# review stores Tushare native units; canonical = review * factor
review_native = dict(raw)
converted = review_daily_to_canonical(review_native)
self.assertEqual(hub["amount"], converted["amount"])
self.assertEqual(hub["amount"], raw["amount"] * AMOUNT_THOUSAND_YUAN)
self.assertEqual(hub["volume"], raw["vol"] * VOLUME_LOT)
self.assertEqual(hub["close"], 10.2)
self.assertEqual(hub["adj_factor"], 1.1)
self.assertEqual(hub["ts_code"], "600000.SH")
def test_moneyflow_wan_to_yuan(self) -> None:
raw = RAW["moneyflow"][0]
hub = normalize_moneyflow(raw)
self.assertEqual(hub["net_mf_amount"], raw["net_mf_amount"] * AMOUNT_WAN_YUAN)
self.assertEqual(hub["buy_lg_amount"], 30 * AMOUNT_WAN_YUAN)
def test_valuation_mv_wan_to_yuan(self) -> None:
raw = RAW["daily_basic"][0]
hub = normalize_valuation(raw)
self.assertEqual(hub["total_mv"], 1000 * AMOUNT_WAN_YUAN)
self.assertEqual(hub["circ_mv"], 800 * AMOUNT_WAN_YUAN)
def test_index_daily_amount_thousand_yuan(self) -> None:
raw = RAW["index_daily"][0]
hub = normalize_index_daily(raw)
self.assertEqual(hub["amount"], raw["amount"] * AMOUNT_THOUSAND_YUAN)
self.assertEqual(hub["volume"], raw["vol"] * VOLUME_LOT)
def test_auction_amount_already_yuan(self) -> None:
raw = RAW["stk_auction"][0]
hub = normalize_auction(raw)
self.assertEqual(hub["amount"], raw["amount"])
self.assertEqual(hub["volume"], raw["vol"] * VOLUME_LOT)
def test_field_diff_against_review_native_is_explained(self) -> None:
"""Golden: every non-zero diff vs review-native daily is a documented unit factor."""
raw = RAW["daily"][0]
hub = normalize_daily(raw)
diffs = {}
for key in ("open", "high", "low", "close", "pct_chg"):
if hub[key] != raw[key]:
diffs[key] = (raw[key], hub[key])
self.assertEqual(diffs, {})
self.assertNotEqual(hub["amount"], raw["amount"])
self.assertEqual(hub["amount"] / raw["amount"], AMOUNT_THOUSAND_YUAN)
self.assertEqual(hub["volume"] / raw["vol"], VOLUME_LOT)
def test_qfq_formula(self) -> None:
self.assertEqual(apply_qfq(10.0, 1.1, 2.2), 5.0)
none_price = apply_qfq(None, 1.1, 2.2)
self.assertIsNone(none_price)
if __name__ == "__main__":
unittest.main()