问题描述
解决方案
这是一个基于 VWAP(成交量加权平均价) 的日内交易策略设计。
策略原理
VWAP(Volume Weighted Average Price)是衡量日内价格趋势的重要指标。
- 计算公式:$\text{VWAP} = \frac{\sum(\text{价格} \times \text{成交量})}{\sum \text{成交量}} = \frac{\text{当日累计成交额}}{\text{当日累计成交量}}$
- 核心逻辑:
- 当 当前价格 > VWAP 时,市场被视为处于多头趋势(买方力量强),策略选择 买入。
- 当 当前价格 < VWAP 时,市场被视为处于空头趋势(卖方力量强),策略选择 卖出。
- 日内风控:在收盘前(如 14:55)强制平仓,确保不持仓过夜(针对 T+0 品种或模拟日内做T)。
注意事项(A股 T+1 限制)
由于中国 A 股市场实行 T+1 交易制度(当日买入的股票次日才能卖出),要在 A 股实现“日内交易”,通常有两种方法:
- 底仓做 T:账户里预先持有该股票(底仓),当日买入后,卖出原本持有的份额;或者先卖出底仓,低位再买回。
- 交易 T+0 品种:如 可转债 或 ETF(部分跨境/债券/黄金 ETF)。
下面的代码示例采用“底仓做 T”的逻辑编写,即在 initialize 中设置底仓,以便在回测中模拟日内买卖。
PTrade 策略代码实现
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数,设置股票池、全局变量和底仓
"""
# 1. 设置要操作的标的(这里以恒生电子为例)
g.security = '600570.SS'
set_universe(g.security)
# 2. 设置每次交易的金额
g.trade_value = 50000
# 3. 设定日内止盈止损阈值(可选)
# 价格偏离 VWAP 超过一定比例才操作,避免震荡磨损
g.threshold = 0.005 # 0.5%
# 4. 【关键】设置底仓以支持 T+0 回测
# 实际交易中需要账户本身有持仓
# 格式:[{'sid':代码, 'amount':总数量, 'enable_amount':可用数量, 'cost_basis':成本}]
pos = {
'sid': g.security,
'amount': 10000,
'enable_amount': 10000,
'cost_basis': 40.0
}
set_yesterday_position([pos])
# 5. 记录当日是否已经开仓的标记,避免频繁开仓
g.has_open_position = False
def before_trading_start(context, data):
"""
盘前处理,每天开盘前重置变量
"""
g.has_open_position = False
log.info("盘前准备:重置交易状态")
def handle_data(context, data):
"""
盘中运行函数,分钟级别回测每分钟执行一次
"""
current_dt = context.blotter.current_dt
security = g.security
# ---------------------------------------------------
# 1. 时间控制:收盘前强制平仓 (14:55)
# ---------------------------------------------------
if current_dt.hour == 14 and current_dt.minute >= 55:
position = get_position(security)
# 如果今日有买入操作导致持仓增加,或者为了恢复到底仓数量
# 这里简单逻辑:恢复到初始底仓 10000 股,或者清空当日买入的部分
# 为简化演示,这里逻辑设为:如果持仓 > 0,全部卖出(针对纯日内)
# 或者:如果是做T策略,应该在此处将持仓调整回昨仓数量
# 这里演示纯日内逻辑:收盘平掉所有当日新增仓位
# 实际底仓做T逻辑:order_target(security, 10000)
return
# ---------------------------------------------------
# 2. 获取当日历史分钟数据计算 VWAP
# ---------------------------------------------------
# 获取当天开盘截至目前的分钟线数据
# 240分钟是4小时交易时间的最大值,确保覆盖全天
# include=True 表示包含当前这一分钟的数据
his_df = get_history(240, '1m', ['money', 'volume'], security, include=True)
# 过滤出今天的行(get_history可能返回昨天的数据如果刚开盘)
today_date = current_dt.date()
today_data = his_df[his_df.index.date == today_date]
if len(today_data) == 0:
return
# 计算累计成交额和累计成交量
cum_money = today_data['money'].sum()
cum_volume = today_data['volume'].sum()
if cum_volume == 0:
return
# 计算 VWAP
vwap = cum_money / cum_volume
# ---------------------------------------------------
# 3. 获取当前价格
# ---------------------------------------------------
current_price = data[security]['close']
# ---------------------------------------------------
# 4. 交易逻辑
# ---------------------------------------------------
# 获取当前持仓信息
position_info = get_position(security)
curr_amount = position_info.amount
enable_amount = position_info.enable_amount # 可卖数量(底仓)
# 策略逻辑:
# 如果 现价 > VWAP * (1 + 阈值) --> 看多,买入
# 如果 现价 < VWAP * (1 - 阈值) --> 看空,卖出
# 买入信号
if current_price > vwap * (1 + g.threshold):
# 限制:如果没有开过仓,且资金充足
if not g.has_open_position:
order_value(security, g.trade_value)
g.has_open_position = True
log.info("买入信号: 现价 %.2f > VWAP %.2f, 买入 %s" % (current_price, vwap, g.trade_value))
# 卖出信号 (需要有底仓才能卖)
elif current_price < vwap * (1 - g.threshold):
# 限制:如果有持仓(底仓或今日买入的)
if enable_amount > 0 and not g.has_open_position:
# 卖出等值金额
# 注意:order_value 卖出传入负数
order_value(security, -g.trade_value)
g.has_open_position = True
log.info("卖出信号: 现价 %.2f < VWAP %.2f, 卖出 %s" % (current_price, vwap, g.trade_value))
代码关键点解析
-
VWAP 计算:
- PTrade 的
handle_data在分钟回测模式下,data对象只包含当前分钟的数据。 - 我们需要使用
get_history获取当天从 9:30 开始到现在的所有分钟数据。 - 通过
his_df.index.date == today_date过滤掉可能存在的昨日数据。 - 公式:
sum(money) / sum(volume)。
- PTrade 的
-
底仓设置 (
set_yesterday_position):- 这是回测中的关键。如果不设置底仓,在 T+1 的 A 股市场,你当天买入后无法卖出,策略就无法进行“日内”闭环。
- 在实盘中,你需要确保账户里有对应的股票持仓。
-
阈值 (
g.threshold):- 直接用
Price > VWAP可能会导致在均线附近频繁开平仓(假突破)。 - 增加一个缓冲阈值(如 0.5%),只有显著突破 VWAP 才交易,能提高胜率。
- 直接用
-
收盘平仓:
- 代码中检测时间是否到达
14:55。如果是,则停止开新仓,并应执行平仓逻辑(代码中留出了位置,需根据你是要做 T 还是纯投机来决定是卖出全部还是恢复底仓)。
- 代码中检测时间是否到达
常见问题 (Q&A)
Q: 这个策略可以直接用于可转债吗?
A: 可以。可转债是 T+0 交易,不需要设置底仓(set_yesterday_position)。你可以直接买入并在当天卖出。只需将 g.security 改为可转债代码(如 '113050.SS'),并去掉卖出逻辑中对 enable_amount 的检查即可。
Q: 为什么使用 get_history 而不是 get_snapshot?
A: 在 PTrade 的回测模式下,get_snapshot 是不支持的。为了保证策略既能回测又能实盘,或者为了计算当日累计数据,使用 get_history 提取分钟线并求和是计算 VWAP 最准确且兼容性最好的方法。
Q: 如何在实盘中获取更精准的 VWAP?
A: 在实盘交易模式下,可以使用 get_snapshot(security),返回的字典中通常包含 wavg_px(加权平均价)字段,这直接就是交易所提供的官方 VWAP,比自己计算更实时。你可以用 if is_trade(): 来区分回测和实盘逻辑。