🚀 AI 一键生成 ptrade 策略代码

PTrade 策略实战:如何构建基于 VWAP 的日内交易策略?

ptrade | 发布时间: 2025/12/16
以下内容由 EasyQuant 生成。

问题描述

如何编写一个基于 VWAP(成交量加权平均价)的日内交易策略?

解决方案

这是一个基于 VWAP(成交量加权平均价) 的日内交易策略设计。

策略原理

VWAP(Volume Weighted Average Price)是衡量日内价格趋势的重要指标。

  1. 计算公式:$\text{VWAP} = \frac{\sum(\text{价格} \times \text{成交量})}{\sum \text{成交量}} = \frac{\text{当日累计成交额}}{\text{当日累计成交量}}$
  2. 核心逻辑
    • 当前价格 > VWAP 时,市场被视为处于多头趋势(买方力量强),策略选择 买入
    • 当前价格 < VWAP 时,市场被视为处于空头趋势(卖方力量强),策略选择 卖出
  3. 日内风控:在收盘前(如 14:55)强制平仓,确保不持仓过夜(针对 T+0 品种或模拟日内做T)。

注意事项(A股 T+1 限制)

由于中国 A 股市场实行 T+1 交易制度(当日买入的股票次日才能卖出),要在 A 股实现“日内交易”,通常有两种方法:

  1. 底仓做 T:账户里预先持有该股票(底仓),当日买入后,卖出原本持有的份额;或者先卖出底仓,低位再买回。
  2. 交易 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))

代码关键点解析

  1. VWAP 计算

    • PTrade 的 handle_data 在分钟回测模式下,data 对象只包含当前分钟的数据。
    • 我们需要使用 get_history 获取当天从 9:30 开始到现在的所有分钟数据。
    • 通过 his_df.index.date == today_date 过滤掉可能存在的昨日数据。
    • 公式:sum(money) / sum(volume)
  2. 底仓设置 (set_yesterday_position)

    • 这是回测中的关键。如果不设置底仓,在 T+1 的 A 股市场,你当天买入后无法卖出,策略就无法进行“日内”闭环。
    • 在实盘中,你需要确保账户里有对应的股票持仓。
  3. 阈值 (g.threshold)

    • 直接用 Price > VWAP 可能会导致在均线附近频繁开平仓(假突破)。
    • 增加一个缓冲阈值(如 0.5%),只有显著突破 VWAP 才交易,能提高胜率。
  4. 收盘平仓

    • 代码中检测时间是否到达 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(): 来区分回测和实盘逻辑。