Qstick趋势策略

一、交易策略解释

核心思想

Qstick趋势指标策略的核心思想是通过分析价格的收盘价与开盘价之间的差异来识别市场趋势和动量。 该指标计算特定周期内"收盘价-开盘价"差值的移动平均线,从而量化市场的看涨或看跌情绪。 当Qstick值为正时,表明市场存在看涨动量;当值为负时,表明市场存在看跌动量。该策略特别关注Qstick指标与零轴的交叉点,将其作为趋势转变的信号。

理论基础

Qstick指标由Tushar Chande开发,基于以下市场观察:

  • 在上升趋势中,收盘价通常高于开盘价

  • 在下降趋势中,收盘价通常低于开盘价

这种价格行为反映了市场参与者的情绪和力量对比。通过将这些差异进行移动平均,Qstick指标能够过滤掉短期噪音,反映更持久的市场趋势。

研究表明,趋势跟踪策略在期货市场中尤其有效,因为商品和金融期货市场常常表现出持续性的价格走势。Qstick指标作为趋势识别工具,能够帮助交易者捕捉这些延续性走势并适时进场交易。

策略适用场景

  • 趋势明显的市场:当市场处于明确的上升或下降趋势中时,Qstick指标能够有效捕捉趋势转变点。

  • 中长期交易:由于采用移动平均平滑处理,该策略更适合日线或更长时间周期的交易,而非短线交易。

  • 波动性适中的品种:如主要商品期货、股指期货等,相比极端高波动或低波动的品种更为适用。

  • 流动性较好的市场:确保能够以合理价格迅速执行交易指令。

二、天勤介绍

天勤平台概述

天勤(TqSdk)是一个由信易科技开发的开源量化交易系统,为期货、期权等衍生品交易提供专业的量化交易解决方案。平台具有以下特 点:

  • 丰富的行情数据 提供所有可交易合约的全部Tick和K线数据,基于内存数据库实现零延迟访问。
  • 一站式的解决方案 从历史数据分析到实盘交易的完整工具链,打通开发、回测、模拟到实盘的全流程。
  • 专业的技术支持 近百个技术指标源码,深度集成pandas和numpy,采用单线程异步模型保证性能。

策略开发流程

  • 环境准备
    • 安装Python环境(推荐Python 3.6或以上版本)
    • 安装tqsdk包:pip install tqsdk
    • 注册天勤账户获取访问密钥
  • 数据准备
    • 订阅近月与远月合约行情
    • 获取历史K线或者Tick数据(用于分析与行情推进)
  • 策略编写
    • 设计信号生成逻辑(基于价差、均值和标准差)
    • 编写交易执行模块(开仓、平仓逻辑)
    • 实现风险控制措施(止损、资金管理)
  • 回测验证
    • 设置回测时间区间和初始资金
    • 运行策略获取回测结果
    • 分析绩效指标(胜率、收益率、夏普率等)
  • 策略优化
    • 调整参数(标准差倍数、窗口大小等)
    • 添加过滤条件(成交量、波动率等)
    • 完善风险控制机制

三、天勤实现策略

策略原理

Qstick指标的计算方法:

def calculate_qstick(open_prices, close_prices, period):
    """计算Qstick指标"""
    # 计算收盘价与开盘价的差值
    diff = close_prices - open_prices
    # 计算移动平均
    qstick = diff.rolling(window=period).mean()
    return qstick

其中:

  • 计算每根K线的收盘价减去开盘价的差值

  • 对这些差值序列取n周期的简单移动平均值

  • 最终得到的Qstick指标反映了一段时间内收盘价相对开盘价的平均偏离方向和幅度

本策略使用10周期的Qstick指标,此参数设置旨在平衡信号灵敏度与稳定性。

此外,还使用了两个辅助指标加强交易信号的质量:

  1. SMA指标:8周期简单移动平均线,用于确认价格所处的大方向

  2. ATR指标:14周期平均真实波幅,用于设置动态止损位置

交易逻辑

