a500 绘图成功
This commit is contained in:
@@ -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
@@ -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]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user