rebuild(stage-8): deliver market insight workspaces
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def build_theme_library(
|
||||
trade_date: str,
|
||||
directory_rows: tuple[dict[str, Any], ...],
|
||||
daily_rows: tuple[dict[str, Any], ...],
|
||||
hot_rows: tuple[dict[str, Any], ...],
|
||||
) -> dict[str, Any]:
|
||||
daily = {str(row.get("ts_code") or ""): row for row in daily_rows}
|
||||
hot = {
|
||||
str(row.get("ts_code") or ""): int(_number(row.get("rank"), 9999))
|
||||
for row in hot_rows
|
||||
if str(row.get("data_type") or "") == "概念板块"
|
||||
}
|
||||
items = []
|
||||
for row in directory_rows:
|
||||
if str(row.get("type") or "").upper() != "N":
|
||||
continue
|
||||
if str(row.get("exchange") or "").upper() != "A":
|
||||
continue
|
||||
code = str(row.get("ts_code") or "")
|
||||
name = str(row.get("name") or "").strip()
|
||||
if not code or not name:
|
||||
continue
|
||||
quote = daily.get(code)
|
||||
items.append(
|
||||
{
|
||||
"code": code,
|
||||
"name": name,
|
||||
"member_count": int(_number(row.get("count"))),
|
||||
"change": round(_number((quote or {}).get("pct_change")), 2)
|
||||
if quote
|
||||
else None,
|
||||
"close": round(_number((quote or {}).get("close")), 3) if quote else None,
|
||||
"turnover_rate": round(_number((quote or {}).get("turnover_rate")), 2)
|
||||
if quote
|
||||
else None,
|
||||
"hot_rank": hot.get(code),
|
||||
"has_quote": bool(quote),
|
||||
}
|
||||
)
|
||||
items.sort(
|
||||
key=lambda item: (
|
||||
bool(item["has_quote"]),
|
||||
item["hot_rank"] is not None,
|
||||
-(item["hot_rank"] or 9999),
|
||||
_number(item["change"], -999),
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
quoted = [item for item in items if item["has_quote"]]
|
||||
return {
|
||||
"trade_date": trade_date,
|
||||
"summary": {
|
||||
"theme_count": len(items),
|
||||
"quoted_count": len(quoted),
|
||||
"up_count": sum(_number(item["change"]) > 0 for item in quoted),
|
||||
"down_count": sum(_number(item["change"]) < 0 for item in quoted),
|
||||
"hot_count": len(hot),
|
||||
},
|
||||
"items": items,
|
||||
}
|
||||
|
||||
|
||||
def build_theme_detail(
|
||||
trade_date: str,
|
||||
theme: dict[str, Any],
|
||||
member_rows: tuple[dict[str, Any], ...],
|
||||
daily_rows: tuple[dict[str, Any], ...],
|
||||
) -> dict[str, Any]:
|
||||
daily = {str(row.get("ts_code") or ""): row for row in daily_rows}
|
||||
members = []
|
||||
seen = set()
|
||||
for row in member_rows:
|
||||
identifier = str(row.get("con_code") or "")
|
||||
if not identifier or identifier in seen:
|
||||
continue
|
||||
seen.add(identifier)
|
||||
quote = daily.get(identifier)
|
||||
members.append(
|
||||
{
|
||||
"identifier": identifier,
|
||||
"code": identifier.split(".")[0],
|
||||
"name": str(row.get("con_name") or ""),
|
||||
"change": round(_number((quote or {}).get("pct_chg")), 2)
|
||||
if quote
|
||||
else None,
|
||||
"close": round(_number((quote or {}).get("close")), 2) if quote else None,
|
||||
"amount": _number((quote or {}).get("amount")) * 1000 if quote else None,
|
||||
"quoted": bool(quote),
|
||||
}
|
||||
)
|
||||
members.sort(
|
||||
key=lambda item: (
|
||||
bool(item["quoted"]),
|
||||
_number(item["change"], -999),
|
||||
_number(item["amount"]),
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
quoted = [item for item in members if item["quoted"]]
|
||||
return {
|
||||
"trade_date": trade_date,
|
||||
"theme": theme,
|
||||
"summary": {
|
||||
"member_count": len(members),
|
||||
"quoted_count": len(quoted),
|
||||
"up_count": sum(_number(item["change"]) > 0 for item in quoted),
|
||||
"down_count": sum(_number(item["change"]) < 0 for item in quoted),
|
||||
"turnover_rate": theme.get("turnover_rate"),
|
||||
},
|
||||
"members": members,
|
||||
}
|
||||
|
||||
|
||||
def _number(value: Any, default: float = 0.0) -> float:
|
||||
try:
|
||||
number = float(value)
|
||||
return number if number == number else default
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
Reference in New Issue
Block a user