问题描述
解决方案
在PTrade量化交易平台中,确保策略逻辑中的“理论持仓”与账户的“实际持仓”一致,最核心的方法是放弃记录累加式的持仓变量,转而使用“目标仓位”类交易函数。
以下是详细的解决方案和代码实现。
核心机制:使用目标仓位函数
在PTrade中,不要使用 order(按数量买卖)来管理持仓,因为网络延迟、废单或手动干预会导致策略记录的数量与实际不符。
应始终使用 order_target 系列函数。这些函数的逻辑是:“无论当前持仓是多少,请通过买入或卖出,将我的持仓调整到指定的数量”。
order_target(security, amount):调整持仓到指定股数。order_target_value(security, value):调整持仓到指定市值。get_position(security):获取当前账户真实的持仓信息。
实现逻辑
- 获取真实持仓:每次运行逻辑时,通过
get_position读取柜台/回测引擎的实时持仓。 - 计算目标:策略逻辑仅计算“我当前应该持有多少”。
- 自动调仓:调用
order_target,系统会自动计算(目标持仓 - 实际持仓)的差额并下单。
策略代码示例
以下代码展示了一个简单的策略:无论账户当前状态如何,策略始终确保账户持有 600570.SS 股票 1000股。如果账户为0股,它会买入1000股;如果账户已有1500股,它会自动卖出500股。
def initialize(context):
"""
初始化函数
"""
# 设置要操作的标的
g.security = '600570.SS'
set_universe(g.security)
# 设定目标持仓数量
g.target_amount = 1000
def handle_data(context, data):
"""
盘中运行函数
"""
# 1. 获取当前账户中该标的的实际持仓信息
# get_position返回的是Position对象,直接读取柜台/回测账户数据
position_info = get_position(g.security)
# 获取当前持仓数量
current_amount = position_info.amount
# 2. 打印调试信息(可选)
log.info("当前实际持仓: %s, 策略目标持仓: %s" % (current_amount, g.target_amount))
# 3. 使用 order_target 确保持仓一致
# 该函数会自动判断:
# - 如果 current < target,则买入 (target - current)
# - 如果 current > target,则卖出 (current - target)
# - 如果 current == target,则不进行任何操作
order_target(g.security, g.target_amount)
# 注意:如果使用 order_target_value,则是按目标市值对齐
# 例如:调整持仓市值到 50000 元
# order_target_value(g.security, 50000)
def after_trading_end(context, data):
"""
盘后处理
"""
# 盘后再次核对
pos = get_position(g.security)
log.info("盘后核对持仓 - 标的: %s, 数量: %s" % (g.security, pos.amount))
关键注意事项
-
避免使用全局变量记录持仓:
不要在initialize中定义如g.my_pos = 0然后在交易时g.my_pos += 100。一旦发生重启或漏单,g.my_pos将永远错误。始终信任get_position返回的数据。 -
未成交订单(Open Orders)的影响:
order_target计算差额时,通常基于已成交的持仓。如果此时还有未成交的挂单(Open Orders),可能会导致重复下单。- 解决方案:在下单前,先撤销所有未完成订单,或者检查是否有未完成订单。
# 进阶:下单前检查未完成订单 open_orders = get_open_orders(g.security) if open_orders: log.info("存在未完成订单,跳过本次调仓或执行撤单") # 可选:cancel_order(open_orders[0].id) else: order_target(g.security, g.target_amount) -
交易频率:
如果策略是分钟级回测或交易,order_target会在每一分钟都尝试对齐持仓。如果目标未变且持仓已对齐,API内部通常会忽略,不会产生废单,但在日志中可能会看到调用记录。 -
实盘同步延迟:
在实盘交易中,柜台回报可能有几秒的延迟。如果高频调用order_target,建议在handle_data中加入适当的逻辑判断,避免在资金未刷新时连续发单。
Q&A
Q: 为什么我的 order_target 没有下单?
A: 可能是因为当前实际持仓已经等于目标持仓,或者资金不足、标的停牌、未达到最小交易单位(如股票不足100股)。
Q: get_position 在实盘中获取的是实时的吗?
A: 是的,get_position 在交易模式下直接读取连接的柜台账户数据。但需注意柜台数据推送本身可能存在微小的延迟(通常在秒级)。
Q: 如果我手动在软件上卖出了一半股票,策略会自动买回来吗?
A: 是的。如果策略逻辑中写的是 order_target(security, 1000),而你手动卖出导致持仓变为500,下一次策略运行 handle_data 时,会检测到持仓不足,自动买入500股以补齐到1000股。
Q: 如何清空某只股票的持仓?
A: 使用 order_target(security, 0) 即可卖出该标的的所有持仓。