开仓信号:

  • 多头信号:Qstick从负值向上穿越零轴,价格在SMA上方
if previous_qstick < 0 and current_qstick > 0 and current_price > current_price_sma:
    current_direction = 1
    target_pos.set_target_volume(POSITION_SIZE)
    entry_price = current_price
    entry_date = current_datetime
    # 设置止损价格
    stop_loss_price = entry_price - STOP_LOSS_MULTIPLIER * current_atr
  • 空头信号:Aroon Down从下方穿越Aroon Up
elif previous_qstick > 0 and current_qstick < 0 and current_price < current_price_sma:
    current_direction = -1
    target_pos.set_target_volume(-POSITION_SIZE)
    entry_price = current_price
    entry_date = current_datetime
    # 设置止损价格
    stop_loss_price = entry_price + STOP_LOSS_MULTIPLIER * current_atr

开仓后,系统会立即设置基于ATR的动态止损位置,使用2倍ATR值作为止损距离。

平仓信号:

  • 止损平仓:价格触及预设止损位置时强制平仓
# 多头止损
if current_price <= stop_loss_price:
    # 平仓逻辑

# 空头止损
if current_price >= stop_loss_price:
    # 平仓逻辑
  • 反向信号平仓:当Qstick指标产生反向穿越零轴信号时平仓
# 多头反向信号平仓
elif previous_qstick > 0 and current_qstick < 0:
    # 平仓逻辑

# 空头反向信号平仓
elif previous_qstick < 0 and current_qstick > 0:
    # 平仓逻辑
  • 时间止损:当持仓时间超过5天时强制平仓
# 时间止损
elif holding_days >= MAX_HOLDING_DAYS:
    # 平仓逻辑

回测

回测初始设置

  • 测试周期: 2022 年 8 月 1 日 - 2023 年 1 月 30 日
  • 交易品种: CFFEX.IC2303
  • 初始资金: 1000万元

回测结果

上述回测累计收益走势图

完整代码示例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Chaos"

from datetime import date
import pandas as pd
from tqsdk import TqApi, TqAuth, TqBacktest, TargetPosTask, BacktestFinished
from tqsdk.ta import ATR

# ===== 全局参数设置 =====
SYMBOL = "CFFEX.IC2303"  # 中证500指数期货
POSITION_SIZE = 30  # 持仓手数
START_DATE = date(2022, 8, 1)  # 回测开始日期
END_DATE = date(2023, 1, 30)  # 回测结束日期

# Qstick指标参数
QSTICK_PERIOD = 10  # Qstick周期
SMA_PERIOD = 8  # 价格SMA周期,用于确认趋势

# 风控参数
ATR_PERIOD = 14  # ATR计算周期
STOP_LOSS_MULTIPLIER = 2.0  # 止损ATR倍数
MAX_HOLDING_DAYS = 5  # 最大持仓天数

# ===== 全局变量 =====
current_direction = 0  # 当前持仓方向:1=多头,-1=空头,0=空仓
entry_price = 0  # 开仓价格
stop_loss_price = 0  # 止损价格
entry_date = None  # 开仓日期


# ===== Qstick指标计算函数 =====
def calculate_qstick(open_prices, close_prices, period):
    """计算Qstick指标"""
    # 计算收盘价与开盘价的差值
    diff = close_prices - open_prices
    # 计算移动平均
    qstick = diff.rolling(window=period).mean()
    return qstick


# ===== 策略开始 =====
print("开始运行Qstick趋势指标期货策略...")

# 创建API实例
api = TqApi(backtest=TqBacktest(start_dt=START_DATE, end_dt=END_DATE),
            auth=TqAuth("快期账号", "快期密码"))

# 订阅合约的K线数据
klines = api.get_kline_serial(SYMBOL, 60 * 60 * 24)  # 日线数据

# 创建目标持仓任务
target_pos = TargetPosTask(api, SYMBOL)

