VNPY 中基於Ta-lib的KDJ策略實現

張國平發表於2018-06-18

VNPY自帶演示策略中,沒有kdj策略,作為一個國內常用策略,這裡講講怎麼實現。

首先,Ta-lib這個python庫裡面並沒有提供kdj策略,估計主要因為這個策略主要在流行,不過ta-lib提供了類似的方法。可以實現KD效果。

那麼首先為class ArrayManager新增一個方法kdj,來輸出KDJ值。我是直接修改ctaTemplate.py 檔案,更合適方法是繼承class ArrayManager。


點選(此處)摺疊或開啟

  1. def kdj(self, fastk_period, slowk_period, slowk_matype ,slowd_period ,slowd_matype, array=False):
  2.         """KDJ指標"""

  3.         slowk, slowd = talib.STOCH(self.high,self.low,self.close,fastk_period, slowk_period,
  4.                                          slowk_matype, slowd_period ,slowd_matype)

  5.         # 求出J值,J = (3 * D) - (2 * K)
  6.         slowj = list(map(lambda x,y: 3 * x - 2 * y, slowk, slowd))
  7.         if array:
  8.             return slowk, slowd, slowj
  9.         return slowk[-1], slowd[-1], slowj[-1]
上面是新增的kdj功能,首先是利用SHOCH計算出kd,在利用kd算出j,輸出kdj三個指標。
SHOCH計算需要每條k線中的最高值,最低值,和結束值引數,作為list數列提供;這個直接使用ArraManger提供值;然後是fastK_period, slowk_period和slowd_period, 這個就是kdj設定中常見的N,M1,M2三個視窗引數, 通常設定是(9,3,3)
然後是slowk_matype, slowd_matype就是平均演算法型別,這裡可以用SMA滑動平均或EMA指數平滑移動平均等。為了和文華一致,用了SMA。
#MA_Type: 0=SMA, 1=EMA, 2=WMA, 3=DEMA, 4=TEMA, 5=TRIMA, 6=KAMA, 7=MAMA, 8=T3 (Default=SMA)
這個後來發現不是一回事,Ta-lib中的SMA中S是simple簡單的意思,SMA是簡單移動平均。和文華SMA不一樣,文華SMA是指數加權移動平均線,這樣的化用EWMA更合適。但是Ta-lib本身並不提供提供EWMA;按照下圖發現預設權重為1; EMA或者較為合適,不過此時權重為2。
後來看著此文又不是一回事,以後有空填這個坑。

VNPY 中基於Ta-lib的KDJ策略實現

j值不提供直接只算,直接用kd值,用3*k-2*d算出j值。對了這些返回都是一堆kdj組list,就是可以畫成一個線。如果是array是false就返回一個點的kdj值。


好了上面就是對 ArrayManager增加一個kdj方法,在下來就是繼承CtaTemplate, 生成策略,這裡基本就是
買入思想就是k或d小於某個閾值時候為超賣,當k大於d,就是描述裡面K線上穿d線時候,開多單。反之k或d值大於閾值多超買,那麼此時k小於d,開空單。。
如果持有多頭,那麼因為j更加敏感,用j值來做平倉指標. 如果持有多單,,如果j小於d,即j線下穿d線時候賣出多單,或者j值快速下降,下降幅度大於定好的jlimit。。
如果持有空頭,同理,如果j大於d,或者j快速增大則平倉。
程式碼如下:


