STOCH(KD 线)策略(附代码!!!)

策略思路

随机指数(KD)是乔治·莱恩在多年前发明的指导金融交易的指标,近年来无论是在股票还是期货市场都得到了广泛的运用。专注于研究 k 线图中的 k 线和 d 线,在设计中综合了动量观念、相对强弱指数和移动平均线的一些优点,计算时主要研究高低价位与收盘价之间的关系,通过计算当天或最近几天的最高价、最低价以及收盘价等价格波动的真实波幅,反应价格走势的强弱和超买超卖现象。

随机指标(KDJ)的主要理论依据是:当价格上涨时,收盘价倾向于接近当日价格区间的上端;相反,在下降趋势中,收盘价倾向于接近当日价格区间的下端。随机指标(KDJ)在设计中充分考虑价格波动的随机振幅与中短期波动的测算,使其短期测市功能比移动平均线更加准确有效,在市场短期超买超卖的预测方面又比相对强弱指标敏感。因此,这一指标被投资者广泛采用。

STOCH(Stochastic Oscillator Slow)就是利用 KDJ 指标中的 KD 指标指导股票交易的策略,本策略基于云宽客平台,利用 talib(Technical Analysis Library)技术分析库中的 STOCH 函数,可以很方便的实现 STOCH 策略。

计算方法

虽然可以直接利用 talib 中的 STOCH 函数避开繁琐的指标计算,但我们还是有必要了解 KD 指标中 K 线和 D 线的计算方法的,这有利于我们理解指标的本质,更好的将指标与市场行情结合,构建自己的量化策略。

随机线一共有 4 种,分别是:FASTK,FASTD,SLOWK,SLOWD。D 是由 K 经过变化得到的。具体计算如下:

