PyAlgoTrade海龟交易算法

912 阅读3分钟

海龟交易系统本质上是一个趋势跟随的系统,但是最值得学习的是资金管理尤其是分批建仓及动态止损的部分。

一个典型的交易系统需要关注如下几个问题:

  • 建仓:什么时候买,买入多少
  • 加仓: 什么时候买,买入多少
  • 止损:什么时候卖,卖多少
  • 清仓:什么时候退出

建仓

海龟交易系统利用[[唐奇安通道]],作为趋势跟踪指标, 其买入的时机是当前闭市价格大于前20个交易日最高价格时即可买入。我们用如下代码即可实现:

self._high = High(feed[instrument].getHighDataSeries(), 20, 5)

那么买入多少,海龟交易系统引入了一个分批建仓的思想。 其用了N值仓位管理法,其引入了一个核心概念头寸规模单位。 一个 头寸规模单位是指账户资金规模的1%与市场的绝对波动幅度(ATR)的比值。 其公式为 unit = (1%总资金)/ATR

举个列子: 假设有10万的本金,当前ATR为0.1,股票价格10元。那么风险承受能力1%对应的资金规模是1000块,则需要买入1w股,耗资10w元,对应的代码:

quantity = self.getBroker().getCash() * 0.01 / self._atr[-2]  
quantity = int(quantity / 100) * 100

这里再解释下市场的绝对波动幅度(ATR)和真实波幅(TR)的概念: TR是当前交易日最高价和最低价之差 、前一交易日收盘价与当前交易日最高价之差、前一交易日收盘价与当前交易日最低价之差三者中的最大值,用公式表示为:TR=Max(High−Low,abs(High−PreClose),abs(PreClose−Low)), ATR则表示真实波幅TR的20日平均值,而

我们使用pyalgotrade的话可以直接用其包装好的

from pyalgotrade.technical.atr import ATR
self._atr = ATR(feed[instrument], 14)

我们可以看下其内部实现:

def _calculateTrueRange(self, value):  
    ret = None  
 if self.__prevClose is None:  
        ret = value.getHigh(self.__useAdjustedValues) - value.getLow(self.__useAdjustedValues)  
    else:  
        tr1 = value.getHigh(self.__useAdjustedValues) - value.getLow(self.__useAdjustedValues)  
        tr2 = abs(value.getHigh(self.__useAdjustedValues) - self.__prevClose)  
        tr3 = abs(value.getLow(self.__useAdjustedValues) - self.__prevClose)  
        ret = max(max(tr1, tr2), tr3)  
    return ret  
  
def onNewValue(self, dateTime, value):  
    tr = self._calculateTrueRange(value)  
    super(ATREventWindow, self).onNewValue(dateTime, tr)  
    self.__prevClose = value.getClose(self.__useAdjustedValues)  
  
    if value is not None and self.windowFull():  
        if self.__value is None:  
            self.__value = self.getValues().mean()  
        else:  
            self.__value = (self.__value * (self.getWindowSize() - 1) + tr) / float(self.getWindowSize())
            

先算tr, 然后根据TR,计算平均值

加仓

海龟们在入市1单位头寸后,然后间隔1/2N的价格间隔逐步扩大头寸,直至达到上限。举个列子: 假设有10万的本机,当前ATR为0.1,股票价格10元。那么第一次买入的价格是10,第二次是10+1的价格买入一个单位,第三次是11+1的价格买入一个单位。

止损

止损标准,即任何一笔交易的风险程度不得超过2%

清仓

突破10日最低价格的时候清仓。

完整代码

借用PyAlgoTrade, 我们只需要实现一个Strategy即可:

class SeaturtleStrategy(strategy.BacktestingStrategy):  
  
    def __init__(self, feed, instrument):  
        super(SeaturtleStrategy, self).__init__(feed, const_init_totle)  
        self._instrument = instrument  
        self._buyprice = 0.0  
 self._sma = ma.SMA(feed[instrument].getCloseDataSeries(), 4)  
        self._high = High(feed[instrument].getHighDataSeries(), 20, 5)  
        self._low = Low(feed[instrument].getLowDataSeries(), 10)  
        self._atr = ATR(feed[instrument], 14)  
       # self._middle = (self._high + self._low)/2  
  
	 def onBars(self, bars):  
	        if len(self._high) < 2:  
	            return  
	 pre_highest = self._high[-2]  
	        if pre_highest is None:  
	            return  
	  
	 pre_lowest = self._low[-2]  
	        if pre_lowest is None:  
	            return  
	  
	 bar = bars[self._instrument]  
	        quantity = self.getBroker().getShares(self._instrument)  
	        # 建仓  
	 if bar.getClose() > pre_highest:  
	            self._buy(bar, pre_highest)  
	  
	        # 加仓  
	 elif quantity > 0 and bar.getClose() > self._buyprice + 0.5 * pre_highest:  
	            self._buy(bar, pre_highest)  
	            print('加仓')  
	        # 清仓  
	 elif quantity > 0 and bar.getClose() < pre_lowest:  
	            self.enterShort(code, quantity)  
	            self._log(bar, 'enterShort {},{},{}'.format(bar.getClose(), pre_lowest, quantity))  
	        # 止损  
	 elif quantity > 0 and bar.getClose() < (self._buyprice - 2 * pre_highest):  
	            self.enterShort(code, quantity)  
	            self._log(bar, 'enterShort2 {},{},{}'.format(bar.getClose(), pre_lowest, quantity))  
	  
	    def _buy(self, bar, pre_highest):  
	        quantity = self.getBroker().getCash() * 0.01 / self._atr[-2]  
	        quantity = int(quantity / 100) * 100  
	 if quantity > 0:  
	            self.enterLong(self._instrument, quantity, True)  
	            self._buyprice = bar.getClose()  
	            self._log(bar, 'enterLong {},{},{}'.format(bar.getClose(), pre_highest, quantity))  
	  
	    def _log(self, bar, txt):  
	        time = bar.getDateTime()  
	        print('{}, {}'.format(time,txt))

整体用上证30,从2017-04-03,进行回归测试, 入市金额100w, 节奏时间到2022-03-01, 结果金额:1054753.38, 五年收益5%,收益率不高 。