You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python下使用WebSocket与SQLAlchemy积累SQL数据及避免K线重复

问题原因

导致K线重复入库的核心原因有两点:

  1. SQLite表未对时间维度设置唯一性约束,相同时间的K线数据可被重复写入
  2. 现有代码中WebSocket的使用姿势错误,加上网络波动重连时币安会重推最近的K线闭合通知,导致同一条闭合消息被多次处理

解决方案

方案1:数据库层面加唯一约束(最稳妥)

建表时将time字段设为唯一主键,从底层直接拒绝重复时间的数据写入,无需在业务代码中做额外判断。
首次运行时先执行一次建表语句添加约束:

from sqlalchemy import text

# 仅需执行一次,后续运行可注释
with engine.connect() as conn:
    conn.execute(text("""
    CREATE TABLE IF NOT EXISTS BTCUSDT (
        time TEXT PRIMARY KEY,
        open REAL,
        high REAL,
        low REAL,
        close REAL,
        volume REAL
    )
    """))
    conn.commit()

后续写入时自动遵循主键约束,重复数据会被自动忽略,可配合INSERT OR IGNORE语法避免冲突报错。

方案2:新增本地缓存校验(性能最优)

在内存中记录上一次成功入库的K线起始时间戳(即消息内k.t字段),每次收到闭合K线时先比对时间戳,相同则直接跳过写入流程:

last_saved_kline_ts = 0  # 记录上次入库的K线起始时间戳

async with socket as sock:
    while True:
        msg = await sock.recv()
        kline_data = msg['k']
        # 仅处理闭合且未入库的K线
        if kline_data['x'] and kline_data['t'] != last_saved_kline_ts:
            frame = createframe(msg)
            frame.to_sql(kline_data['s'], engine, if_exists='append', index=False)
            last_saved_kline_ts = kline_data['t'] # 更新缓存时间戳

方案3:修正WebSocket连接逻辑

原有代码中每次循环都调用await socket.__aenter__()属于错误用法,会导致连接反复重连,重复接收相同消息。正确的异步WebSocket使用方式应将上下文管理器放在循环外层:

# 全局创建一次连接,不要在循环内重复初始化
async with bsm.kline_socket('BTCUSDT', interval=KLINE_INTERVAL_1MINUTE) as socket:
    last_saved_ts = 0
    while True:
        msg = await socket.recv()
        k = msg['k']
        if k['x'] and k['t'] != last_saved_ts:
            frame = createframe(msg)
            frame.to_sql(k['s'], engine, if_exists='append', index=False)
            last_saved_ts = k['t']

内容的提问来源于stack exchange,提问作者Keyvan Tajbakhsh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 17:09:01