點選(此處)摺疊或開啟

  1. # encoding: UTF-8

  2. """
  3. 這裡的Demo是一個kdj策略實現
  4. """

  5. from __future__ import division

  6. from vnpy.trader.vtConstant import EMPTY_STRING, EMPTY_FLOAT
  7. from vnpy.trader.app.ctaStrategy.ctaTemplate import (CtaTemplate,
  8.                                                      BarGenerator,
  9.                                                      ArrayManager)
  10. from talib import MA_Type


  11. ########################################################################
  12. class KDJStrategy(CtaTemplate):
  13.     """KDJ策略Demo"""
  14.     className = 'KDJStrategy'
  15.     author = u'BillyZhang'

  16.     # 策略引數
  17.     fastk_period = 9
  18.     slowk_period = 3
  19.     slowk_matype = MA_Type.EMA
  20.     slowd_period = 3
  21.     slowd_matype = MA_Type.EMA
  22.     kdlimit = 20
  23.     jlimit = 10
  24.     initDays = 10
  25.     fixedSize = 1
  26.     barmins = 15

  27.     # 策略變數
  28.     k = 0
  29.     d = 0
  30.     j = 0

  31.     # 引數列表,儲存了引數的名稱
  32.     paramList = ['name',
  33.                  'className',
  34.                  'author',
  35.                  'vtSymbol',
  36.                  'fastk_period',
  37.                  'slowk_period',
  38.                  'slowk_matype',
  39.                  'slowd_period',
  40.                  'slowd_matype',
  41.                  'fixedSize'
  42.                  'barmins'
  43.                  ]

  44.     # 變數列表,儲存了變數的名稱
  45.     varList = ['inited',
  46.                'pos',
  47.                'k',
  48.                'd',
  49.                'j']

  50.     # 同步列表,儲存了需要儲存到資料庫的變數名稱
  51.     syncList = ['pos']

  52.     # ----------------------------------------------------------------------
  53.     def __init__(self, ctaEngine, setting):
  54.         """Constructor"""
  55.         super(KDJStrategy, self).__init__(ctaEngine, setting)

  56.         self.bg = BarGenerator(self.onBar, self.barmins, self.onXminBar)
  57.         self.am = ArrayManager()

  58.         # 注意策略類中的可變物件屬性(通常是list和dict等),在策略初始化時需要重新建立,
  59.         # 否則會出現多個策略例項之間資料共享的情況,有可能導致潛在的策略邏輯錯誤風險,
  60.         # 策略類中的這些可變物件屬性可以選擇不寫,全都放在__init__下面,寫主要是為了閱讀
  61.         # 策略時方便(更多是個程式設計習慣的選擇)

  62.     # ----------------------------------------------------------------------
  63.     def onInit(self):
  64.         """初始化策略(必須由使用者繼承實現)"""
  65.         self.writeCtaLog(u'KDJ策略初始化')

  66.         initData = self.loadBar(self.initDays)
  67.         for bar in initData:
  68.             self.onBar(bar)

  69.         self.putEvent()

  70.     # ----------------------------------------------------------------------
  71.     def onStart(self):
  72.         """啟動策略(必須由使用者繼承實現)"""
  73.         self.writeCtaLog(u'KDJ策略啟動')
  74.         self.putEvent()

  75.     # ----------------------------------------------------------------------
  76.     def onStop(self):
  77.         """停止策略(必須由使用者繼承實現)"""
  78.         self.writeCtaLog(u'KDJ策略停止')
  79.         self.putEvent()

  80.     # ----------------------------------------------------------------------
  81.     def onTick(self, tick):
  82.         """收到行情TICK推送(必須由使用者繼承實現)"""
  83.         self.bg.updateTick(tick)

  84.     # ----------------------------------------------------------------------
  85.     def onBar(self, bar):
  86.         """收到Bar推送(必須由使用者繼承實現)"""
  87.         self.bg.updateBar(bar)

  88.     # ----------------------------------------------------------------------
  89.     def onXminBar(self, bar):
  90.         """收到Bar推送(必須由使用者繼承實現)"""
  91.         am = self.am
  92.         am.updateBar(bar)
  93.         if not am.inited:
  94.             return

  95.         # 計算kdj數值
  96.         slowk, slowd, slowj = am.kdj(self.fastk_period, self.slowk_period, self.slowk_matype,
  97.                                      self.slowd_period, self.slowd_matype, array=True)

  98.         self.k = slowk[-1]
  99.         self.d = slowd[-1]
  100.         self.j = slowj[-1]
  101.         self.jdif = slowj[-1] - slowj[-2]

  102.         tradeindictor = 0
  103.         if self.k > (100 - self.kdlimit) or self.d > (100 - self.kdlimit):
  104.             tradeindictor = -1
  105.         if self.k < self.kdlimit or self.d < self.kdlimit:
  106.             tradeindictor = 1

  107.         # 當前無倉位,傳送開倉委託
  108.         if self.pos == 0:
  109.             self.intraTradeHigh = bar.high
  110.             self.intraTradeLow = bar.low

  111.             # 如果k值大於d值均線,開多單;反之,如果如果k值小於d值時候開空單
  112.             if self.k > self.d and tradeindictor == 1:
  113.                 self.buy(bar.close, self.fixedSize, False)

  114.             elif self.k < self.d and tradeindictor == -1:
  115.                 self.short(bar.close, self.fixedSize, False)

  116.         # 持有多頭倉位; 如果j小於d,或者j最近兩個k線,j值下跌超過jlimi平倉, :
  117.         elif self.pos > 0:
  118.             if self.j < self.d or self.jdif < -1 *self.jlimit:
  119.                 self.sell(bar.close * 1.03, abs(self.pos))

  120.         # 持有空頭倉位;如果j大於d,或j快速上揚 平倉;
  121.         elif self.pos < 0:
  122.             if self.j > self.d or self.jdif > self.jlimit:
  123.                 self.cover(bar.close * 0.97, abs(self.pos))

  124.         # 同步資料到資料庫
  125.         self.saveSyncData()

  126.         # 發出狀態更新事件
  127.         self.putEvent()

  128.     # ----------------------------------------------------------------------
  129.     def onOrder(self, order):
  130.         """收到委託變化推送(必須由使用者繼承實現)"""
  131.         # 對於無需做細粒度委託控制的策略,可以忽略onOrder
  132.         pass

  133.     # ----------------------------------------------------------------------
  134.     def onTrade(self, trade):
  135.         """收到成交推送(必須由使用者繼承實現)"""
  136.         # 對於無需做細粒度委託控制的策略,可以忽略onOrder
  137.         pass

  138.     # ----------------------------------------------------------------------
  139.     def onStopOrder(self, so):
  140.         """停止單推送"""
  141.         pass

 




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2156307/,如需轉載,請註明出處,否則將追究法律責任。

相關文章