Tortoise/sidecar/sources/tencent.py
2026-06-23 10:37:31 +08:00

124 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""腾讯财经数据源适配器。"""
from __future__ import annotations
from datetime import datetime
from typing import List, Optional
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from sidecar.models import KlineBar, Snapshot, parse_float
from sidecar.symbols import normalize_symbol
class TencentFinanceSource:
"""腾讯财经行情适配器。"""
def __init__(self, timeout_seconds: int = 8) -> None:
"""
功能说明:创建腾讯财经数据源。
参数说明timeout_seconds 为 HTTP 请求超时时间。
返回值说明:无返回值。
注意事项:腾讯接口无需 Key但字段为 GBK 编码和波浪线分隔。
"""
self.timeout_seconds = timeout_seconds
def fetch_kline(self, symbol: str, period: str, limit: int) -> List[KlineBar]:
"""
功能说明:获取 K 线数据。
参数说明symbol 为股票代码period 为周期limit 为最大条数。
返回值说明:腾讯适配器暂不提供 K 线,固定抛出 NotImplementedError。
注意事项:本项目 K 线优先由 mootdx 提供。
"""
raise NotImplementedError("腾讯财经适配器暂不提供 K 线")
def fetch_snapshot(self, symbol: str) -> Snapshot:
"""
功能说明:获取腾讯财经实时行情快照。
参数说明symbol 为股票代码。
返回值说明:返回统一 Snapshot 模型。
注意事项PB 使用索引 46PE_TTM 使用索引 39避免常见错误映射。
"""
normalized = normalize_symbol(symbol)
query = urlencode({"q": normalized})
request = Request(
"https://qt.gtimg.cn/?" + query,
headers={"User-Agent": "Mozilla/5.0 TortoiseSidecar/0.1"},
)
with urlopen(request, timeout=self.timeout_seconds) as response:
text = response.read().decode("gbk", errors="ignore")
fields = parse_tencent_payload(text)
return snapshot_from_tencent_fields(normalized, fields)
def parse_tencent_payload(payload: str) -> List[str]:
"""
功能说明:解析腾讯财经原始响应。
参数说明payload 为形如 v_sh600000="..." 的响应文本。
返回值说明:返回按波浪线拆分后的字段列表。
注意事项:响应为空或格式异常时抛出 ValueError。
"""
if '="' not in payload:
raise ValueError("腾讯财经响应格式异常")
content = payload.split('="', 1)[1].split('"', 1)[0]
fields = content.split("~")
if len(fields) < 53:
raise ValueError("腾讯财经响应字段不足")
return fields
def snapshot_from_tencent_fields(symbol: str, fields: List[str]) -> Snapshot:
"""
功能说明:把腾讯财经字段转换为统一快照。
参数说明symbol 为标准股票代码fields 为腾讯响应字段列表。
返回值说明:返回 Snapshot 模型。
注意事项字段索引按项目文档校准43 是振幅不是 PB。
"""
return Snapshot(
symbol=symbol,
name=_field(fields, 1) or None,
trade_time=_parse_trade_time(_field(fields, 30)),
price=parse_float(_field(fields, 3)),
previous_close=parse_float(_field(fields, 4)),
open=parse_float(_field(fields, 5)),
high=parse_float(_field(fields, 33)),
low=parse_float(_field(fields, 34)),
volume=parse_float(_field(fields, 6)),
amount=parse_float(_field(fields, 37)),
change=parse_float(_field(fields, 31)),
change_percent=parse_float(_field(fields, 32)),
turnover_rate=parse_float(_field(fields, 38)),
pe_ttm=parse_float(_field(fields, 39)),
pe_static=parse_float(_field(fields, 52)),
pb=parse_float(_field(fields, 46)),
market_cap=parse_float(_field(fields, 44)),
float_market_cap=parse_float(_field(fields, 45)),
limit_up=parse_float(_field(fields, 47)),
limit_down=parse_float(_field(fields, 48)),
source="tencent",
)
def _field(fields: List[str], index: int) -> str:
"""
功能说明:安全读取腾讯字段。
参数说明fields 为字段列表index 为字段索引。
返回值说明:字段存在返回原始字符串,不存在返回空字符串。
注意事项:腾讯字段偶发缺失时可避免 IndexError。
"""
return fields[index] if index < len(fields) else ""
def _parse_trade_time(value: str) -> Optional[datetime]:
"""
功能说明:解析腾讯交易时间字段。
参数说明value 为 YYYYMMDDHHMMSS 格式字符串。
返回值说明:解析成功返回 datetime空值或异常返回 None。
注意事项:返回值不带时区,表示交易所本地时间。
"""
if not value:
return None
try:
return datetime.strptime(value[:14], "%Y%m%d%H%M%S")
except ValueError:
return None