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
+62
View File
@@ -107,6 +107,68 @@ class DataGateway:
row = self._repository.latest_summary(connection, context.actual_date)
return {"context": context, "values": json.loads(str(row["payload_json"])) if row else None}
def snapshot_inputs(
self, trade_date: str, previous_trade_date: str
) -> dict[str, Any]:
provider = self._provider(DataSource.TUSHARE)
self._policy.assert_allowed(provider.source, DataUsage.CALCULATION)
return provider.snapshot_inputs(trade_date, previous_trade_date)
def sector_members(
self, trade_date: str, sector_name: str, representative: str
) -> dict[str, Any]:
with self._database.read() as connection:
cached = self._repository.sector_members(connection, trade_date, sector_name)
if cached:
return json.loads(str(cached["payload_json"]))
provider = self._provider(DataSource.TUSHARE)
self._policy.assert_allowed(provider.source, DataUsage.CALCULATION)
result = provider.sector_members(representative, trade_date)
if not result.rows:
raise MarketDataUnavailable("该板块暂无可核验的申万成分股")
rows = sorted(
(dict(row) for row in result.rows),
key=lambda row: (
bool(row.get("quoted")),
_number(row.get("change")),
_number(row.get("amount")),
),
reverse=True,
)
payload = {
"trade_date": trade_date,
"sector_name": str(rows[0].get("sector_name") or sector_name),
"sector_code": str(rows[0].get("sector_code") or ""),
"member_count": len(rows),
"quoted_count": sum(bool(row.get("quoted")) for row in rows),
"coverage": round(result.metadata.coverage, 4),
"items": [
{
"identifier": str(row.get("ts_code") or ""),
"code": str(row.get("ts_code") or "").split(".")[0],
"name": str(row.get("name") or ""),
"change": row.get("change"),
"open": row.get("open"),
"close": row.get("close"),
"amount": row.get("amount"),
"quoted": bool(row.get("quoted")),
}
for row in rows
],
}
with self._database.transaction() as connection:
self._repository.save_sector_members(
connection,
trade_date=trade_date,
sector_name=sector_name,
sector_code=payload["sector_code"],
observed_at=result.metadata.observed_at.isoformat(timespec="seconds"),
source=result.metadata.source.value,
coverage=result.metadata.coverage,
payload=payload,
)
return payload
def search(self, query: str) -> tuple[MarketEntity, ...]:
with self._database.read() as connection:
return self._repository.search(connection, query)