fix(sector-radar): 兼容股票市场字段为空
This commit is contained in:
@@ -202,7 +202,8 @@ def _is_lifecycle_candidate(stock: StockBasicRow, target: date) -> bool:
|
|||||||
return False
|
return False
|
||||||
if stock.symbol.startswith(("200", "900")):
|
if stock.symbol.startswith(("200", "900")):
|
||||||
return False
|
return False
|
||||||
if "北交" in stock.market or "B股" in stock.market.upper():
|
market = stock.market or ""
|
||||||
|
if "北交" in market or "B股" in market.upper():
|
||||||
return False
|
return False
|
||||||
if stock.list_date is None or stock.list_date > target:
|
if stock.list_date is None or stock.list_date > target:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ class StockBasicRow:
|
|||||||
ts_code: str
|
ts_code: str
|
||||||
symbol: str
|
symbol: str
|
||||||
name: str
|
name: str
|
||||||
market: str
|
market: str | None
|
||||||
exchange: str
|
exchange: str
|
||||||
list_status: str
|
list_status: str
|
||||||
list_date: date | None
|
list_date: date | None
|
||||||
@@ -356,7 +356,7 @@ class StockBasicRow:
|
|||||||
ts_code=_required_text(row, "ts_code"),
|
ts_code=_required_text(row, "ts_code"),
|
||||||
symbol=_required_text(row, "symbol"),
|
symbol=_required_text(row, "symbol"),
|
||||||
name=_required_text(row, "name"),
|
name=_required_text(row, "name"),
|
||||||
market=_required_text(row, "market"),
|
market=_optional_text(row, "market"),
|
||||||
exchange=_required_text(row, "exchange"),
|
exchange=_required_text(row, "exchange"),
|
||||||
list_status=_required_text(row, "list_status"),
|
list_status=_required_text(row, "list_status"),
|
||||||
list_date=_source_date(row, "list_date", required=False),
|
list_date=_source_date(row, "list_date", required=False),
|
||||||
|
|||||||
@@ -36,12 +36,17 @@ def result[T](api_name: str, rows: tuple[T, ...]) -> SourceResult[T]:
|
|||||||
return SourceResult((snapshot,), rows)
|
return SourceResult((snapshot,), rows)
|
||||||
|
|
||||||
|
|
||||||
def basic(ts_code: str, *, list_date: date = date(2020, 1, 1)) -> StockBasicRow:
|
def basic(
|
||||||
|
ts_code: str,
|
||||||
|
*,
|
||||||
|
list_date: date = date(2020, 1, 1),
|
||||||
|
market: str | None = "主板",
|
||||||
|
) -> StockBasicRow:
|
||||||
return StockBasicRow(
|
return StockBasicRow(
|
||||||
ts_code=ts_code,
|
ts_code=ts_code,
|
||||||
symbol=ts_code.split(".")[0],
|
symbol=ts_code.split(".")[0],
|
||||||
name=ts_code,
|
name=ts_code,
|
||||||
market="主板",
|
market=market,
|
||||||
exchange="SZSE",
|
exchange="SZSE",
|
||||||
list_status="L",
|
list_status="L",
|
||||||
list_date=list_date,
|
list_date=list_date,
|
||||||
@@ -49,6 +54,28 @@ def basic(ts_code: str, *, list_date: date = date(2020, 1, 1)) -> StockBasicRow:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_market_keeps_hs_a_stock_but_code_rules_still_exclude_bse_and_b_shares() -> None:
|
||||||
|
codes = ("000001.SZ", "920001.BJ", "200001.SZ", "900001.SH")
|
||||||
|
basics = tuple(basic(code, market=None) for code in codes)
|
||||||
|
daily_rows = tuple(daily(code, Decimal("1")) for code in codes)
|
||||||
|
moneyflow_rows = tuple(moneyflow(code, Decimal("1")) for code in codes)
|
||||||
|
|
||||||
|
facts = normalize_stock_facts(
|
||||||
|
target_trade_date=TARGET_DATE,
|
||||||
|
candidate_codes=codes,
|
||||||
|
stock_basics=result("stock_basic", basics),
|
||||||
|
suspensions=result("suspend_d", ()),
|
||||||
|
daily=result("daily", daily_rows),
|
||||||
|
moneyflow=result("moneyflow_dc", moneyflow_rows),
|
||||||
|
)
|
||||||
|
by_code = {fact.ts_code: fact for fact in facts}
|
||||||
|
|
||||||
|
assert by_code["000001.SZ"].status is StockFactStatus.AVAILABLE
|
||||||
|
assert by_code["920001.BJ"].status is StockFactStatus.LIFECYCLE_INVALID
|
||||||
|
assert by_code["200001.SZ"].status is StockFactStatus.LIFECYCLE_INVALID
|
||||||
|
assert by_code["900001.SH"].status is StockFactStatus.LIFECYCLE_INVALID
|
||||||
|
|
||||||
|
|
||||||
def daily(ts_code: str, amount: Decimal | None) -> DailyRow:
|
def daily(ts_code: str, amount: Decimal | None) -> DailyRow:
|
||||||
return DailyRow(
|
return DailyRow(
|
||||||
ts_code=ts_code,
|
ts_code=ts_code,
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ def test_stock_basic_explicitly_requests_all_lifecycle_statuses() -> None:
|
|||||||
"ts_code": f"00000{index}.SZ",
|
"ts_code": f"00000{index}.SZ",
|
||||||
"symbol": f"00000{index}",
|
"symbol": f"00000{index}",
|
||||||
"name": status,
|
"name": status,
|
||||||
"market": "主板",
|
"market": None if status == "D" else "主板",
|
||||||
"exchange": "SZSE",
|
"exchange": "SZSE",
|
||||||
"list_status": status,
|
"list_status": status,
|
||||||
"list_date": "20200101",
|
"list_date": "20200101",
|
||||||
@@ -329,6 +329,7 @@ def test_stock_basic_explicitly_requests_all_lifecycle_statuses() -> None:
|
|||||||
result = make_adapter(client).fetch_stock_basics()
|
result = make_adapter(client).fetch_stock_basics()
|
||||||
|
|
||||||
assert {row.list_status for row in result.rows} == {"L", "D", "P", "G", "UN"}
|
assert {row.list_status for row in result.rows} == {"L", "D", "P", "G", "UN"}
|
||||||
|
assert next(row for row in result.rows if row.list_status == "D").market is None
|
||||||
assert [call[1]["list_status"] for call in client.calls] == ["L", "D", "P", "G", "UN"]
|
assert [call[1]["list_status"] for call in client.calls] == ["L", "D", "P", "G", "UN"]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user