FASTK(K2740b537bc604c2096788f33e3fb335a-image.png x 100
FASTD(FastDperiod)= MA Smoothed FASTK over FastDperiod
SLOWK(SlowKperiod)= MA smoothed FASTK over SlowKperiod
SLOWD(SlowDperiod)= MA smoothed SLOWK over SlowDperiod

使用方法

快速确认线 K 线大于 90,慢速确认线 D 线大于 80 时,认为股票超买,执行卖出操作;
快速确认线 D 线小于 10,慢速确认线 D 线小于 20 时,认为股票超卖,执行买入操作。
注:利用 talib 的 STOCH 函数,可以避免繁琐的 KD 指标计算,再结合云宽客方便的数据接口,可以大大缩短策略开发时间,提升开发效率。

基于上述交易思路和 Quantdesk 平台的策略实现

# -*- coding:utf-8 -*-

from CloudQuant import SDKCoreEngine  # 导入量子金服SDK
from CloudQuant import AssetType  # 导入资产类型
from CloudQuant import QuoteCycle  # 导入回测周期
import numpy as np  # 导入numpy用于数据对象的接收和处理
import talib  # 导入TA_Lib技术分析库,简化相关指标的计算

config = {
'username': 'xxx',  # 访问SDK所使用的用户名,即云宽客的登录名
'password': 'xxxxxxxxxx',  # 访问SDK所使用的用户名对应的密码
'rootpath': 'd:/cStrategy/',  # 基础数据存放的路径,即客户端所在路径
'initCapitalStock': 10000000,  # 回测开始时的初始资金
'startDate': 20140101,  # 回测开始时的日期
'endDate': 20150601,  # 回测结束时的日期
'cycle': QuoteCycle.D ,  # 进行回测前的时间粒度,定义在枚举QuoteCycle中
'assetType': AssetType.Stock,  # 回测资产类型
'feeRate': 0.001,  # 手续费率
'feeLimit': 5,  # 最低手续费
'strategyName': 'STOCH_0724',  # 策略名
'dealByVolume': True  # 撮合是否考虑实际交易量
}

def initial(sdk):  # 定义整个回测开始前的操作
pass  # 本策略无回测开始前操作

def initPerDay(sdk):  # 定义每天回测开始前的操作
lclose_raw = sdk.getFactorData('LZ_CN_STKA_QUOTE_TCLOSE')  # 获取今日收盘价数据,数据只取到回测时点前一天,numpy.ndarray类型的对象
 high_raw = sdk.getFactorData('LZ_CN_STKA_QUOTE_THIGH')  # 获取今日最高价数据,数据只取到回测时点前一天,numpy.ndarray类型的对象
 low_raw = sdk.getFactorData('LZ_CN_STKA_QUOTE_TLOW')  # 获取今日最低价数据,数据只取到回测时点前一天,numpy.ndarray类型的对象
 if lclose_raw is None or high_raw is None or low_raw is None:  # 有时因子文件读入会出错,返回None值,需要提前进行判断
return
# 以回测前一天的沪深300成分股作为交易标的
temp = sdk.getFactorData('LZ_CN_STKA_INDEX_HS300MEMBER')  # 获取沪深300成分股标识数据,成分股标识为'1',非成分股为'NaN'
if temp is None:  # 有时因子文件读入会出错,返回None值,需要提前进行判断
return
temp1 = temp[-1]  # 获取回测前一交易日的成分股标识
loc = []
condition = [1 if j == '1' else 0 for j in temp1]
for k in range(len(condition)):
if condition[k] == 1:
    loc.append(k)  # 生成成分股下标列表

stock_list = sdk.getStockList()  # 获取6位股票代码数据,返回对象数据类型为列表,所有股票相关的因子文件均按此顺序排列股票交易代码
if stock_list is None:  # 有时因子文件读入会出错,返回None值,需要提前进行判断
return
deal_stock = list(np.array(stock_list)[loc])  # 取出回测前一天的沪深300成分股6位交易代码
sdk.setGlobal('DEAL_STOCK', deal_stock)  # 设置为全局变量供每日策略使用
# 取出沪深300成分股最近30个交易日的收盘价、最高价、最低价,并设置为全局变量
lclose_deal = [lclose_raw[i][loc] for i in range(-30,0)]
high_deal = [high_raw[i][loc] for i in range(-30,0)]
low_deal = [low_raw[i][loc] for i in range(-30,0)]
# 生成KD值字典
KD_dict = {}
for k in range(len(deal_stock)):
lclose = np.array([[lclose_deal[i][j] for i in range(30)] for j in range(len(deal_stock))])
high = np.array([[high_deal[i][j] for i in range(30)] for j in range(len(deal_stock))])
low = np.array([[low_deal[i][j] for i in range(30)] for j in range(len(deal_stock))])
slowk,slowd = talib.STOCH(high[k], low[k], lclose[k])  # talib中的STOCH函数,具体参数可访问talib官网
KD_dict[deal_stock[k]] = [slowk[-1], slowd[-1]]
sdk.setGlobal('KD_DICT', KD_dict)

def strategy(sdk):  # 策略主体,每天以所设时间粒度的频率进行调用
# 获取全局变量
deal_stock = sdk.getGlobal('DEAL_STOCK')
KD_dict = sdk.getGlobal('KD_DICT')
# 获取所有交易标的盘口数据
quotes = sdk.getQuotes(deal_stock, tsInd = AssetType.Stock)
if quotes is None:  # 有时盘口数据读入会出错,返回None值,需要提前进行判断
return
# 获取持仓数据
pos = sdk.getPositions()
if pos is None:  # 有时持仓数据读入会出错,返回None值,需要提前进行判断
return
pos_code = [i.code for i in pos]  # 生成持仓标的6位代码列表

deal_diff = np.setdiff1d(quotes.keys(), pos_code)  # 只对当前未持仓股票判断是否满足买入条件
if pos:  # 如果持仓列表非空
for stockrecord in pos:
    if stockrecord.code not in deal_stock:  # 沪深300成分股每年会进行2次调整,对于持有的不再是成分股的股票,直接卖出
        quote = sdk.getQuote(stockrecord.code)  # 获取持有个股的盘口数据
        if quote is None:  # 有时盘口数据读入会出错,需要提前进行判断
            continue
        sdk.makeOrder(stockrecord.code, quote.current, stockrecord.optPosition, -1)  # 平仓,卖出所有可平仓量
    elif stockrecord.code in deal_stock and stockrecord.code in quotes.keys():  # 是成分股且能取到行情数据
        kd = KD_dict[stockrecord.code]  # 获取KD值
        if kd[0] >= 90 and kd[1] >= 80:  # 快速确认线k线大于90,慢速确认线d线大于80,超买,执行卖出操作
            sdk.makeOrder(stockrecord.code, quotes[stockrecord.code].current,
                          stockrecord.optPosition, -1)
    else:
        pass

 account = sdk.getAccountInfo()  # 获取账户资产明细
 if account is None:  # 有时账户资产明细读入会出错,需要提前进行判断
return
 budget = account.availableCash  # 将当前可用现金作为建仓预算
if list(deal_diff):  # 存在未持有的成分股
stock_to_buy = []
for code in deal_diff:
    kd = KD_dict[code]  # 获取KD值
    if kd[0] <= 10 and kd[1] <= 20:  # 快速确认线k线小于10,慢速确认线d线小于20,超卖,执行买入操作
        stock_to_buy.append(code)
if stock_to_buy:
    # 生成批量下单委托列表,列表元素按交易标的6位代码,交易价格,交易量,交易类型的顺序排列
    # 本策略执行现价委托,在所有待买股票间平均分配资金,执行买入操作
    orders = [[i, quotes[i].current,
               int(budget / len(stock_to_buy) / quotes[i].current / 100) * 100, 1] for i in stock_to_buy if quotes[i].current != 0]
    sdk.makeOrders(orders, tsInd = AssetType.Stock) # 批量下单买入

def main():
# 将策略函数加入config
config['initial'] = initial
config['strategy'] = strategy
config['preparePerDay'] = initPerDay
# 启动SDK
SDKCoreEngine(**config).run()

if __name__ == "__main__":
main()

回测结果
回测时间:20140101—20150601
fb78234b98ef4f0ba28e56e090da6971-image.png
业绩基准:沪深 300
c1572c1f0e534e619ad761d13a602d98-image.png
业绩基准:中证 500
7e146f09558243fb8ee15483558507e4-image.png

回测结果分析与总结

可以看出,STOCH(KD 线)策略的收益率较高,能够及时捕捉利好行情,实现收益。与此同时,观察 14 年 1 月至 14 年 6 月的回测结果可以发现,当市场小幅走弱时,STOCH 策略表现优于市场,大部分时间超额收益为正,这是 KD 指标对市场行情上升和下降趋势敏感的结果,当盘面处于整体下降通道时,STOCH 能够及时规避受市场影响较大而出现大幅下跌的股票,买入表现优于市场的股票。然而,STOCH 的表现在大盘出现强势上涨时则不尽如人意:仅能追随大盘涨势,无法跑赢市场实现超额收益。由于缺少止盈和止损机制,也没有引入股指期货交易对冲风险,策略的最大回撤达到 -14.10%,风险较高。但超过 1 的夏普比也对高风险做出了一定的补偿。

注意:由于 STOCH 策略对市场趋势较为敏感,故能够在熊市时及时离市保存本金,虽然无法实现较大收益,但也避免了本金的大额损失,延长回测时间可以明显发现其交易特点(尤其是在熊市时能够较早离市或谨慎投资,避免本金亏损)。

7c4fc2666bd243dab9c17231bcfca300-image.png
业绩基准:沪深 300

3601f723bcf646c8b54c23b3a08f9750-image.png
业绩基准:中证 500

6a12fd0e181046eda50744bca7d25465-image.png
由于随机指标(KDJ)是在 WMS 指标的基础上发展起来的,所以 KD 指标有 WMS 指标的一些特性。在反映股市价格变化时,WMS 最快,K 其次,D 最慢。在使用随机指标(KD)时,往往称 K 指标为快指标,D 指标为慢指标。K 指标反应敏捷,但容易出错,D 指标反应稍慢,但稳重可靠。

关于回测平台、历史数据,可以在这里下载:http://www.yunkuanke.com/#/introduce
这个平台的数据都是经过清洗的,而且策略不会外露,有保密系统,还是比较安全的。
想学习更多量化策略,也可以来这里看看:http://www.yunkuanke.com/#/lzClass
想学习更多关于量化投资策略相关内容的,可以关注微信:量化投资与金融科技(微信号:QuantumFintech)
微信二维码: