行情程序刚跑起来就报错,策略没有信号,日志里却全是“请求失败”。很多人第一反应是换 IP、换 Token,结果越重试越严重。被封通常不是接口坏了,而是请求行为触发了限流、鉴权或权限规则。
下面 HTTP 行情接口为例,把判断、止损、修复和架构调整一次说清楚。
XTick 接口返回 JSON,核心字段是 code、message、data。不要只看 HTTP 状态码,先把响应体完整记录下来,再按错误码判断。
现象或错误码 | 更可能的原因 | 处理方向 |
|---|---|---|
| 短时间请求过密、并发过高或重试风暴 | 立即降频,做退避和缓存 |
| Token 填错、过期或被泄露后失效 | 在个人中心核对并更换 Token |
| 当前权限等级不包含该接口或数据 | 核对接口权限,必要时升级套餐 |
| 股票代码、日期或参数格式不符合要求 | 对参数做白名单校验 |
| URL 路径或版本写错 | 对照文档重新拼接 URL |
HTTP | 网关限流、连接数过多或网络抖动 | 停止并发重试,逐步恢复 |
判断封禁的关键,不是“失败了几次”,而是同一 Token、同一接口、同一时间窗口是否持续被拒绝。
发现连续失败后,马上暂停该接口的自动重试。固定间隔重试会形成整齐的请求波峰,指数退避才有机会让限流窗口恢复。
可以按 1 秒、2 秒、4 秒、8 秒、16 秒 逐步等待,并加上少量随机抖动。达到上限后进入冷却状态,冷却期间只保留一次健康探测。
不要让每个策略、每个进程都直接请求行情源。多个任务同时发现数据过期,会在同一秒发出几十个完全相同的请求,这就是常见的“惊群”。
高频不等于无脑高频轮询。先估算策略真正需要的数据粒度,再决定请求节奏。
场景 | 常见误区 | 更稳的做法 |
|---|---|---|
多策略读取同一股票 | 每个策略各拉一遍 | 一个采集器拉取,内部广播给策略 |
盘前、午休、收盘后 | 仍按盘中频率轮询 | 按交易时段切换频率,非交易时段暂停 |
只用最新价 | 每次拉完整历史数据 | 只请求实时字段,历史数据本地存储 |
失败后自动重试 | 所有任务同时重试 | 统一队列、指数退避、带随机抖动 |
监控多个代码 | 每个代码单独建定时器 | 批量调度,限制并发数,设全局令牌桶 |
可以把请求量粗略算出来:
每分钟请求数 = 股票数量 × 每分钟轮询次数 × 消费者数量
如果 200 只股票、每 2 秒轮询一次、3 个策略各自请求,理论上就是每分钟 18,000 次。把采集器合并成一个,再配合本地缓存,数量会立刻降一个数量级。
实时行情适合“短缓存”,不适合“无缓存”。同一标的在 200 毫秒内被多个策略读取,通常没有必要向上游请求 200 次。
实现上可以给每个 symbol 设置过期时间,并增加单飞锁:第一个任务负责刷新,其余任务等待刷新结果。Redis、进程内字典或本地 SQLite 都能承担这层缓存,关键是让所有消费者共享它。
缓存要记录时间戳。超过新鲜度阈值后再刷新,刷新失败时可以短暂返回最近一次成功值,同时把数据标记为“陈旧”,避免把网络故障伪装成实时行情。
下面这段 Python 示例只展示稳健调用思路。Token 放在环境变量里,代码中不硬编码;遇到频率超限时退避,遇到参数或权限错误时直接告警,不做无意义重试。
import os
import random
import time
from typing import Any
import requests
API\_URL = "http://api.xtick.top/api/v1/stockinfo"
TOKEN = os.environ["XTICK\_TOKEN"]
def fetch\_quote(code: str, attempts: int = 5) -> dict[str, Any]:
delay = 1.0
for attempt in range(attempts):
try:
response = requests.get(
API\_URL,
params={"token": TOKEN, "code": code},
timeout=5,
)
payload = response.json()
except (requests.RequestException, ValueError) as exc:
if attempt == attempts - 1:
raise RuntimeError(f"行情请求失败: {exc}") from exc
time.sleep(delay + random.uniform(0, 0.3))
delay = min(delay \* 2, 30)
continue
error\_code = payload.get("code", 0)
if error\_code == 0:
return payload
# 频率超限或网关 429 才退避,其余错误先修配置。
if error\_code == 5 or response.status\_code == 429:
if attempt == attempts - 1:
raise RuntimeError(f"触发限流: {payload.get('message')}")
time.sleep(delay + random.uniform(0, 0.3))
delay = min(delay \* 2, 60)
continue
if error\_code in {1, 2, 3, 4}:
raise RuntimeError(
f"不可重试错误 code={error\_code}: {payload.get('message')}"
)
raise RuntimeError(f"接口返回未知错误: {payload}")
raise RuntimeError("行情请求超过重试次数")生产环境还应增加全局限速器、连接池、请求耗时、成功率和错误码监控。不要把重试次数当成吞吐量,**重试是故障保护,不是获取更多配额的工具。**
XTick 文档说明,Token 可以在注册登录后进入个人中心查看,作为 URL 参数传入。Token 出现在 Git 仓库、日志、截图或前端代码里,就可能被他人复用,随后出现失效或异常流量。
建议把 Token 放进服务器环境变量或密钥管理服务,日志只打印脱敏后的前几位。发现泄露时,先撤销旧 Token,再检查所有部署实例是否已经更新。
不同权限等级对应不同数据范围。文档列出的等级包括青铜、白银、黄金、至尊和量化版,调用前要确认当前账号是否有目标接口权限。**权限不足不会因为换 IP 而消失,继续重试只会制造更多噪声。
做法 | 短期看起来的效果 | 实际风险 |
|---|---|---|
不停换 IP | 偶尔绕过单个出口限制 | 违反服务规则,账号和数据质量都不稳定 |
批量注册 Token | 暂时获得更多凭证 | 可能触发风控,后续统一失效 |
把并发调得更高 | 单位时间返回更多结果 | 更容易触发限流,重试成本放大 |
隐藏错误、继续写库 | 任务表面上不报错 | 把空数据或旧数据当成实时数据 |
直接切换不明数据源 | 业务暂时恢复 | 字段口径、授权和稳定性不可控 |
正确方向是减少无效请求、确认授权范围,并和服务方沟通合理的配额。涉及交易决策的数据,合规性和可追溯性比“多拿几次响应”更重要。
code、message、请求时间、接口路径和脱敏后的 Token 标识。高频行情采集的核心不是“请求越快越好”,而是**在明确配额内,用最少的请求拿到足够新鲜、可验证的数据**。先停下重试风暴,再处理 Token、权限和参数,最后用采集器、缓存和限速器重构链路,通常比换 IP 更快恢复,也更不容易再次被限制。
接口细节以XTick接口文档的当前说明为准,错误处理和请求频率要结合你的账号权限与服务协议落地。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。