【PTrade】为什么回测能赚钱,实盘却无法下单?7个排查步骤与诊断代码
2026/8/29 10:28:13 网站建设 项目流程

本方案由EasyQuant AI量化助手提供。

问题背景

很多策略在 PTrade 回测中能正常产生收益,切换到实盘后却没有任何委托,或者日志显示下单了但始终没有成交。

这通常不代表策略逻辑一定错误,而是回测与实盘在交易时间、账户资金、涨跌停、停牌、可卖数量、交易权限和成交规则上存在差异。PTrade 提供了is_trade()get_open_orders()get_stock_status()check_limit()等接口,可用于定位问题。PTrade API 文档

先区分三种情况

  1. 根本没有产生交易信号;

  2. 产生了信号,但没有成功提交委托;

  3. 委托已提交,但没有成交或被撤销。

排查时不要只看收益曲线,应把每一步都打印到策略日志中。

常见原因

1. 实盘策略没有真正运行

回测点击“运行”即可执行,实盘还需要确认策略已启动、绑定了正确账户、交易终端保持登录,且处于允许交易的时间段。

if is_trade(): log.info("当前为实盘环境") else: log.info("当前为回测环境")

2. 信号触发时间与回测不同

日线回测与实盘的handle_data执行时间可能不同。若策略依赖“当天收盘价”,实盘盘中可能尚未满足条件,导致没有下单。

建议记录策略运行时间和信号值:

def handle_data(context, data): security = "510300.SS" price = data[security].price log.info("当前时间:%s,最新价:%.3f" % ( context.current_dt, price ))

3. 回测使用了未来数据

回测中若误用了当天收盘后的数据、未正确设置财务数据日期,容易形成“看到了未来”的信号;实盘自然无法复现。

财务数据、调仓日和指标计算都应确保只使用当前时点以前可获得的数据。PTrade 数据接口说明

4. 实盘可用资金不足

回测通常使用设定的初始资金;实盘必须使用账户中的真实可用资金,还要预留手续费和滑点空间。

def check_cash_before_buy(context, data, security, amount): price = data[security].price estimated_cash = price * amount * 1.002 if context.portfolio.cash < estimated_cash: log.info( "资金不足:可用资金=%.2f,预计需要=%.2f" % (context.portfolio.cash, estimated_cash) ) return False return True

5. 股票停牌、ST、退市或不在可交易状态

回测数据中可能存在历史价格,但实盘时股票可能停牌、退市,或策略需要主动过滤 ST 标的。

def check_stock_status(security): halt_info = get_stock_status([security], "HALT") st_info = get_stock_status([security], "ST") if halt_info.get(security, False): log.info("%s 当前停牌,跳过下单" % security) return False if st_info.get(security, False): log.info("%s 为ST股票,策略不交易" % security) return False return True

6. 股票涨跌停,委托无法成交

买入时涨停、卖出时跌停,可能有委托但无法成交。交易模块中可通过check_limit()判断涨跌停状态。

def check_limit_status(security, side): limit_status = check_limit(security) # 1:涨停;2:触及涨停但仍有卖盘 if side == "buy" and limit_status in [1, 2]: log.info("%s 接近或处于涨停,不追买" % security) return False # -1:跌停;-2:触及跌停但仍有买盘 if side == "sell" and limit_status in [-1, -2]: log.info("%s 接近或处于跌停,卖出可能无法成交" % security) return False return True

7. 卖出数量超过可卖数量

A 股通常存在 T+1 限制:当天买入的部分无法在当天卖出。下单前需要检查可卖持仓,而不是只看总持仓。

def check_sellable_position(security, sell_amount): position = get_position(security) if position is None: log.info("%s 无持仓,无法卖出" % security) return False enable_amount = getattr(position, "enable_amount", 0) if enable_amount < sell_amount: log.info( "%s 可卖数量不足:可卖=%s,计划卖出=%s" % (security, enable_amount, sell_amount) ) return False return True

完整诊断下单模板

def initialize(context): g.security = "510300.SS" set_universe([g.security]) def can_place_order(context, data, security, amount): if amount == 0: log.info("下单数量为0,跳过") return False if not check_stock_status(security): return False side = "buy" if amount > 0 else "sell" if not check_limit_status(security, side): return False # 买入前检查资金 if amount > 0: if not check_cash_before_buy(context, data, security, amount): return False # 卖出前检查可卖数量 if amount < 0: if not check_sellable_position(security, abs(amount)): return False # 避免同一标的存在未完成委托时重复下单 open_orders = get_open_orders(security) if open_orders: log.info("%s 存在未完成委托,暂不重复下单" % security) return False return True def handle_data(context, data): security = g.security current_price = data[security].price # 示例信号:价格大于10元时尝试买入100股 buy_signal = current_price > 10.0 log.info( "时间=%s,标的=%s,价格=%.3f,买入信号=%s" % (context.current_dt, security, current_price, buy_signal) ) if not buy_signal: return amount = 100 if not can_place_order(context, data, security, amount): return order_id = order(security, amount) if order_id: log.info("委托已提交,订单号:%s" % order_id) else: log.info("委托提交失败,请检查交易日志、账户权限和参数")

实盘排查清单

  • 策略是否已经启动并绑定正确资金账户;

  • 当前是否为交易日、交易时段;

  • 实盘是否真的触发了买卖信号;

  • 股票代码、市场后缀和下单数量是否正确;

  • 是否有足够可用资金;

  • 卖出数量是否小于等于可卖数量;

  • 标的是否停牌、ST、涨跌停;

  • 是否存在未完成委托;

  • 委托提交后,是否继续查询订单状态与成交状态。

总结

回测赚钱但实盘无法下单,最常见的原因不是策略收益失效,而是交易条件没有满足或委托没有通过真实交易环境的校验。将信号、资金、持仓、股票状态、涨跌停和订单状态逐项记录,通常可以快速定位问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询