问题描述
解决方案
在PTrade中实现国债逆回购(Reverse Repo)的自动化交易,核心逻辑是在每日收盘前(通常是14:50~15:00之间),检查账户的闲置资金,并将其“卖出”(借给市场)以获取利息。
策略核心逻辑
- 交易方向:国债逆回购在交易系统中属于卖出操作(即融出资金),因此在调用
order函数时,数量amount必须为负数。 - 标的选择:
- 沪市(GC系列):门槛较高,10万元起购(1000张)。常用代码:
204001.SS(1天期)。 - 深市(R系列):门槛较低,1000元起购(10张)。常用代码:
131810.SZ(1天期)。
- 沪市(GC系列):门槛较高,10万元起购(1000张)。常用代码:
- 交易单位:1张 = 100元面值。
- 执行时间:建议设置在 14:55 左右,此时当天的股票交易基本结束,可以最大化利用闲置资金,且避免错过收盘。
完整的PTrade策略代码
以下代码实现了一个自动化的闲置资金管理策略。它会优先尝试进行沪市逆回购(如果资金大于10万),剩余资金或不足10万的资金会尝试进行深市逆回购。
def initialize(context):
"""
策略初始化函数
"""
# 设置沪市1天期国债逆回购代码 (门槛10万)
g.gc_code = '204001.SS'
# 设置深市1天期国债逆回购代码 (门槛1000元)
g.r_code = '131810.SZ'
# 设定保留资金(可选):账户中不参与逆回购的最小保留金额
g.reserve_cash = 0.0
# 每天 14:55 运行闲置资金管理函数
# 注意:逆回购通常在尾盘操作,以免影响日内正常的股票买入
run_daily(context, manage_idle_cash, time='14:55')
log.info("国债逆回购策略已启动,将在每日 14:55 自动管理闲置资金")
def manage_idle_cash(context):
"""
闲置资金管理逻辑
"""
# 1. 获取当前账户可用资金
available_cash = context.portfolio.cash
# 减去预留资金
tradable_cash = available_cash - g.reserve_cash
log.info("当前可用资金: %.2f, 可用于逆回购资金: %.2f" % (available_cash, tradable_cash))
if tradable_cash < 1000:
log.info("资金不足1000元,无法进行逆回购交易")
return
# 2. 优先尝试沪市逆回购 (GC001)
# 沪市门槛:10万元 (1000张 * 100元/张)
# 交易单位:必须是10万元的整数倍
if tradable_cash >= 100000:
# 计算最大可交易的张数 (1000张的倍数)
# 逻辑:(资金 / 100000) 取整 * 1000张
gc_amount = int(tradable_cash / 100000) * 1000
if gc_amount > 0:
# 获取当前行情快照,用于日志记录或限价参考
snapshot = get_snapshot(g.gc_code)
current_rate = snapshot[g.gc_code]['last_px']
# 下单:注意 amount 必须为负数,表示“卖出”资金
# limit_price 不传则默认使用最新价
order(g.gc_code, -gc_amount)
log.info("执行沪市逆回购: %s, 数量: %d张, 利率: %.3f" % (g.gc_code, gc_amount, current_rate))
# 更新剩余资金 (粗略计算,未扣除极少的手续费)
tradable_cash -= (gc_amount * 100)
# 3. 剩余资金尝试深市逆回购 (R-001)
# 深市门槛:1000元 (10张 * 100元/张)
# 交易单位:必须是1000元的整数倍
if tradable_cash >= 1000:
# 计算最大可交易的张数 (10张的倍数)
# 逻辑:(资金 / 1000) 取整 * 10张
r_amount = int(tradable_cash / 1000) * 10
if r_amount > 0:
# 获取当前行情快照
snapshot = get_snapshot(g.r_code)
current_rate = snapshot[g.r_code]['last_px']
# 下单:注意 amount 必须为负数
order(g.r_code, -r_amount)
log.info("执行深市逆回购: %s, 数量: %d张, 利率: %.3f" % (g.r_code, r_amount, current_rate))
def handle_data(context, data):
"""
盘中运行函数(本策略主要依赖 run_daily 定时任务,此处留空即可)
"""
pass
代码关键点解析
-
run_daily(..., time='14:55'):- 这是最关键的设置。我们不希望在上午或下午开盘时就锁死资金,因为那样会导致无法买入股票。通常选择在收盘前5-10分钟进行操作。
-
order(code, -amount):- 负数 Amount: 在PTrade和大多数国内量化柜台中,国债逆回购被视为“卖出资金”的行为,因此下单数量必须是负数。
- 不设置
limit_price: 代码中未传递limit_price参数,系统会默认以当前快照的最新价(即实时利率)进行委托,这在尾盘抢单时能保证较高的成交率。
-
数量计算逻辑:
- 沪市 (204001.SS): 最小交易额10万元。计算公式为
int(资金 / 100000) * 1000。这里1000代表1000张(即1000手,每手100元面值)。 - 深市 (131810.SZ): 最小交易额1000元。计算公式为
int(资金 / 1000) * 10。这里10代表10张。
- 沪市 (204001.SS): 最小交易额10万元。计算公式为
-
资金优先级:
- 代码逻辑是优先满足沪市的大额逆回购(因为通常沪市利率波动更稳定且资金利用率高),剩下的“零钱”再通过深市逆回购处理,最大化资金利用率。
注意事项
- 手续费: 国债逆回购的手续费极低(通常十万分之一),代码中计算剩余资金时未精确扣除手续费,但在实际交易中,柜台会校验资金是否足够支付手续费,因此
int取整的方式通常是安全的。 - 节假日效应: 在长假(如春节、国庆)前的倒数第二个交易日,1天期逆回购通常能享受到多天的利息,是操作的黄金时间。
- 回测限制: 在回测模式下,逆回购的撮合逻辑可能与实盘略有差异(主要体现在成交价格和资金即时冻结上),建议主要用于实盘或模拟盘。
Q&A
Q1: 为什么下单数量 amount 必须是负数?
A1: 在金融交易系统中,国债逆回购的本质是你将资金“借出”给需要资金的一方,并获得债券作为质押。这个过程在交易指令上被定义为“卖出”资金(Sell),因此在PTrade API中,卖出操作对应的数量需要用负数表示。
Q2: 为什么选择 14:55 而不是 15:00?
A2: 15:00 是市场收盘时间,此时已无法申报新的委托。14:55 是尾盘集合竞价前的最后连续竞价阶段,此时操作既能确保当天的股票交易机会已过(资金确实闲置),又能保证有足够的时间让订单成交。
Q3: 如果我有深市股东账户但没有沪市股东账户,代码会报错吗?
A3: order 函数在资金不足或账户权限不足时通常会返回 None 或在日志中记录错误,而不会直接导致策略崩溃停止运行。不过,为了严谨,建议您确认开通了对应的交易权限。上述代码采用了分步尝试的逻辑,如果沪市下单失败(例如没有权限),它会继续尝试深市下单。