Flask应用Webhook重复执行问题:TradingView重复止损致额外持仓
解决TradingView重复Webhook导致Flask重复执行订单的问题
现有代码的核心问题
你的代码已经尝试用锁和缓存拦截重复请求,但没处理好两个关键环节:
- 检查到缓存无重复后,没把当前请求的标识存入缓存,导致第二个并发请求进来时依然能通过检查,触发重复平仓。
time.sleep(0.7)放在锁内会阻塞其他请求,完全没必要,反而拖慢响应速度。
修正后的代码方案
from flask import Flask, request import json from threading import Lock from datetime import datetime, timedelta app = Flask(__name__) lock = Lock() # 用字典实现带过期时间的缓存,存储格式:{请求唯一标识: 过期时间} request_cache = {} def clean_expired_cache(): """清理过期的缓存条目,避免内存占用过高""" current_time = datetime.now() # 筛选出已过期的缓存键 expired_keys = [key for key, expire_at in request_cache.items() if current_time > expire_at] for key in expired_keys: del request_cache[key] @app.route('/webhook_pivot', methods=['POST']) def webhook(): # 解析请求数据 data = json.loads(request.data) # 生成请求的唯一标识(按key排序后序列化,确保相同内容生成相同字符串) req_identifier = json.dumps(data, sort_keys=True) side = data['side'] qty = int(data['qty']) * int(lot) # 假设lot是你已定义的全局变量 sym = data.get('symbol') # 假设请求中包含symbol字段,用于持仓检查 if side == "BUY": # 买入订单执行代码 pass elif side == "FULL_EXIT_BUY" and check_symbol_and_position(sym): with lock: # 先清理过期缓存,避免无效条目干扰判断 clean_expired_cache() # 检查是否是重复请求 if req_identifier in request_cache: print("重复消息,忽略") return "Duplicate data, ignoring", 200 # 标记当前请求为已处理,设置10秒过期(覆盖TradingView重复发送的时间窗口) request_cache[req_identifier] = datetime.now() + timedelta(seconds=10) # 平仓逻辑放在锁外,避免阻塞其他请求的处理 # 这里写入你的BUY EXIT order执行代码 print("执行平仓操作") return "Success", 200 # 你的持仓检查辅助函数(示例) def check_symbol_and_position(symbol): # 实现你原本的持仓检查逻辑,返回布尔值 return True
关键优化点说明
- 原子化缓存操作:在锁内完成「检查缓存-写入缓存」的操作,确保并发请求不会同时通过重复校验。
- 缓存过期机制:给缓存条目设置10秒过期时间,既可以拦截短时间内的重复Webhook,又不会让缓存无限占用内存。
- 锁范围优化:只在缓存操作时加锁,平仓逻辑放在锁外,避免不必要的请求阻塞,提升服务的并发处理能力。
额外可选优化
- 如果TradingView允许自定义Webhook内容,建议给每个告警加唯一
request_id字段,用这个ID作为缓存键,比序列化整个请求数据更高效。 - 如果你部署Flask时用了多进程(比如gunicorn多worker),内存缓存会不共享,这时可以换成Redis等外部缓存来存储请求标识。
内容的提问来源于stack exchange,提问作者Gurjant Singh
相关产品推荐
相关产品推荐