try:
    while True:
        # 等待更新
        api.wait_update()

        # 如果K线有更新
        if api.is_changing(klines.iloc[-1], "datetime"):
            # 确保有足够的数据计算指标
            if len(klines) < max(QSTICK_PERIOD, SMA_PERIOD, ATR_PERIOD) + 5:
                continue

            # 计算Qstick指标
            klines['qstick'] = calculate_qstick(klines.open, klines.close, QSTICK_PERIOD)

            # 计算价格SMA,用于确认趋势
            klines['price_sma'] = klines.close.rolling(window=SMA_PERIOD).mean()

            # 计算ATR用于设置止损
            atr_data = ATR(klines, ATR_PERIOD)

            # 获取最新数据
            current_price = float(klines.close.iloc[-1])
            current_datetime = pd.to_datetime(klines.datetime.iloc[-1], unit='ns')
            current_qstick = float(klines.qstick.iloc[-1])
            previous_qstick = float(klines.qstick.iloc[-2])
            current_price_sma = float(klines.price_sma.iloc[-1])
            current_atr = float(atr_data.atr.iloc[-1])

            # 输出调试信息
            print(f"日期: {current_datetime.strftime('%Y-%m-%d')}, 价格: {current_price:.2f}")
            print(f"Qstick: {current_qstick:.4f}")

            # ===== 交易逻辑 =====

            # 空仓状态 - 寻找开仓机会
            if current_direction == 0:
                # 多头开仓条件:Qstick从负值向上穿越零轴,价格在SMA上方
                if previous_qstick < 0 and current_qstick > 0 and current_price > current_price_sma:
                    current_direction = 1
                    target_pos.set_target_volume(POSITION_SIZE)
                    entry_price = current_price
                    entry_date = current_datetime

                    # 设置止损价格
                    stop_loss_price = entry_price - STOP_LOSS_MULTIPLIER * current_atr

                    print(f"多头开仓: 价格={entry_price}, 止损={stop_loss_price:.2f}")

                # 空头开仓条件:Qstick从正值向下穿越零轴,价格在SMA下方
                elif previous_qstick > 0 and current_qstick < 0 and current_price < current_price_sma:
                    current_direction = -1
                    target_pos.set_target_volume(-POSITION_SIZE)
                    entry_price = current_price
                    entry_date = current_datetime

                    # 设置止损价格
                    stop_loss_price = entry_price + STOP_LOSS_MULTIPLIER * current_atr

                    print(f"空头开仓: 价格={entry_price}, 止损={stop_loss_price:.2f}")

            # 多头持仓 - 检查平仓条件
            elif current_direction == 1:
                # 计算持仓天数
                holding_days = (current_datetime - entry_date).days

                # 1. 止损条件:价格低于止损价格
                if current_price <= stop_loss_price:
                    profit_pct = (current_price - entry_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"多头止损平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

                # 2. 反向信号平仓:Qstick从正值向下穿越零轴
                elif previous_qstick > 0 and current_qstick < 0:
                    profit_pct = (current_price - entry_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"多头信号平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

                # 3. 时间止损:持仓时间过长
                elif holding_days >= MAX_HOLDING_DAYS:
                    profit_pct = (current_price - entry_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"多头时间平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

            # 空头持仓 - 检查平仓条件
            elif current_direction == -1:
                # 计算持仓天数
                holding_days = (current_datetime - entry_date).days

                # 1. 止损条件:价格高于止损价格
                if current_price >= stop_loss_price:
                    profit_pct = (entry_price - current_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"空头止损平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

                # 2. 反向信号平仓:Qstick从负值向上穿越零轴
                elif previous_qstick < 0 and current_qstick > 0:
                    profit_pct = (entry_price - current_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"空头信号平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

                # 3. 时间止损:持仓时间过长
                elif holding_days >= MAX_HOLDING_DAYS:
                    profit_pct = (entry_price - current_price) / entry_price * 100
                    target_pos.set_target_volume(0)
                    current_direction = 0
                    print(f"空头时间平仓: 价格={current_price}, 盈亏={profit_pct:.2f}%, 持仓天数={holding_days}")

except BacktestFinished as e:
    print("回测结束")
    api.close()