3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个基于 QMT 平台的 MACD 和 KDJ 双指标共振(同时金叉) 策略。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import talib
def init(ContextInfo):
# 1. 设置资金账号 (请替换为您自己的资金账号)
ContextInfo.accID = 'YOUR_ACCOUNT_ID'
ContextInfo.set_account(ContextInfo.accID)
# 2. 设置股票池 (示例:平安银行)
ContextInfo.target_list = ['000001.SZ', '600000.SH']
ContextInfo.set_universe(ContextInfo.target_list)
# 3. 设置策略运行周期,这里以日线为例
ContextInfo.period = '1d'
# 4. 设置一些参数
ContextInfo.macd_fast = 12
ContextInfo.macd_slow = 26
ContextInfo.macd_signal = 9
ContextInfo.kdj_n = 9
ContextInfo.kdj_m1 = 3
ContextInfo.kdj_m2 = 3
def handlebar(ContextInfo):
# 获取当前设置的股票池
stock_list = ContextInfo.get_universe()
# 获取当前 K 线位置,如果是回测,barpos 会随时间递增
index = ContextInfo.barpos
# 获取当前时间,用于日志输出
realtime = ContextInfo.get_bar_timetag(index)
date_str = timetag_to_datetime(realtime, '%Y-%m-%d')
# 遍历每只股票进行计算
for stock in stock_list:
# 1. 获取历史行情数据
# 为了计算 MACD 和 KDJ,我们需要足够多的历史数据,这里取 100 根
# 注意:get_market_data_ex 返回的是 dict {code: dataframe}
data_map = ContextInfo.get_market_data_ex(
['open', 'high', 'low', 'close'],
[stock],
period=ContextInfo.period,
count=100,
dividend_type='front' # 前复权
)
if stock not in data_map:
continue
df = data_map[stock]
# 数据长度不足,无法计算指标,跳过
if len(df) < 30:
continue
# 2. 计算 MACD 指标
close_prices = df['close'].values
# 使用 talib 计算 MACD
# diff 对应 DIF, dea 对应 DEA, macd 对应 MACD柱
diff, dea, macd = talib.MACD(
close_prices,
fastperiod=ContextInfo.macd_fast,
slowperiod=ContextInfo.macd_slow,
signalperiod=ContextInfo.macd_signal
)
# 3. 计算 KDJ 指标
# 由于 talib 的 STOCH 计算方式与国内软件(如通达信)略有差异,
# 这里使用自定义函数计算符合国内习惯的 KDJ
k_vals, d_vals, j_vals = calc_kdj_china(df, ContextInfo.kdj_n, ContextInfo.kdj_m1, ContextInfo.kdj_m2)
# 4. 获取当前和上一周期的指标值
# -1 代表当前最新一根 K 线,-2 代表上一根
curr_diff, prev_diff = diff[-1], diff[-2]
curr_dea, prev_dea = dea[-1], dea[-2]
curr_k, prev_k = k_vals[-1], k_vals[-2]
curr_d, prev_d = d_vals[-1], d_vals[-2]
# 5. 判断金叉与死叉
# MACD 金叉: 上一刻 DIF <= DEA, 当前 DIF > DEA
macd_golden_cross = (prev_diff <= prev_dea) and (curr_diff > curr_dea)
# MACD 死叉
macd_dead_cross = (prev_diff >= prev_dea) and (curr_diff < curr_dea)
# KDJ 金叉: 上一刻 K <= D, 当前 K > D
kdj_golden_cross = (prev_k <= prev_d) and (curr_k > curr_d)
# KDJ 死叉
kdj_dead_cross = (prev_k >= prev_d) and (curr_k < curr_d)
# 6. 获取当前持仓
position = get_position(ContextInfo, stock)
# 7. 交易逻辑
# 买入逻辑:MACD 和 KDJ 同时金叉 (双金叉共振)
if macd_golden_cross and kdj_golden_cross:
if position == 0:
# 全仓买入 (示例逻辑,实际请做好资金管理)
# 这里简单演示,每次买入 1000 股
order_shares(stock, 1000, 'fix', df['close'].iloc[-1], ContextInfo, ContextInfo.accID)
print(f"{date_str} [买入] {stock}: MACD与KDJ同时金叉")
# 卖出逻辑:任一指标死叉则平仓 (止盈/止损)
elif (macd_dead_cross or kdj_dead_cross):
if position > 0:
order_shares(stock, -position, 'fix', df['close'].iloc[-1], ContextInfo, ContextInfo.accID)
print(f"{date_str} [卖出] {stock}: 出现死叉信号 (MACD:{macd_dead_cross}, KDJ:{kdj_dead_cross})")
def get_position(ContextInfo, stock_code):
"""
获取某只股票的持仓数量
"""
# 获取持仓对象列表
positions = get_trade_detail_data(ContextInfo.accID, 'stock', 'position')
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
return pos.m_nVolume
return 0
def calc_kdj_china(df, n=9, m1=3, m2=3):
"""
计算符合国内行情的 KDJ 指标
RSV = (CLOSE - LOW_N) / (HIGH_N - LOW_N) * 100
K = (M1-1)/M1 * PREV_K + 1/M1 * RSV
D = (M2-1)/M2 * PREV_D + 1/M2 * K
J = 3*K - 2*D
"""
high = df['high']
low = df['low']
close = df['close']
# 计算 RSV
low_list = low.rolling(window=n, min_periods=1).min()
high_list = high.rolling(window=n, min_periods=1).max()
rsv = (close - low_list) / (high_list - low_list) * 100
# 处理除以0的情况
rsv = rsv.fillna(0)
# 计算 K, D, J
k_vals = []
d_vals = []
# 初始值通常设为 50
k = 50
d = 50
for i in range(len(rsv)):
rsv_val = rsv.iloc[i]
# 迭代计算
k = (m1 - 1) / m1 * k + 1 / m1 * rsv_val
d = (m2 - 1) / m2 * d + 1 / m2 * k
k_vals.append(k)
d_vals.append(d)
k_series = pd.Series(k_vals, index=df.index)
d_series = pd.Series(d_vals, index=df.index)
j_series = 3 * k_series - 2 * d_series
return k_series.values, d_series.values, j_series.values
get_market_data_ex):
get_market_data_ex 接口,这是 QMT 推荐的高效数据接口。count=100:为了保证 MACD(需要较长周期平滑)和 KDJ 计算准确,至少需要获取 100 根 K 线的数据。talib 库,计算速度快且标准。calc_kdj_china 函数。talib 自带的 STOCH 函数计算逻辑与国内略有不同,直接使用可能会导致信号对不上。if macd_golden_cross and kdj_golden_cross: 来判断。order_shares 进行下单。get_trade_detail_data 来查询当前持仓,确保不会重复买入或卖空。ContextInfo.accID 为您的实际资金账号。Q: 为什么 KDJ 指标不用 talib.STOCH?
A: talib.STOCH 是国际标准算法,而国内 A 股软件普遍采用的是一种平滑移动平均算法(迭代法)。直接用 talib 算出来的数值会和您在看盘软件上看到的不一样,导致信号偏差。代码中提供的 calc_kdj_china 函数模拟了国内软件的算法。
Q: 策略没有信号怎么办?
A: "同时金叉"是一个非常严格的条件。如果回测没有交易,建议放宽条件。例如:只要 MACD 趋势向上(DIF > DEA),此时 KDJ 发生金叉即可买入。您可以将买入判断修改为:
# 宽松版买入逻辑
if (curr_diff > curr_dea) and kdj_golden_cross:
# 买入...
Q: 如何在回测中查看买卖点?
A: 代码中包含了 print 语句,回测时请查看底部的“日志”窗口,或者在图表上右键选择“叠加模型”来查看信号。