rebuild(stage-7): deliver ladder and sector rotation

This commit is contained in:
leefer
2026-07-30 03:28:24 +08:00
parent 31a53de890
commit a76d344a98
28 changed files with 1161 additions and 13 deletions
+70 -1
View File
@@ -16,6 +16,7 @@ def build_snapshot(
limits = _pool(_rows(inputs, "limit_up"), "涨停")
broken = _pool(_rows(inputs, "broken"), "炸板")
down_limits = _pool(_rows(inputs, "limit_down"), "跌停")
previous_limits = _pool(_rows(inputs, "previous_limit_up"), "涨停")
price_limits = {str(row.get("ts_code") or ""): row for row in _rows(inputs, "price_limits")}
for row in broken:
up_limit = _number(price_limits.get(row["identifier"], {}).get("up_limit"))
@@ -24,7 +25,7 @@ def build_snapshot(
)
yesterday = _yesterday(
_pool(_rows(inputs, "previous_limit_up"), "涨停"),
previous_limits,
daily,
limits,
broken,
@@ -35,6 +36,8 @@ def build_snapshot(
flat_count = len(daily_rows) - up_count - down_count
amount = sum(_number(row.get("amount")) * 1000 for row in daily_rows)
seal_rate = len(limits) / max(len(limits) + len(broken), 1) * 100
sectors = _sectors(limits)
previous_sectors = _sectors(previous_limits)
overview = {
"up_count": up_count,
"down_count": down_count,
@@ -54,6 +57,9 @@ def build_snapshot(
"down_limits": down_limits,
"yesterday_limits": yesterday,
"limit_performance": _performance(yesterday),
"ladders": _ladders(limits),
"sectors": sectors,
"sector_rotation": _rotation(sectors, previous_sectors),
}
@@ -161,6 +167,69 @@ def _performance(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
return result
def _ladders(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
levels = sorted({int(row["streak"]) for row in rows}, reverse=True)
return [
{
"level": level,
"label": "首板" if level == 1 else f"{level}",
"count": sum(int(row["streak"]) == level for row in rows),
"stocks": sorted(
(row for row in rows if int(row["streak"]) == level),
key=lambda row: row.get("first_time") or "99:99",
),
}
for level in levels
]
def _sectors(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
names = sorted({str(row.get("sector") or "").strip() for row in rows} - {""})
result = []
for name in names:
stocks = [row for row in rows if str(row.get("sector") or "").strip() == name]
leader = max(stocks, key=lambda row: (int(row["streak"]), _number(row["amount"])))
max_streak = max(int(row["streak"]) for row in stocks)
count = len(stocks)
result.append(
{
"name": name,
"count": count,
"strength": min(100, 44 + count * 8 + max_streak * 5),
"amount": round(sum(_number(row["amount"]) for row in stocks), 2),
"leader": leader["name"],
"representative": leader["identifier"],
"change": round(mean(_number(row["change"]) for row in stocks), 2),
"max_streak": max_streak,
}
)
return sorted(
result,
key=lambda row: (int(row["count"]), int(row["max_streak"]), _number(row["amount"])),
reverse=True,
)[:20]
def _rotation(
current: list[dict[str, Any]], previous: list[dict[str, Any]]
) -> list[dict[str, Any]]:
previous_map = {str(row["name"]): row for row in previous}
result = []
for rank, sector in enumerate(current[:12], start=1):
previous_count = int(previous_map.get(str(sector["name"]), {}).get("count") or 0)
delta = int(sector["count"]) - previous_count
result.append(
{
**sector,
"rank": rank,
"previous_count": previous_count,
"delta": delta,
"trend": "升温" if delta > 0 else "降温" if delta < 0 else "持平",
}
)
return result
def _number(value: Any, default: float = 0.0) -> float:
try:
number = float(value)