rebuild(stage-7): deliver ladder and sector rotation
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user