mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 14:24:15 +08:00
Merge pull request #294 from kevin9327/fix/overview-dimension-leader-zero-pct
fix(overview): 板块领涨股不再把 0.00% 当成缺失涨跌幅
This commit is contained in:
@@ -237,6 +237,16 @@ def _symbol_keys(row: dict, config: ExtConfig) -> list[str]:
|
||||
return keys
|
||||
|
||||
|
||||
def _leader_sort_key(row: dict) -> float:
|
||||
"""领涨股排序键: 缺涨跌幅的成分股排最后。
|
||||
|
||||
0.00% 是有效涨跌幅, 不能与"无行情"合并成同一个哨兵值 —— 板块整体下跌时
|
||||
平盘股就是领涨股。
|
||||
"""
|
||||
value = _finite(row.get("change_pct"))
|
||||
return value if value is not None else float("-inf")
|
||||
|
||||
|
||||
def _dimension_rank(rows: list[dict], repo, kind: str, limit: int = 5, level: int | None = None) -> dict:
|
||||
if not rows:
|
||||
return {"leading": [], "lagging": []}
|
||||
@@ -281,7 +291,7 @@ def _dimension_rank(rows: list[dict], repo, kind: str, limit: int = 5, level: in
|
||||
changes = [v for v in changes if v is not None]
|
||||
if not changes:
|
||||
continue
|
||||
leader = max(stocks, key=lambda s: _finite(s.get("change_pct")) or -999)
|
||||
leader = max(stocks, key=_leader_sort_key)
|
||||
items.append({
|
||||
"name": name,
|
||||
"count": len(stocks),
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"""市场总览「领涨股」选取: 涨跌幅 0.00% 是有效值, 不能被当成缺失值。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import polars as pl
|
||||
|
||||
from app.services.market_overview_builder import _dimension_rank
|
||||
|
||||
|
||||
def _fake_repo(tmp_path):
|
||||
import types
|
||||
|
||||
return types.SimpleNamespace(store=types.SimpleNamespace(data_dir=tmp_path))
|
||||
|
||||
|
||||
def _write_concept_ext(tmp_path, mapping: dict[str, str]) -> None:
|
||||
"""写一张 snapshot 模式的概念扩展表 (symbol → 所属概念)。"""
|
||||
cfg_dir = tmp_path / "ext_data" / "concept_tbl"
|
||||
cfg_dir.mkdir(parents=True, exist_ok=True)
|
||||
cfg_dir.joinpath("config.json").write_text(
|
||||
json.dumps({
|
||||
"id": "concept_tbl",
|
||||
"label": "概念表",
|
||||
"mode": "snapshot",
|
||||
"fields": [{"name": "所属概念", "dtype": "string", "label": "所属概念"}],
|
||||
}, ensure_ascii=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
pl.DataFrame({
|
||||
"symbol": list(mapping.keys()),
|
||||
"所属概念": list(mapping.values()),
|
||||
}).write_parquet(cfg_dir / "part.parquet")
|
||||
|
||||
|
||||
def test_flat_stock_can_be_leader_of_a_falling_concept(tmp_path):
|
||||
"""全概念下跌、最强的一只恰好平盘(0.00%)时, 领涨股必须是那只平盘股。"""
|
||||
_write_concept_ext(tmp_path, {
|
||||
"000001.SZ": "人工智能",
|
||||
"000002.SZ": "人工智能",
|
||||
"000003.SZ": "人工智能",
|
||||
})
|
||||
rows = [
|
||||
{"symbol": "000001.SZ", "name": "跌一", "change_pct": -0.01, "amount": 1e8},
|
||||
{"symbol": "000002.SZ", "name": "平盘", "change_pct": 0.0, "amount": 2e8},
|
||||
{"symbol": "000003.SZ", "name": "跌三", "change_pct": -0.03, "amount": 3e8},
|
||||
]
|
||||
|
||||
result = _dimension_rank(rows, _fake_repo(tmp_path), "concept")
|
||||
|
||||
items = {item["name"]: item for item in result["lagging"]}
|
||||
assert "人工智能" in items
|
||||
leader = items["人工智能"]["leader"]
|
||||
assert leader["name"] == "平盘"
|
||||
assert leader["change_pct"] == 0.0
|
||||
|
||||
|
||||
def test_leader_falls_back_to_none_pct_last(tmp_path):
|
||||
"""change_pct 缺失(None)的成分股仍排在所有有值的成分股之后。"""
|
||||
_write_concept_ext(tmp_path, {
|
||||
"000001.SZ": "芯片",
|
||||
"000002.SZ": "芯片",
|
||||
})
|
||||
rows = [
|
||||
{"symbol": "000001.SZ", "name": "无行情", "change_pct": None, "amount": 1e8},
|
||||
{"symbol": "000002.SZ", "name": "微跌", "change_pct": -0.02, "amount": 1e8},
|
||||
]
|
||||
|
||||
result = _dimension_rank(rows, _fake_repo(tmp_path), "concept")
|
||||
|
||||
items = {item["name"]: item for item in result["lagging"]}
|
||||
assert items["芯片"]["leader"]["name"] == "微跌"
|
||||
|
||||
|
||||
def test_leader_still_picks_max_when_all_positive(tmp_path):
|
||||
"""普通情形不受影响: 全部上涨时仍取涨幅最大的一只。"""
|
||||
_write_concept_ext(tmp_path, {
|
||||
"000001.SZ": "光伏",
|
||||
"000002.SZ": "光伏",
|
||||
})
|
||||
rows = [
|
||||
{"symbol": "000001.SZ", "name": "涨多", "change_pct": 0.05, "amount": 1e8},
|
||||
{"symbol": "000002.SZ", "name": "涨少", "change_pct": 0.01, "amount": 1e8},
|
||||
]
|
||||
|
||||
result = _dimension_rank(rows, _fake_repo(tmp_path), "concept")
|
||||
|
||||
items = {item["name"]: item for item in result["leading"]}
|
||||
assert items["光伏"]["leader"]["name"] == "涨多"
|
||||
Reference in New Issue
Block a user