问题描述
解决方案
基于线性回归斜率的趋势策略是一种经典的动量策略。其核心逻辑是利用统计学中的线性回归方法,计算过去一段时间内价格走势的斜率(Slope)。
策略逻辑
- 计算斜率 ($k$):选取过去 $N$ 根 K 线的收盘价,利用最小二乘法拟合一条直线 $y = kx + b$。
- $k > 0$:表示处于上升趋势。
- $k < 0$:表示处于下降趋势。
- 交易信号:
- 买入信号:当斜率 $k$ 大于设定的正阈值(例如 0.05),认为上涨趋势确立,买入持仓。
- 卖出信号:当斜率 $k$ 小于设定的负阈值(例如 -0.05)或跌破 0,认为趋势反转或结束,卖出平仓。
QMT 策略代码实现
以下是完整的 Python 策略代码。该代码使用了 numpy 库来进行线性拟合计算,并使用 get_market_data_ex 获取历史数据。
# -*- coding: gbk -*-
import numpy as np
import pandas as pd
def init(ContextInfo):
"""
策略初始化函数
"""
# 1. 设置策略参数
ContextInfo.N = 20 # 线性回归计算周期(如过去20天)
ContextInfo.slope_threshold = 0.1 # 斜率阈值,用于过滤震荡噪音
ContextInfo.stock_list = ['600000.SH', '000001.SZ'] # 股票池示例
# 2. 设置股票池
ContextInfo.set_universe(ContextInfo.stock_list)
# 3. 设置资金账号 (请替换为您的真实资金账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.set_account(ContextInfo.account_id)
# 4. 设置回测参数 (仅回测有效)
ContextInfo.set_commission(0, [0.0001, 0.0001, 0.0003, 0.0003, 0, 5]) # 设置费率
ContextInfo.capital = 1000000 # 初始资金
def handlebar(ContextInfo):
"""
K线逐根运行函数
"""
# 获取当前设置的股票池
stock_list = ContextInfo.get_universe()
# 获取当前周期 (例如 '1d')
period = ContextInfo.period
# 批量获取历史行情数据
# 我们需要 N 个数据点来进行回归计算
# count = ContextInfo.N 表示获取过去 N 根K线
market_data = ContextInfo.get_market_data_ex(
fields=['close'],
stock_code=stock_list,
period=period,
count=ContextInfo.N,
dividend_type='front' # 前复权
)
# 遍历每一只股票进行计算
for stock in stock_list:
# 获取该股票的 DataFrame 数据
if stock not in market_data:
continue
df = market_data[stock]
# 数据长度校验,如果数据不足 N 个,无法计算回归,跳过
if len(df) < ContextInfo.N:
continue
# 提取收盘价序列
close_prices = df['close'].values
# --- 核心逻辑:计算线性回归斜率 ---
# 创建 X 轴序列 [0, 1, 2, ..., N-1]
x = np.arange(len(close_prices))
# 创建 Y 轴序列 (收盘价)
y = close_prices
# 使用 numpy 的 polyfit 进行一次多项式拟合 (线性回归)
# deg=1 表示一次方程 y = kx + b
# 返回值 fit[0] 是斜率 k,fit[1] 是截距 b
try:
fit = np.polyfit(x, y, 1)
slope = fit[0]
except Exception as e:
print(f"计算斜率出错 {stock}: {e}")
continue
# --- 交易逻辑 ---
# 获取当前持仓信息
position = get_position(stock, ContextInfo)
# 1. 买入逻辑:斜率 > 阈值 且 当前无持仓
if slope > ContextInfo.slope_threshold and position == 0:
print(f"[{stock}] 触发买入信号,当前斜率: {slope:.4f} > 阈值: {ContextInfo.slope_threshold}")
# 目标市值买入,例如调整仓位到 20% (假设资金足够分散)
# 这里简单演示,全仓买入或固定比例买入
order_target_percent(stock, 0.2, ContextInfo, ContextInfo.account_id)
# 2. 卖出逻辑:斜率 < -阈值 (或跌破0) 且 当前有持仓
# 这里设置为斜率转负即卖出,或者小于负阈值卖出,取决于激进程度
elif slope < -ContextInfo.slope_threshold and position > 0:
print(f"[{stock}] 触发卖出信号,当前斜率: {slope:.4f} < 阈值: -{ContextInfo.slope_threshold}")
# 清仓
order_target_percent(stock, 0, ContextInfo, ContextInfo.account_id)
def get_position(stock_code, ContextInfo):
"""
辅助函数:获取某只股票的当前持仓数量
"""
# 获取持仓对象列表
positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
# 返回当前持仓量
return pos.m_nVolume
return 0
代码关键点解析
-
数据获取 (
get_market_data_ex):- 使用
get_market_data_ex是 QMT 推荐的方式,效率比旧接口高。 count=ContextInfo.N确保我们获取了足够的数据点来进行回归分析。dividend_type='front'使用前复权数据,这对于计算历史趋势至关重要,避免因分红除权导致的价格跳空影响斜率计算。
- 使用
-
线性回归计算 (
np.polyfit):- 我们使用
numpy.polyfit(x, y, 1)。其中x是时间序列(0 到 N-1),y是价格序列。 - 参数
1代表拟合一次函数(直线)。 - 结果是一个数组,第一个元素即为斜率 $k$。
- 斜率的含义:斜率代表了“平均每根 K 线价格变动的数值”。例如,斜率为 0.5 意味着在过去 N 天内,股价平均每天上涨 0.5 元。
- 我们使用
-
阈值设定 (
ContextInfo.slope_threshold):- 直接使用价格斜率有一个缺点:高价股(如贵州茅台)的斜率数值天然比低价股(如农业银行)大。
- 优化建议:在实际应用中,建议对数据进行归一化处理。例如,对收盘价取对数,或者将斜率除以当前股价,将其转换为“涨跌幅斜率”,这样阈值就可以设置为百分比(如 0.01 代表平均每天涨 1%),从而适应不同价格的股票。
-
交易执行 (
order_target_percent):- 使用
order_target_percent可以方便地管理仓位。0.2表示将该股票仓位调整至总资产的 20%。0表示清仓。
- 使用
常见问题 Q&A
Q: 为什么我的策略在回测时没有交易?
A: 请检查以下几点:
ContextInfo.stock_list是否包含了有效的股票代码。ContextInfo.slope_threshold设置是否过大,导致很难触发信号。- 数据是否下载完整(在“数据管理”中补充历史数据)。
Q: 如何让策略对高价股和低价股一视同仁?
A: 修改计算逻辑,对价格进行归一化。将 y = close_prices 改为 y = close_prices / close_prices[0](以区间第一天价格为基准)。这样计算出的斜率代表的是相对于起始价格的增长率。
Q: 这个策略适合什么周期?
A: 线性回归策略通常在趋势明显的周期表现较好,如日线(1d)或 60分钟线(60m)。在震荡市或极短周期(如 1分钟)中可能会产生大量假信号。