VNPY 單品種期貨的網格交易策略的實現

張國平發表於2018-06-30
這裡做了單品種期貨網格交易策略實現。
首先按照過去的n條k線計算出簡單評價價SMA基準線,然後按照標準差STD,算出最高線和最低線,然後在之間定出一組通道區間。
當bar.close在通道中時候,下個bar打到上軌開多單,打到下軌空單。


這裡採用了均量交易法,就是每筆下單手數都是一樣,並非金字塔式下單。
空倉時候,每次突破上線是開多單,突破下線是開空單;
有多單時候,突破上線加多單,突破下線情況清空所有多單,當多單到達定義的最大手數不再下單
有空單時候,突破下線加空單,突破上線清空所有空單,當空單到達定義的最大手數不再下單
為防止在一個線上下波動,造成重複開平倉情況,如果突破平倉,比如平多單,後面n個bar不能再開多單,只能開空單;反之平空單後,
後面n個bar只能開多單。
現在這個策略很粗糙,只做實現邏輯分析,可以回測,考慮涉及倉位控制,別實盤。

點選(此處)摺疊或開啟

  1. # encoding: UTF-8
  2. from __future__ import division



  3. from vnpy.trader.vtGateway import *
  4. from math import isnan
  5. import numpy as np
  6. import pandas as pd
  7. from vnpy.trader.app.ctaStrategy.ctaTemplate import (CtaTemplate, TargetPosTemplate,
  8.                                                      BarGenerator,
  9.                                                      ArrayManager)


  10. class GridStrategy(CtaTemplate):
  11.     className = 'GridStrategy'
  12.     author = u'BillyZhang'

  13.     # 策略引數
  14.     historyBars = 200 # 歷史資料大小,用來確定網格基準線
  15.     initDays = 20 # 初始化資料所用的天數,隨著歷史資料大小要改變
  16.     gridlines = 10 # 網格線數量,單邊數量
  17.     ordersize = 10 # 最大持倉數量
  18.     order = 1 # 每次下單手數
  19.     barMins = 30 #bar的時間
  20.     frozenBars = 1 #平倉後,frozenBars個bar不再開反向單
  21.     atrWindow = 30 # ATR視窗數
  22.     slMultiplier = 5.0 # 計算止損距離的乘數

  23.     # 基本變數
  24.     upline = 0 #當前上線
  25.     bottomline = 0 #當前下線
  26.     frozen = 0 #當前是否凍結開反向單
  27.     intraTradeHigh = 0
  28.     intraTradeLow = 0
  29.     atrValue = 0



  30.     # 引數列表,儲存了引數的名稱
  31.     paramList = ['name',
  32.                  'className',
  33.                  'author',
  34.                  'vtSymbol',
  35.                  'historyBars'
  36.                  'initDays',
  37.                  'gridlines',
  38.                  'barMins',
  39.                  'order',
  40.                  'ordersize',
  41.                  'atrWindow',
  42.                  'slMultiplier'
  43.                  ]
  44.     # 變數列表,儲存了變數的名稱
  45.     varList = ['inited',
  46.                'trading',
  47.                'pos',
  48.                'frozen',
  49.                'upline',
  50.                'bottomline'
  51.                'atrValue']
  52.     # 同步列表,儲存了需要儲存到資料庫的變數名稱
  53.     syncList = ['pos',
  54.                 'frozen']

  55.     # ----------------------------------------------------------------------
  56.     def __init__(self, ctaEngine, setting):
  57.         """Constructor"""
  58.         super(GridStrategy, self).__init__(ctaEngine, setting)
  59.         self.bg = BarGenerator(self.onBar, self.barMins, self.onXminBar) # 建立K線合成器物件
  60.         self.am = ArrayManager(self.historyBars + 50)

  61.     # ----------------------------------------------------------------------
  62.     def onInit(self):
  63.         """初始化策略(必須由使用者繼承實現)"""
  64.         self.writeCtaLog(u'%s策略初始化' % self.name)
  65.         # 載入歷史資料,並採用回放計算的方式初始化策略數值
  66.         initData = self.loadBar(self.initDays)
  67.         for bar in initData:
  68.             self.onBar(bar)
  69.         self.putEvent()

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

  74.     def onStop(self):
  75.         """停止策略(必須由使用者繼承實現)"""
  76.         self.writeCtaLog(u'%s策略停止' % self.name)
  77.         self.putEvent()



  78.     # -----------------------------------------------------------------------
  79.     def onXminBar(self, bar):
  80.         """收到X分鐘K線"""
  81.         # 全撤之前發出的委託
  82.         self.cancelAll()
  83.         # 儲存K線資料
  84.         am = self.am
  85.         am.updateBar(bar)
  86.         if not am.inited:
  87.             return
  88.         # 這裡採用了均量交易法,就是每筆。
  89.         # 空倉時候,每次突破上線是開多單,突破下線是開空單;
  90.         # 有多單時候,突破上線加多單,突破下線情況清空所有多單,
  91.         # 有空單時候,突破下線加空單,突破上線清空所有空單,
  92.         # 為防止在一個線上下波動,造成重複開平倉情況,如果突破平倉,比如平多單,後面n個bar不能再開多單,只能開空單;反之平空單後,
  93.         # 後面n個bar只能開多單。
  94.         # 計算網格,返回通道佇列, 再算出當前點位所在通道,0為最下通道,2*self.gridlines - 1為最上通道
  95.         baseline = self.am.sma(self.historyBars)
  96.         # 過去300的標準差,按照頂一個gridlines取整做出一個佇列
  97.         intervallist = baseline+ np.array([n * 1.00 / self.gridlines for n in range(-1 * self.gridlines, self.gridlines + 1)]) * self.am.std(self.historyBars)

  98.         griploc = pd.cut([bar.close], intervallist, labels=[nx for nx in range(0,2*self.gridlines)])[0]
  99.         # 如果返回為nan,說明現在bar.close在標準差範圍以外,如果沒有倉位,先不處理;如果有,按照ATR波動移動止盈
  100.         if isnan(griploc):
  101.             # 持有多頭倉位
  102.             if self.pos > 0:
  103.                 self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
  104.                 self.intraTradeLow = bar.low
  105.                 self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier
  106.                 self.sell(self.longStop, abs(self.pos), True)
  107.             # 持有空頭倉位
  108.             elif self.pos < 0:
  109.                 self.intraTradeHigh = bar.high
  110.                 self.intraTradeLow = min(self.intraTradeLow, bar.low)
  111.                 self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier
  112.                 self.cover(self.shortStop, abs(self.pos), True)
  113.             return
  114.         #返回上下線:
  115.         self.upline = intervallist[griploc + 1]
  116.         self.bottomline = intervallist[griploc]

  117.         # 空倉時候,每次突破上線是開多單,突破下線是開空單;
  118.         # 如果此時在最下一個通道,此時只掛往上的多單, 如果在最上面通道,此時只掛往下空單;如果在中間的,則同時開上下單
  119.         if self.pos == 0:
  120.             if griploc ==0:
  121.                 self.buy(self.upline, self.order, True)
  122.             elif griploc == 2*self.gridlines - 1:
  123.                 self.short(self.bottomline,self.order,True)
  124.             else:
  125.                 #此時如果frozen 為0, 直接開上下單:
  126.                 if self.frozen == 0:
  127.                     self.buy(self.upline, self.order, True)
  128.                     self.short(self.bottomline, self.order, True)
  129.                 #此時如果大於0,只能開空單,如果小於0,只能開多單
  130.                 elif self.frozen > 0:
  131.                     self.frozen = self.frozen -1
  132.                     self.short(self.bottomline, self.order, True)
  133.                 elif self.frozen < 0:
  134.                     self.frozen = self.frozen + 1
  135.                     self.buy(self.upline, self.order, True)
  136.         #如果持有多倉時候,如果在中間通道,同時開上下單;如果最高點位不再開單,突破最大標準差高點,
  137.         elif self.pos > 0:
  138.             # 在最下通道不可能有多單,只用考量在中間段,pos 小於ordersize可以增多倉,否則只能向下平倉;和最高段情況,最高段設定往下平倉,
  139.             if griploc == 2*self.gridlines - 1:
  140.                 self.intraTradeHigh = bar.high
  141.                 self.sell(self.bottomline, abs(self.pos), True)
  142.             else:
  143.                 if abs(self.pos) < self.ordersize:
  144.                     self.buy(self.upline, self.order, True)
  145.                     self.sell(self.bottomline, abs(self.pos), True)
  146.                 else:
  147.                     self.sell(self.bottomline, abs(self.pos), True)
  148.         elif self.pos < 0:
  149.             # 最上通道通道不可能有空單,只用考慮中間段,和最低檔情況
  150.             if griploc == 0:
  151.                 self.intraTradeLow = bar.low
  152.                 self.cover(self.upline,abs(self.pos),True)
  153.             else:
  154.                 if abs(self.pos) < self.ordersize:
  155.                     self.cover(self.upline, abs(self.pos),True)
  156.                     self.sell(self.bottomline, self.order, True)
  157.                 else:
  158.                     self.cover(self.upline, abs(self.pos), True)


  159.     # ----------------------------------------------------------------------
  160.     def onTick(self, tick):
  161.         """收到行情TICK推送(必須由使用者繼承實現)"""
  162.         self.bg.updateTick(tick)

  163.     # ----------------------------------------------------------------------
  164.     def onBar(self, bar):
  165.         """收到Bar推送(必須由使用者繼承實現)"""
  166.         self.bg.updateBar(bar)
  167.     # ----------------------------------------------------------------------
  168.     def onOrder(self, order):
  169.         """收到委託推送"""
  170.         pass


  171.     # ----------------------------------------------------------------------
  172.     def onTrade(self, trade):
  173.         # 發出狀態更新事件
  174.         # 如果收到成交,清空所有掛單
  175.         self.cancelAll()
  176.         # 如果交易多頭方向,且現在倉位為0,則應該是空頭平倉,不再開空單
  177.         if trade.direction == DIRECTION_LONG and self.pos == 0:
  178.             self.frozen = -1* self.frozen
  179.         # 如果交易空頭方向,且現在倉位為0,則應該是多平倉,不再開多單
  180.         elif trade.direction == DIRECTION_SHORT and self.pos == 0:
  181.             self.frozen = self.frozen


  182.         self.putEvent()

  183.     # ----------------------------------------------------------------------
  184.     def onStopOrder(self, so):
  185.         """停止單推送"""
  186.         pass

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

相關文章