Python编写Binance交易算法重复发送市价单问题求解
核心问题原因
1. 无订单执行状态标记,重复触发同信号
当前逻辑仅判断最新K线的action_final值,没有记录上一次已经执行过的信号。1分钟K线的最后一行信号会在整整1分钟内保持同一个值,你每60秒触发一次循环,只要新K线还未生成,最新的action_final就还是上一次的1/-1,自然会重复发单。
你提到的action列不会连续出现1/-1是指所有历史K线的完整序列,但你每次仅取当前最后一个值,这个值会在多个循环周期里重复出现,是连续发单的核心诱因。
2. 浮点数等值判断风险
你用==直接判断浮点数action_final是否等于1/-1,diff()计算、Series转float的过程中可能出现浮点精度误差,比如原本应该是1的值变成0.999999999999999或者1.000000000000002,会导致判断逻辑不符合预期。
3. 无持仓/订单状态前置校验
你没有先查询当前是否已经持有对应仓位、是否有未成交订单,就直接发单,就算信号重复,也没有第二层拦截机制。
4. 交易所接口无异常处理
create_order调用可能因为网络波动、接口限流、余额不足等原因报错,你没有捕获异常也没有确认订单最终状态,可能出现你以为没发单成功实际上已经成交,下一次循环又重复发单的情况。
修复方案
第一步:添加已执行信号标记,避免重复触发
新增变量记录上一次执行的动作,只有当新的信号和上一次执行的动作不同,且信号为1/-1时才发单,同时补全异常处理和订单状态校验:
def bot(): # 初始化变量记录上一次执行的动作,初始为0(无操作) last_executed_action = 0 while True: tick = datetime.datetime.now() print(tick) # 从Binance导入数据,最终输出仅包含收盘价数据点的dataframe candles = client.get_historical_klines("ADAUSDT", Client.KLINE_INTERVAL_1MINUTE, "1 day ago UTC") data = pd.DataFrame({'close':np.asarray(candles)[:,4]}) data['close'] = data['close'].astype(float) # 模型生成position_signal逻辑 data['action'] = data['position_signal'].diff() # 用取整替代直接浮点数判断,规避精度问题,改用iloc明确取最后一个值 action_final = int(round(float(data['action'].iloc[-1]))) close = float(data['close'].iloc[-1]) balancebuy = float(client.get_asset_balance(asset='ADA', recvWindow=10000)['free']) balancesell = float(client.get_asset_balance(asset='USDT', recvWindow=10000)['free']) maxBuy = round(balancesell / close * .99) # 新增判断:当前信号和上一次执行的信号不同才执行 if action_final == 1 and last_executed_action != 1: # 新增异常捕获和订单状态确认 try: order = client.create_order( symbol='ADAUSDT', side=client.SIDE_BUY, type=client.ORDER_TYPE_MARKET, quantity=maxBuy, recvWindow=10000) # 只有确认订单完全成交才更新执行标记 if order['status'] == 'FILLED': last_executed_action = 1 print('Buy filled.') except Exception as e: print(f"Buy order failed: {str(e)}") elif action_final == -1 and last_executed_action != -1: try: order = client.create_order( symbol='ADAUSDT', side=client.SIDE_SELL, type=client.ORDER_TYPE_MARKET, quantity=round(balancebuy - 0.1), recvWindow=10000) if order['status'] == 'FILLED': last_executed_action = -1 print('Sell filled.') except Exception as e: print(f"Sell order failed: {str(e)}") else: print('No trade.') time.sleep(60.0 - ((time.time() - starttime) % 60.0)) bot()
第二步:新增持仓校验作为第二重拦截
发单前额外校验当前持仓状态,比如买单前确认ADA余额小于0.1(空仓状态),卖单前确认ADA余额大于1(持仓状态),进一步避免重复发单。
第三步:优化K线数据校验
拉取K线后先校验最新的K线是否已经闭合,避免使用还在生成中的未完成K线的信号,防止信号来回变动导致误发单。
内容的提问来源于stack exchange,提问作者jack sharp

