Files
xiaobaifupan/app/backend/data/providers/tushare_stocks.py
T

138 lines
6.1 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any
from backend.bootstrap.config import display_compact_date as _display_date
from backend.data.numbers import finite_number as _number
class StockMixin:
def stock_detail(self, ts_code: str, requested_date: str) -> dict[str, Any]:
trade_date, _ = self.resolve_trade_context(requested_date)
end = datetime.strptime(trade_date, "%Y%m%d")
start_date = (end - timedelta(days=190)).strftime("%Y%m%d")
daily = self.query(
"daily",
{"ts_code": ts_code, "start_date": start_date, "end_date": trade_date},
"ts_code,trade_date,open,high,low,close,pct_chg,vol,amount",
)
factors = self.query(
"adj_factor",
{"ts_code": ts_code, "start_date": start_date, "end_date": trade_date},
"ts_code,trade_date,adj_factor",
)
basics = self.query(
"stock_basic",
{"ts_code": ts_code},
"ts_code,symbol,name,area,industry,market,list_date",
)
daily_basics = self.query(
"daily_basic",
{"ts_code": ts_code, "trade_date": trade_date},
"ts_code,trade_date,turnover_rate,volume_ratio,total_mv,circ_mv",
)
moneyflow = self.query(
"moneyflow",
{"ts_code": ts_code, "trade_date": trade_date},
"ts_code,trade_date,buy_sm_amount,sell_sm_amount,buy_md_amount,sell_md_amount,"
"buy_lg_amount,sell_lg_amount,buy_elg_amount,sell_elg_amount,net_mf_amount",
)
factor_map = {row["trade_date"]: _number(row.get("adj_factor"), 1) for row in factors}
latest_factor = max(factor_map.values(), default=1) or 1
prices = []
for row in sorted(daily, key=lambda item: item.get("trade_date", ""))[-90:]:
factor = factor_map.get(row.get("trade_date"), latest_factor)
ratio = factor / latest_factor
prices.append(
{
"trade_date": _display_date(str(row.get("trade_date", ""))),
"open": round(_number(row.get("open")) * ratio, 3),
"high": round(_number(row.get("high")) * ratio, 3),
"low": round(_number(row.get("low")) * ratio, 3),
"close": round(_number(row.get("close")) * ratio, 3),
"change": _number(row.get("pct_chg")),
"volume": _number(row.get("vol")),
"amount_billion": round(_number(row.get("amount")) / 100000, 2),
}
)
flow = moneyflow[0] if moneyflow else {}
basic = basics[0] if basics else {}
daily_basic = daily_basics[0] if daily_basics else {}
latest = prices[-1] if prices else {}
actual_trade_date = max(
(str(row.get("trade_date") or "") for row in daily),
default=trade_date,
) or trade_date
return {
"meta": {
"requested_date": _display_date(requested_date),
"trade_date": _display_date(actual_trade_date),
"source": "tushare",
"updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
"notice": "",
},
"stock": {
"code": ts_code.split(".")[0],
"ts_code": ts_code,
"name": basic.get("name") or "--",
"industry": basic.get("industry") or "其他",
"area": basic.get("area") or "--",
"market": basic.get("market") or "--",
"list_date": _display_date(str(basic.get("list_date") or "")),
"price": latest.get("close", 0),
"change": latest.get("change", 0),
"turnover_rate": _number(daily_basic.get("turnover_rate")),
"volume_ratio": _number(daily_basic.get("volume_ratio")),
"amount_billion": latest.get("amount_billion", 0),
},
"prices": prices,
"moneyflow": {
"net_million": round(_number(flow.get("net_mf_amount")) / 100, 2),
"large_million": round(
(_number(flow.get("buy_lg_amount")) + _number(flow.get("buy_elg_amount"))
- _number(flow.get("sell_lg_amount")) - _number(flow.get("sell_elg_amount"))) / 100,
2,
),
"medium_million": round(
(_number(flow.get("buy_md_amount")) - _number(flow.get("sell_md_amount"))) / 100,
2,
),
"small_million": round(
(_number(flow.get("buy_sm_amount")) - _number(flow.get("sell_sm_amount"))) / 100,
2,
),
},
}
def stock_intraday(self, ts_code: str, requested_date: str) -> dict[str, Any]:
trade_date, _ = self.resolve_trade_context(requested_date)
display_date = _display_date(trade_date)
rows = self.query(
"stk_mins",
{
"ts_code": ts_code,
"freq": "1min",
"start_date": f"{display_date} 09:00:00",
"end_date": f"{display_date} 15:30:00",
},
"ts_code,trade_time,open,close,high,low,vol,amount",
)
points = []
for row in sorted(rows, key=lambda item: str(item.get("trade_time") or "")):
trade_time = str(row.get("trade_time") or "")
if not trade_time:
continue
points.append(
{
"time": trade_time[-8:-3] if len(trade_time) >= 8 else trade_time,
"open": round(_number(row.get("open")), 3),
"high": round(_number(row.get("high")), 3),
"low": round(_number(row.get("low")), 3),
"close": round(_number(row.get("close")), 3),
"volume": _number(row.get("vol")),
"amount": _number(row.get("amount")),
}
)
return {"trade_date": display_date, "points": points}