🚀 AI 一键生成 joinquant 策略代码

JoinQuant 量化教程:构建 PVT 价量趋势交易系统

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

问题描述

怎么编写一个 PVT 价量趋势指标的交易策略?

解决方案

这是一个基于 PVT (Price Volume Trend, 价量趋势指标) 的交易策略设计。

策略原理

PVT 指标类似于 OBV(能量潮),但它考虑了价格变动的幅度,而不仅仅是方向。

  • 公式:$PVT = \text{昨日PVT} + \text{成交量} \times \frac{\text{今日收盘价} - \text{昨日收盘价}}{\text{昨日收盘价}}$
  • 交易逻辑
    1. 计算 PVT 线。
    2. 计算 PVT 的 N 日移动平均线 (MA)。
    3. 买入信号:当 PVT 向上突破 PVT 均线(金叉)。
    4. 卖出信号:当 PVT 向下跌破 PVT 均线(死叉)。

策略代码

以下是完整的 JoinQuant 策略代码。该策略选取一只标的(如平安银行),使用 PVT 与其 20 日均线的交叉作为买卖信号。

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

def initialize(context):
    """
    初始化函数,设定基准、手续费、滑点、全局变量等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # --- 策略参数设置 ---
    # 操作的股票:平安银行
    g.security = '000001.XSHE'
    # PVT均线窗口
    g.ma_window = 20
    
    # 每天开盘时运行
    run_daily(market_open, time='09:30')

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 1. 获取数据
    # 我们需要足够长的数据来计算PVT和它的均线
    # 获取过去 100 天的数据,确保均线计算稳定
    fetch_count = 100
    h_data = attribute_history(security, fetch_count, '1d', ['close', 'volume'], skip_paused=True)
    
    # 如果数据不足,直接返回
    if len(h_data) < g.ma_window + 2:
        return

    # 2. 计算 PVT 指标
    # 计算每日涨跌幅: (今日收盘 - 昨日收盘) / 昨日收盘
    pct_change = h_data['close'].pct_change()
    
    # 计算每日PVT增量: 涨跌幅 * 成交量
    # 注意:pct_change的第一项是NaN,fillna(0)处理
    pvt_change = pct_change * h_data['volume']
    pvt_change = pvt_change.fillna(0)
    
    # 计算累积 PVT 值
    pvt_series = pvt_change.cumsum()
    
    # 3. 计算 PVT 的移动平均线 (MA)
    pvt_ma = pvt_series.rolling(window=g.ma_window).mean()
    
    # 4. 获取当前的 PVT 和 MA 值,以及前一日的值(用于判断交叉)
    curr_pvt = pvt_series.iloc[-1]
    prev_pvt = pvt_series.iloc[-2]
    
    curr_ma = pvt_ma.iloc[-1]
    prev_ma = pvt_ma.iloc[-2]
    
    # 检查是否计算出有效值(避免NaN)
    if np.isnan(curr_ma) or np.isnan(prev_ma):
        return

    # 5. 获取当前账户资金和持仓
    cash = context.portfolio.available_cash
    position = context.portfolio.positions[security]
    
    # 获取当前标的状态(停牌判断)
    current_data = get_current_data()
    if current_data[security].paused:
        return

    # 6. 生成交易信号
    # 金叉:昨天PVT在均线下,今天PVT在均线上
    cross_over = (prev_pvt < prev_ma) and (curr_pvt > curr_ma)
    # 死叉:昨天PVT在均线上,今天PVT在均线下
    cross_under = (prev_pvt > prev_ma) and (curr_pvt < curr_ma)
    
    # 7. 执行交易
    if cross_over and cash > 0:
        # 买入信号:全仓买入
        log.info(f"PVT金叉 (PVT: {curr_pvt:.2f}, MA: {curr_ma:.2f}) - 买入 {security}")
        order_value(security, cash)
        
    elif cross_under and position.closeable_amount > 0:
        # 卖出信号:清仓
        log.info(f"PVT死叉 (PVT: {curr_pvt:.2f}, MA: {curr_ma:.2f}) - 卖出 {security}")
        order_target(security, 0)
        
    # 8. 绘图记录(可选,用于回测分析)
    record(PVT=curr_pvt, PVT_MA=curr_ma)

代码解析

  1. 数据获取 (attribute_history):

    • 我们获取了 close (收盘价) 和 volume (成交量)。
    • 获取长度设为 100 天,虽然均线只需要 20 天,但 PVT 是累积指标,获取更多历史数据能让曲线形态更具参考性(尽管对于均线交叉策略,相对位置比绝对数值更重要)。
  2. 指标计算:

    • 利用 Pandas 的 pct_change() 快速计算价格变化率。
    • pvt_change = pct_change * volume 计算每日的 PVT 变动量。
    • cumsum() 计算累积的 PVT 线。
    • rolling(window=20).mean() 计算 PVT 的 20 日均线。
  3. 信号判断:

    • 通过比较 curr_pvt (当前值) 与 curr_ma (当前均线值) 以及它们前一天的状态,来判断是否发生了“金叉”或“死叉”。
  4. 交易执行:

    • 使用 order_value 进行买入。
    • 使用 order_target 进行卖出(清仓)。

常见问题 (Q&A)

Q1: PVT 指标和 OBV 指标有什么区别?
A: OBV (能量潮) 只是简单地根据收盘价涨跌将当日全部成交量加减到累积值中(涨加跌减)。而 PVT 考虑了涨跌的幅度。例如,股价微涨 0.1% 和大涨 5%,在 OBV 中加的量是一样的,但在 PVT 中,大涨 5% 增加的权重会大得多。因此,PVT 通常比 OBV 更能反映资金的真实意图。

Q2: 这个策略可以用于多只股票吗?
A: 可以。目前的示例代码针对单只股票 (g.security)。如果要操作多只股票,可以将股票列表放入 g.stocks,然后在 market_open 中使用 for security in g.stocks: 循环遍历每只股票进行计算和下单。

Q3: 为什么计算 PVT 时要获取较长的历史数据?
A: PVT 是一个累积指标(Cumulative)。如果只获取最近 20 天的数据,第 1 天的 PVT 会从 0 开始,这可能导致计算出的均线在初期不稳定。虽然对于“交叉信号”来说,相对趋势比绝对值重要,但保留一定的历史缓冲区(如 100 天)可以减少计算初期的噪音。

Q4: 如何优化这个策略?
A:

  1. 参数优化:调整均线窗口 g.ma_window(如 10日、30日)。
  2. 结合价格趋势:仅在股价位于长期均线(如 60日线)之上时才响应 PVT 的买入信号。
  3. 止损止盈:加入固定的止损逻辑(如亏损 5% 止损)以控制风险。