一、事前:订单去重键(Order Key)
为每一条下单指令生成全局唯一标识,下单前先看这个 Key 是否已经存在推荐做法:
把策略ID、交易标的、买卖方向、下单价格、时间戳这 5 个信息先拼成一个字符串,再对这个字符串做一次哈希(如 MD5、SHA256 等),得到一个固定长度、几乎不会重复的“指纹”。
这个指纹就是所谓的 订单去重键(order_key),用来唯一标识这一次下单请求。
如:
import hashlib, timedef make_order_key(strategy, symbol, side, price): raw = f"{strategy}_{symbol}_{side}_{price}_{int(time.time()*1000)}" return hashlib.md5(raw.encode()).hexdigest()
存储:
把已成功的 order_key写进Redis Set(过期时间>交易所撮合延迟),下单前用SISMEMBER判断:
if redis_client.sismember("order_keys", order_key): return None # 已存在,跳过
二、事中:本地订单簿同步(Order Book Sync)
即使 Key 不重复,网络重传或交易所 Session 复用也可能造成重单。因此要把“已报“状态保存在本地,并在任何下单动作前强制刷新。
典型流程:
1.下单前通过 REST 轮询或 WebSocket 订阅把订单簿拉到本地内存/数据库。
2.用(symbol, client_order_id)做唯一索引,若已存在且状态为 NEW/PARTIALLY_FILLED ,则拦截。
3.下单后立即把这条记录写进本地表,并标记状态PENDING等收到回报再更新。
def submit_order(symbol, price, qty, client_oid): if local_order_repo.exists(symbol, client_oid): return "DUPLICATE" order_id = exchange_api.place_order(symbol, price, qty, client_oid) local_order_repo.insert(order_id, symbol, client_oid, "PENDING")
三、事后:幂等重试 +超时回滚
即便本地已判重,仍可能因 下单后未收到回报 而误判为失败并重试。需要幂等重试策略:
def safe_place_order(params): oid = params["client_oid"] if order_repo.get_status(oid) in ["NEW", "PENDING"]: return order_repo.get_exchange_oid(oid) try: eoid = exchange.place_order(**params) order_repo.record(oid, eoid, "PENDING") return eoid except NetworkError: # 网络抖动,先查 return exchange.query_order(oid)
四、附加手段
数据库唯一索引:在 MySQL里给 client_order_id 建唯一索引,兜底防重。
滑点保护:连续 N 秒内只允许同方向同价位一单,减少重复信号
日志审计:所有下单、回报、查询写入 Kafka,实时消费做幂等校验。
有其他问题的可以评论区留言,这边看到就会解答,如果问题简单就会直接答复,问的多的会举例说明。
Copyright © 2024-2025 成都宁时科技有限公司 版权所有