"""導入常用模塊"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime
from environment import * # 導入大樹工作室開發的回測模塊
KDJ指標計算函數
import talib as tl
from functools import reduce
# SMA計算函數
def SMA(close, timeperiod) :
close = np.nan_to_num(close)
return reduce(lambda x, y: ((timeperiod - 1) * x + y) / timeperiod, close)
# KDJ計算函數
def KDJ(high, low, close, fastk_period, slowk_period, fastd_period) :
kValue, dValue = tl.STOCHF(high, low, close, fastk_period, fastd_period=1, fastd_matype=0)
kValue = np.array(list(map(lambda x : SMA(kValue[:x], slowk_period), range(1, len(kValue) + 1))))
dValue = np.array(list(map(lambda x : SMA(kValue[:x], fastd_period), range(1, len(kValue) + 1))))
jValue = 3 * kValue - 2 * dValue
func = lambda arr : np.array([0 if x < 0 else (100 if x > 100 else x) for x in arr])
kValue = func(kValue)
dValue = func(dValue)
jValue = func(jValue)
return kValue, dValue, jValue
# 獲取某標的的KDJ信息
def get_kdj(stock, count, end_date, unit):
data = get_bars(security=stock, count=count, unit=unit,
include_now=False,
end_dt=end_date, fq_ref_date=None)
close = data['close']
open = data['open']
high = data['high']
low = data['low']
return KDJ(high, low, close, 9, 3, 3)
KDJ的使用方法可以參考:https://www.joinquant.com/view/community/detail/16464
KDJ指標使用方法 一
KDJ是一種擺動指標,20與80的位置是我們對當前超買超賣狀態的最基本判斷。接下來的擇時條件如下:
- 當D線處於20位置以下時,買進;
- 當D線處於80位置以上時,賣出;
另外,20與80的位置並不是硬性指標,我們可以爲其指定一個浮動範圍;我們將這個範圍N值定在1%~9%之間,然後做回測,並查看其結果。
trade_list = []
N =[n/100 for n in range(1, 10)]
for n in N:
"""初始化以下內容"""
context = Context() # 賬戶對象
order = Order(context) # 下單對象
trade = Trade(context, order) # 回測對旬
context.start_date = '2005-05-01'
context.end_date = '2019-02-22'
context.universe = ['000300.XSHG']
context.base = '000300.XSHG'
"""策略主體"""
def handle(context, order):
stock = context.universe[0]
current_date = trade.context.current_dt
kdj_day = get_kdj(stock, 30, current_date, '1d')
line_bottm = 20 * (1+n)
line_top = 80 * (1-n)
k1 = kdj_day[0][-1]
d1 = kdj_day[1][-1]
j1 = kdj_day[2][-1]
k2 = kdj_day[0][-2]
d2 = kdj_day[1][-2]
j2 = kdj_day[2][-2]
close = get_price(security=stock,
end_date=context.current_dt,
frequency='daily',
fields=None,
skip_paused=False,
fq='pre',
count=5)['close']
if d2 > line_bottm and d1 < line_bottm :
if stock in context.position.keys():
return
order.buy(stock, close[-1], context.cash // close[-1])
elif d2 < line_top and d1 > line_top:
if stock not in context.position.keys():
return
order.sell(stock, close[-1], context.position[stock]['count'])
"""執行策略"""
trade.trade(handle, show=False, log=True)
trade_list.append(trade)
# 展示
Trade.show_ratio_compare('n', N, trade_list, 3, 3)
End Time : 2019-03-10 16:01:27.192853, Elapsed Time: 0:00:18.351210
End Time : 2019-03-10 16:01:45.031913, Elapsed Time: 0:00:17.838861
End Time : 2019-03-10 16:02:02.843599, Elapsed Time: 0:00:17.811513
End Time : 2019-03-10 16:02:20.318987, Elapsed Time: 0:00:17.475214
End Time : 2019-03-10 16:02:38.392089, Elapsed Time: 0:00:18.072942
End Time : 2019-03-10 16:02:55.567773, Elapsed Time: 0:00:17.175470
End Time : 2019-03-10 16:03:13.802732, Elapsed Time: 0:00:18.234765
End Time : 2019-03-10 16:03:32.918843, Elapsed Time: 0:00:19.115479
End Time : 2019-03-10 16:03:51.401436, Elapsed Time: 0:00:18.482391
Trade.show_result('n', N, trade_list)
從上圖來看,kdj在超買與超賣區的反應對於滬深300指數來說,反應並不好。
KDJ使用方法 二
- 當k線與d線形成金叉時買進;
- 當k線與d線形成死叉時賣出;
金叉與死叉形成時,最小的時間段是前天與當天的值的比較,這個時間段也可以被當作一種參數D,其取值範圍是1~9天。查看回測結果:
trade_list = []
D = range(1, 10)
for d in D:
"""初始化以下內容"""
context = Context() # 賬戶對象
order = Order(context) # 下單對象
trade = Trade(context, order) # 回測對旬
context.start_date = '2005-05-01'
context.end_date = '2019-02-22'
context.universe = ['000300.XSHG']
context.base = '000300.XSHG'
"""策略主體"""
def handle(context, order):
stock = context.universe[0]
current_date = trade.context.current_dt
kdj_day = get_kdj(stock, 30, current_date, '1d')
k1 = kdj_day[0][-1]
d1 = kdj_day[1][-1]
j1 = kdj_day[2][-1]
k2 = kdj_day[0][-d-1]
d2 = kdj_day[1][-d-1]
j2 = kdj_day[2][-d-1]
close = get_price(security=stock,
end_date=context.current_dt,
frequency='daily',
fields=None,
skip_paused=False,
fq='pre',
count=5)['close']
if k2 < d2 and k1 > d1:
if stock in context.position.keys():
return
order.buy(stock, close[-1], context.cash // close[-1])
elif k2 > d2 and k1 < d1:
if stock not in context.position.keys():
return
order.sell(stock, close[-1], context.position[stock]['count'])
"""執行策略"""
trade.trade(handle, show=False, log=True)
trade_list.append(trade)
# 展示
Trade.show_ratio_compare('d', D, trade_list, 3, 3)
End Time : 2019-03-10 16:04:13.577469, Elapsed Time: 0:00:20.034098
End Time : 2019-03-10 16:04:33.005696, Elapsed Time: 0:00:19.428044
End Time : 2019-03-10 16:04:52.154929, Elapsed Time: 0:00:19.149037
End Time : 2019-03-10 16:05:12.142793, Elapsed Time: 0:00:19.987659
End Time : 2019-03-10 16:05:31.073016, Elapsed Time: 0:00:18.930031
End Time : 2019-03-10 16:05:51.557113, Elapsed Time: 0:00:20.483901
End Time : 2019-03-10 16:06:10.902478, Elapsed Time: 0:00:19.345159
End Time : 2019-03-10 16:06:29.924519, Elapsed Time: 0:00:19.021839
End Time : 2019-03-10 16:06:48.871547, Elapsed Time: 0:00:18.946834
Trade.show_result('d', D, trade_list)
從上面結果來看,當d=2時回測的效果比較好,但整體看來,仍舊沒有跑贏大盤。因此,效果也並不理想。
KDJ使用方法 三
- 當j線與價格發生底背離時,買進;
- 當j線與價格發生頂背離時,賣出;
背離計算的是一段時間內,j線的趨勢與價格的趨勢在方向上不一致,把時間段設爲D值,取值範圍是1~9天。回測並查看結果:
trade_list = []
D = range(1, 10)
for d in D:
"""初始化以下內容"""
context = Context() # 賬戶對象
order = Order(context) # 下單對象
trade = Trade(context, order) # 回測對旬
context.start_date = '2005-05-01'
context.end_date = '2019-02-22'
context.universe = ['000300.XSHG']
context.base = '000300.XSHG'
"""策略主體"""
def handle(context, order):
stock = context.universe[0]
current_date = trade.context.current_dt
kdj_day = get_kdj(stock, 30, current_date, '1d')
k1 = kdj_day[0][-1]
d1 = kdj_day[1][-1]
j1 = kdj_day[2][-1]
k2 = kdj_day[0][-d-1]
d2 = kdj_day[1][-d-1]
j2 = kdj_day[2][-d-1]
close = get_price(security=stock,
end_date=context.current_dt,
frequency='daily',
fields=None,
skip_paused=False,
fq='pre',
count=20)['close']
if j1 > j2 and close[-1] < close[-d-1]:
if stock in context.position.keys():
return
order.buy(stock, close[-1], context.cash // close[-1])
elif j1 < j2 and close[-1] > close[-d-1]:
if stock not in context.position.keys():
return
order.sell(stock, close[-1], context.position[stock]['count'])
"""執行策略"""
trade.trade(handle, show=False, log=True)
trade_list.append(trade)
# 展示
Trade.show_ratio_compare('d', D, trade_list, 3, 3)
End Time : 2019-03-10 16:07:10.097162, Elapsed Time: 0:00:19.401507
End Time : 2019-03-10 16:07:30.041172, Elapsed Time: 0:00:19.943802
End Time : 2019-03-10 16:07:51.642053, Elapsed Time: 0:00:21.600708
End Time : 2019-03-10 16:08:12.491229, Elapsed Time: 0:00:20.848990
End Time : 2019-03-10 16:08:32.879961, Elapsed Time: 0:00:20.388533
End Time : 2019-03-10 16:08:53.409177, Elapsed Time: 0:00:20.528626
End Time : 2019-03-10 16:09:12.488550, Elapsed Time: 0:00:19.079192
End Time : 2019-03-10 16:09:32.450308, Elapsed Time: 0:00:19.961573
End Time : 2019-03-10 16:09:52.517584, Elapsed Time: 0:00:20.067092
Trade.show_result('d', D, trade_list)
從上圖來看,j線的背離效果也不好,k線與d線的背離效果這裏就不做加測了。
下面展示本次研究效果最好的參數,即金叉與死叉條件下,d=2時的結果。
"""初始化以下內容"""
context = Context() # 賬戶對象
order = Order(context) # 下單對象
trade = Trade(context, order) # 回測對旬
context.start_date = '2005-05-01'
context.end_date = '2019-02-22'
context.universe = ['000300.XSHG']
context.base = '000300.XSHG'
"""策略主體"""
def handle(context, order):
stock = context.universe[0]
current_date = trade.context.current_dt
kdj_day = get_kdj(stock, 30, current_date, '1d')
k1 = kdj_day[0][-1]
d1 = kdj_day[1][-1]
j1 = kdj_day[2][-1]
k2 = kdj_day[0][-2-1]
d2 = kdj_day[1][-2-1]
j2 = kdj_day[2][-2-1]
close = get_price(security=stock,
end_date=context.current_dt,
frequency='daily',
fields=None,
skip_paused=False,
fq='pre',
count=20)['close']
if k2 < d2 and k1 > d1:
if stock in context.position.keys():
return
order.buy(stock, close[-1], context.cash // close[-1])
elif k2 > d2 and k1 < d1:
if stock not in context.position.keys():
return
order.sell(stock, close[-1], context.position[stock]['count'])
"""執行策略"""
trade.trade(handle, show=True, log=True)
End Time : 2019-03-10 16:11:04.901546, Elapsed Time: 0:00:20.362267