🚀 AI 一键生成 joinquant 策略代码

JoinQuant 教程:漩涡指标(VI)趋势跟踪策略 Python 实现

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

问题描述

请帮我写一个漩涡指标 Vortex Indicator 的交易脚本

解决方案

这是一个基于 漩涡指标 (Vortex Indicator, VI) 的聚宽 (JoinQuant) 交易策略脚本。

策略原理

漩涡指标 (VI) 由两条线组成:$VI^+$ (正向漩涡) 和 $VI^-$ (负向漩涡)。

  1. $VI^+$ (VIPlus):代表上升趋势的强度。
  2. $VI^-$ (VIMinus):代表下降趋势的强度。

交易信号:

  • 买入信号:当 $VI^+$ 上穿 $VI^-$ 时(即 $VI^+$ > $VI^-$),表明多头力量占据优势,执行买入。
  • 卖出信号:当 $VI^-$ 上穿 $VI^+$ 时(即 $VI^+$ < $VI^-$),表明空头力量占据优势,执行卖出。

策略代码

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

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'
    
    # 漩涡指标的周期参数,通常为14
    g.n = 14
    
    # 每天开盘时运行策略
    run_daily(market_open, time='09:30')

def calculate_vortex(security, n):
    """
    计算漩涡指标 (VI)
    返回: VI_plus, VI_minus
    """
    # 获取历史数据,需要 n+1 天的数据来计算 n 个周期的变动
    # fields: 最高价, 最低价, 收盘价
    df = attribute_history(security, n + 1, '1d', ['high', 'low', 'close'])
    
    if len(df) < n + 1:
        return None, None

    # 提取数据
    high = df['high']
    low = df['low']
    close = df['close']
    
    # 计算真实波幅 (True Range, TR)
    # TR = Max(High-Low, Abs(High-PrevClose), Abs(Low-PrevClose))
    tr1 = high - low
    tr2 = abs(high - close.shift(1))
    tr3 = abs(low - close.shift(1))
    
    # 拼接成DataFrame取最大值,axis=1表示按行取最大值
    tr_df = pd.concat([tr1, tr2, tr3], axis=1)
    true_range = tr_df.max(axis=1)
    
    # 计算正向和负向移动 (VM+, VM-)
    # VM+ = Abs(CurrentHigh - PrevLow)
    # VM- = Abs(CurrentLow - PrevHigh)
    vm_plus = abs(high - low.shift(1))
    vm_minus = abs(low - high.shift(1))
    
    # 去除第一行(因为shift导致第一行为NaN)
    true_range = true_range.iloc[1:]
    vm_plus = vm_plus.iloc[1:]
    vm_minus = vm_minus.iloc[1:]
    
    # 计算周期内的总和
    sum_tr = true_range.sum()
    sum_vm_plus = vm_plus.sum()
    sum_vm_minus = vm_minus.sum()
    
    # 避免除以0的错误
    if sum_tr == 0:
        return 0, 0
        
    # 计算 VI+ 和 VI-
    vi_plus = sum_vm_plus / sum_tr
    vi_minus = sum_vm_minus / sum_tr
    
    return vi_plus, vi_minus

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 计算当前的 VI 指标
    vi_plus, vi_minus = calculate_vortex(security, g.n)
    
    if vi_plus is None or vi_minus is None:
        log.info("数据不足,跳过计算")
        return

    # 获取当前持仓
    curr_position = context.portfolio.positions[security].total_amount
    # 获取当前可用资金
    cash = context.portfolio.available_cash
    
    # 记录指标值方便回测查看
    record(VI_Plus=vi_plus, VI_Minus=vi_minus)
    
    # 交易逻辑
    # 1. 买入信号: VI+ 上穿 VI- (即 VI+ > VI-) 且当前无持仓
    if vi_plus > vi_minus and curr_position == 0:
        # 全仓买入
        order_value(security, cash)
        log.info("买入信号触发: VI+ ({:.4f}) > VI- ({:.4f}), 买入 {}".format(vi_plus, vi_minus, security))
        
    # 2. 卖出信号: VI- 上穿 VI+ (即 VI+ < VI-) 且当前有持仓
    elif vi_plus < vi_minus and curr_position > 0:
        # 清仓卖出
        order_target(security, 0)
        log.info("卖出信号触发: VI+ ({:.4f}) < VI- ({:.4f}), 卖出 {}".format(vi_plus, vi_minus, security))

代码说明

  1. 数据获取:使用 attribute_history 获取过去 N+1 天的最高价、最低价和收盘价。多取一天是因为计算变动量(VM)和真实波幅(TR)需要用到前一日的数据(shift(1))。
  2. 指标计算
    • TR (True Range):取当日最高最低差、当日最高与昨日收盘差、当日最低与昨日收盘差这三者中的最大值。
    • VM+ (Positive Vortex Movement):当日最高价与昨日最低价之差的绝对值。
    • VM- (Negative Vortex Movement):当日最低价与昨日最高价之差的绝对值。
    • 最后将 N 天内的 VM 和 TR 分别求和,相除得到 $VI^+$ 和 $VI^-$。
  3. 交易逻辑
    • 如果 $VI^+ > VI^-$,视为上升趋势,全仓买入。
    • 如果 $VI^+ < VI^-$,视为下降趋势,清仓卖出。
  4. 可视化:使用了 record() 函数,在回测图表中绘制 VI+ 和 VI- 的曲线,便于观察交叉点。

Q&A

Q: 如何修改策略操作的股票?
A: 在 initialize 函数中,修改 g.security = '000001.XSHE' 这一行,将代码替换为您想要回测的股票代码即可。

Q: 如何修改漩涡指标的周期?
A: 默认周期为 14 天。您可以在 initialize 函数中修改 g.n = 14,例如改为 g.n = 21 以适应更长周期的趋势判断。

Q: 这个策略可以用于多只股票吗?
A: 当前代码是针对单只股票设计的。如果需要操作多只股票,需要将 g.security 改为股票列表(如 g.stocks = ['000001.XSHE', '600519.XSHG']),并在 market_open 函数中使用 for 循环遍历列表,对每只股票分别计算指标和下单。

Q: 为什么计算 TR 时要用 shift(1)
A: 漩涡指标的计算公式中,部分逻辑(如 VM+ 和 VM-)依赖于“昨日”的价格(昨日最低价或昨日最高价)。在 Pandas 中,shift(1) 用于获取上一行的数据,从而实现当前行与前一行数据的运算。