问题描述
解决方案
这是一个基于 KDJ 指标的经典趋势跟踪策略。
策略逻辑
- 指标计算:使用 KDJ 指标(随机指标),通常参数设置为 N=9, M1=3, M2=3。
- 买入信号(金叉):当 K 线由下向上穿过 D 线时(即上一期 K < D,且当前 K > D),全仓买入。
- 卖出信号(死叉):当 K 线由上向下穿过 D 线时(即上一期 K > D,且当前 K < D),清仓卖出。
策略代码
以下是基于聚宽(JoinQuant)API 实现的完整策略代码。
# -*- coding: utf-8 -*-
import jqdata
import talib
import numpy as np
def initialize(context):
"""
初始化函数,设定基准、手续费、滑点及全局变量
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设置交易费用:买入万分之三,卖出万分之三加千分之一印花税,最低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'
# KDJ 指标参数 (N, M1, M2)
g.N = 9
g.M1 = 3
g.M2 = 3
# 每天开盘时运行策略
run_daily(market_open, time='09:30')
def market_open(context):
"""
每日交易逻辑
"""
security = g.security
# 获取历史数据
# KDJ计算需要High, Low, Close数据
# 获取过去 50 天的数据以确保计算指标时有足够的预热数据
data = attribute_history(security, 50, '1d', ['high', 'low', 'close'], df=False)
# 提取数据为 numpy 数组,talib 需要 numpy 格式
high_prices = data['high']
low_prices = data['low']
close_prices = data['close']
# 检查数据长度是否足够
if len(close_prices) < 20:
return
# 使用 talib 计算 KDJ
# STOCH 函数返回 K 和 D 的值
# fastk_period 对应 N
# slowk_period 对应 M1
# slowd_period 对应 M2
K, D = talib.STOCH(high_prices,
low_prices,
close_prices,
fastk_period=g.N,
slowk_period=g.M1,
slowk_matype=0,
slowd_period=g.M2,
slowd_matype=0)
# 获取当前和上一期的 K、D 值
# [-1] 为当前值,[-2] 为上一期值
current_k = K[-1]
current_d = D[-1]
prev_k = K[-2]
prev_d = D[-2]
# 获取当前账户资金和持仓
curr_position = context.portfolio.positions[security].total_amount
available_cash = context.portfolio.available_cash
# 交易信号判断
# 金叉:K线上穿D线 (上期 K < D 且 本期 K > D)
golden_cross = (prev_k < prev_d) and (current_k > current_d)
# 死叉:K线下穿D线 (上期 K > D 且 本期 K < D)
death_cross = (prev_k > prev_d) and (current_k < current_d)
# 执行交易
if golden_cross and available_cash > 0:
# 买入所有可用资金
order_value(security, available_cash)
log.info("KDJ金叉,买入 %s,当前K: %.2f, 当前D: %.2f" % (security, current_k, current_d))
elif death_cross and curr_position > 0:
# 卖出所有持仓
order_target(security, 0)
log.info("KDJ死叉,卖出 %s,当前K: %.2f, 当前D: %.2f" % (security, current_k, current_d))
# 记录KDJ值以便在回测图表中查看
record(K=current_k, D=current_d)
代码解析
-
数据获取 (
attribute_history):- KDJ 计算依赖于最高价(High)、最低价(Low)和收盘价(Close)。
- 我们获取了过去 50 天的数据。虽然 KDJ 参数通常是 9 天,但指标计算(特别是平滑处理)需要一定长度的历史数据来消除初始值的偏差,因此取 50 天比较稳妥。
-
指标计算 (
talib.STOCH):- 使用了 Python 的金融技术分析库
talib。这是计算指标最高效、最标准的方法。 talib.STOCH直接返回 K 线和 D 线的数据序列。- 参数对应关系:
fastk_period对应 KDJ 中的 N(周期),slowk_period对应 M1,slowd_period对应 M2。
- 使用了 Python 的金融技术分析库
-
信号判断:
- 金叉:判断逻辑为
prev_k < prev_d(昨天 K 在 D 下方) 且current_k > current_d(今天 K 在 D 上方)。 - 死叉:判断逻辑为
prev_k > prev_d(昨天 K 在 D 上方) 且current_k < current_d(今天 K 在 D 下方)。
- 金叉:判断逻辑为
-
交易执行:
- 使用
order_value进行买入,使用order_target进行卖出(设为 0 即清仓)。
- 使用
常见问题 (Q&A)
Q: 为什么使用 talib 而不是自己写公式计算 KDJ?
A: talib 是经过高度优化的 C 语言底层库,计算速度极快且结果标准。自己编写公式容易在平滑算法(如 SMA, EMA 的选择)上出现偏差,导致与主流软件结果不一致。
Q: 如何修改 KDJ 的参数,例如改为 (18, 6, 6)?
A: 在 initialize 函数中修改 g.N, g.M1, g.M2 的值即可。例如:g.N = 18, g.M1 = 6, g.M2 = 6。
Q: 这个策略可以用于多只股票吗?
A: 当前代码仅针对单只股票(g.security)。如果要操作多只股票,需要将 g.security 改为一个列表,并在 market_open 函数中使用 for 循环遍历列表,对每一只股票分别计算指标和下单。
Q: 为什么有时候会出现 KDJ 钝化(长期在高位或低位)导致信号失效?
A: KDJ 是一个敏感的摆动指标,在单边强趋势行情中容易出现钝化(即一直提示超买或超卖但价格继续上涨或下跌)。在实盘中,通常建议结合均线(MA)或 MACD 等趋势指标进行过滤,避免在强趋势中逆势操作。