a500 绘图成功

This commit is contained in:
cheney
2026-06-23 10:57:20 +08:00
parent 31d28d8683
commit c4db32374e
11 changed files with 482 additions and 39 deletions
+63 -7
View File
@@ -2,12 +2,13 @@
from __future__ import annotations
import json
from datetime import datetime
from typing import List, Optional
from typing import Any, Dict, List, Optional
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from sidecar.models import KlineBar, Snapshot, parse_float
from sidecar.models import KlineBar, Snapshot, parse_date, parse_float
from sidecar.symbols import normalize_symbol
@@ -25,12 +26,23 @@ class TencentFinanceSource:
def fetch_kline(self, symbol: str, period: str, limit: int) -> List[KlineBar]:
"""
功能说明:获取 K 线数据。
参数说明:symbol 为股票代码,period 为周期,limit 为最大条数。
返回值说明:腾讯适配器暂不提供 K 线,固定抛出 NotImplementedError。
注意事项:本项目 K 线优先由 mootdx 提供。
功能说明:获取腾讯财经 K 线数据。
参数说明:symbol 为股票或指数代码,period 为周期,limit 为最大条数。
返回值说明:返回统一 KlineBar 列表。
注意事项:当前仅支持 day/week/month,用于补充指数历史 K 线。
"""
raise NotImplementedError("腾讯财经适配器暂不提供 K 线")
period_map = {"day": "day", "week": "week", "month": "month"}
tencent_period = period_map.get(period)
if tencent_period is None:
raise ValueError("腾讯财经 K 线暂不支持周期: %s" % period)
normalized = normalize_symbol(symbol)
url = "https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?" + urlencode(
{"param": "%s,%s,,,%d,qfq" % (normalized, tencent_period, limit)}
)
request = Request(url, headers={"User-Agent": "Mozilla/5.0 TortoiseSidecar/0.1"})
with urlopen(request, timeout=self.timeout_seconds) as response:
payload = response.read().decode("utf-8", errors="ignore")
return kline_from_tencent_payload(normalized, period, payload)
def fetch_snapshot(self, symbol: str) -> Snapshot:
"""
@@ -67,6 +79,40 @@ def parse_tencent_payload(payload: str) -> List[str]:
return fields
def kline_from_tencent_payload(symbol: str, period: str, payload: str) -> List[KlineBar]:
"""
功能说明:把腾讯财经 K 线 JSON 转换为统一 K 线列表。
参数说明:symbol 为标准股票代码,period 为统一周期,payload 为腾讯 K 线 JSON 文本。
返回值说明:返回 KlineBar 列表。
注意事项:腾讯字段顺序为 日期、开盘、收盘、最高、最低、成交量,部分响应可能附带成交额。
"""
data = json.loads(payload)
if data.get("code") != 0:
raise ValueError("腾讯财经 K 线响应失败: %s" % data.get("msg"))
symbol_data = data.get("data", {}).get(symbol, {})
rows = symbol_data.get(period) or symbol_data.get("qfq%s" % period) or []
bars = []
for row in rows:
trade_date = parse_date(_row_field(row, 0))
if trade_date is None:
continue
bars.append(
KlineBar(
symbol=symbol,
period=period,
trade_date=trade_date,
open=parse_float(_row_field(row, 1)),
close=parse_float(_row_field(row, 2)),
high=parse_float(_row_field(row, 3)),
low=parse_float(_row_field(row, 4)),
volume=parse_float(_row_field(row, 5)),
amount=parse_float(_row_field(row, 6)),
source="tencent",
)
)
return bars
def snapshot_from_tencent_fields(symbol: str, fields: List[str]) -> Snapshot:
"""
功能说明:把腾讯财经字段转换为统一快照。
@@ -109,6 +155,16 @@ def _field(fields: List[str], index: int) -> str:
return fields[index] if index < len(fields) else ""
def _row_field(row: List[Any], index: int) -> Any:
"""
功能说明:安全读取腾讯 K 线行字段。
参数说明:row 为腾讯 K 线数组,index 为字段索引。
返回值说明:字段存在返回原始值,不存在返回 None。
注意事项:不同标的可能缺少成交额字段。
"""
return row[index] if index < len(row) else None
def _parse_trade_time(value: str) -> Optional[datetime]:
"""
功能说明:解析腾讯交易时间字段。
+27 -28
View File
@@ -88,38 +88,37 @@ class DuckDBStore:
功能说明:批量写入 K 线缓存。
参数说明:bars 为统一 KlineBar 列表。
返回值说明:无返回值。
注意事项:相同 symbol、period、trade_date 的记录会被覆盖。
注意事项:相同 symbol、period、trade_date 的记录会被覆盖;为兼容 DuckDB 0.10 主键索引限制,先删除再插入。
"""
if not bars:
return
deduped = {}
for bar in bars:
deduped[(bar.symbol, bar.period, bar.trade_date)] = bar
bars = list(deduped.values())
now = datetime.now(timezone.utc).replace(tzinfo=None)
self.conn.execute("BEGIN TRANSACTION")
try:
for bar in bars:
self.conn.execute(
"DELETE FROM md.kline_bars WHERE symbol = ? AND period = ? AND trade_date = ?",
[bar.symbol, bar.period, bar.trade_date],
)
self.conn.execute(
"INSERT INTO md.kline_bars VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
bar.symbol,
bar.period,
bar.trade_date,
bar.open,
bar.high,
bar.low,
bar.close,
bar.volume,
bar.amount,
bar.source,
now,
],
)
self.conn.execute("COMMIT")
except Exception:
self.conn.execute("ROLLBACK")
raise
for bar in bars:
self.conn.execute(
"DELETE FROM md.kline_bars WHERE symbol = ? AND period = ? AND trade_date = ?",
[bar.symbol, bar.period, bar.trade_date],
)
for bar in bars:
self.conn.execute(
"INSERT INTO md.kline_bars VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
bar.symbol,
bar.period,
bar.trade_date,
bar.open,
bar.high,
bar.low,
bar.close,
bar.volume,
bar.amount,
bar.source,
now,
],
)
def load_kline(self, symbol: str, period: str, limit: int) -> List[KlineBar]:
"""