Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return -6.690% Drawdown 5.900% Expectancy 0 Net Profit -1.101% Sharpe Ratio -0.267 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.041 Beta -0.235 Annual Standard Deviation 0.158 Annual Variance 0.025 Information Ratio -0.192 Tracking Error 0.248 Treynor Ratio 0.18 Total Fees $1.67 |
from collections import deque from datetime import datetime, timedelta class ZigZagDev(QCAlgorithm): def Initialize(self): self.myResolution1 = Resolution.Hour self.myResolution2 = Resolution.Daily self.zzPriod1 = 200 self.zzPriod2 = 200 self.ticker ="SPY" self.SetStartDate(2019,8,1) #self.SetEndDate(2019,8,31) self.SetEndDate(datetime.now()) self.SetWarmUp(TimeSpan.FromDays(self.zzPriod2)) self.symbol = self.AddEquity(self.ticker, self.myResolution1).Symbol self.atr1 = AverageTrueRange(15) self.RegisterIndicator(self.symbol, self.atr1, self.myResolution1) self.ZZ1 = ZigZag(self, 'ZZ1', self.zzPriod1, 5, 1, 0.05, self.atr1) self.RegisterIndicator(self.symbol, self.ZZ1, self.myResolution1) self.atr2 = AverageTrueRange(15) self.RegisterIndicator(self.symbol, self.atr2, self.myResolution2) self.ZZ2 = ZigZag(self, 'ZZ2', self.zzPriod2, 5, 2, 5, self.atr2) self.RegisterIndicator(self.symbol, self.ZZ2, self.myResolution2) self.rsi1 = RelativeStrengthIndex(35) self.RegisterIndicator(self.ticker, self.rsi1, self.myResolution1) self.rsi1_zz = ZigZag(self, 'rsi1_zz', self.zzPriod1, 5, 1, 0.1, self.atr1) self.rsi1.Updated += self.rsi1_zz.IndicatorUpdate def OnData(self, data): if self.IsWarmingUp: return if not self.Portfolio.Invested: self.SetHoldings(self.ticker, 1) def OnEndOfAlgorithm(self): if False: self.ZZ1.ListZZ() if True: self.ZZ1.listZZPoints() if False: self.ZZ2.ListZZ() if False: self.ZZ2.listZZPoints() if False: self.rsi1_zz.ListZZ() if False: self.rsi1_zz.listZZPoints() def MyDebug(self, debugString): message = str(self.Time) + " " + str(self.symbol) + ": "+ debugString self.Debug(message) class ZigZag(): def __init__(self, algo, name, period, lookback=10, thresholdType=1, threshold=0.05, atr=None): self.algo = algo self.name = name self.period = period self.lookback = lookback self.thresholdType = thresholdType self.threshold = threshold self.currentThreshold = 0 self.atr = None if atr != None: self.atr = atr self.Time = datetime.min self.Value = 0 self.IsReady = False self.lastLow = None self.lastHigh = None self.shortTrendCount = 0 self.longTrendCount = 0 self.trendDir = None self.trendChange = False self.bars = deque(maxlen=period) self.zzLow = deque(maxlen=period) self.shortTrendCount = deque(maxlen=period) self.zzHigh = deque(maxlen=period) self.longTrendCount = deque(maxlen=period) self.zigzag = deque(maxlen=period) self.barCount = 0 self.count = 0 self.zzPoints = [] def __str__(self): return self.name def __repr__(self): return "Zig Zag Name:{}, IsReady:{}, Time:{}, Value:{}".format(self.Name, self.IsReady, self.Time, self.Value) # Update method is mandatory def IndicatorUpdate(self, caller, updated): bar = TradeBar() value = updated.Value bar.Open = value bar.High = value bar.Low = value bar.Close = value bar.EndTime = updated.EndTime self.Update(bar) # Update method is mandatory def Update(self, input): bar = input self.count +=1 self.bars.appendleft(bar) self.barCount = len(self.bars) self.Time = bar.EndTime self.Value = self.count self.IsReady = self.barCount == self.bars.maxlen #update currentThreshold value if self.thresholdType == 1: self.currentThreshold = self.bars[0].Close * self.threshold elif self.thresholdType == 2: self.currentThreshold = self.atr.Current.Value * self.threshold if self.barCount < self.lookback: #Not enough bars yet to calculte self.zzLow.appendleft(0) self.shortTrendCount.appendleft(0) self.zzHigh.appendleft(0) self.longTrendCount.appendleft(0) self.zigzag.appendleft(0) return self.IsReady low, thisisLow , retraceLow = self.localLow() high, thisisHigh, retraceHigh = self.localHigh() self.lastLow = low self.lastHigh = high #previousTrendDir = self.trendDir self.trendChange = False if thisisLow or retraceLow: if self.trendDir == 1: self.trendChange = True self.trendDir = -1 if thisisHigh or retraceHigh: if self.trendDir == -1: self.trendChange = True self.trendDir = 1 #Update ZZ Low if thisisLow or retraceLow: self.zzLow.appendleft(low) else: self.zzLow.appendleft(0) #Update Short TrendCount shortTrendCount = self.shortTrendCount[0] if self.trendChange and self.trendDir == -1: shortTrendCount-=1 self.shortTrendCount.appendleft(shortTrendCount) #Update ZZ High if thisisHigh or retraceHigh: self.zzHigh.appendleft(high) else: self.zzHigh.appendleft(0) #Update Long TrendCount longTrendCount = self.longTrendCount[0] if self.trendChange and self.trendDir == 1: longTrendCount+=1 self.longTrendCount.appendleft(longTrendCount) #Update ZZ if self.zzLow[0]!=0: self.zigzag.appendleft(self.zzLow[0]) else: self.zigzag.appendleft(self.zzHigh[0]) self.Value = self.zigzag[0] #Erase precious values if any in this Short Trend i=1 while i < len(self.bars)-1 and self.zzLow[0] !=0 and self.shortTrendCount[i] == shortTrendCount: if self.zzLow[i] == self.zigzag[i]: self.zigzag[i] = 0 self.zzLow[i] = 0 i+=1 #Erase precious values if any in this Long Trend i=1 while i < len(self.bars)-1 and self.zzHigh[0] != 0 and self.longTrendCount[i] == longTrendCount: if self.zzHigh[i] == self.zigzag[i]: self.zigzag[i] = 0 self.zzHigh[i] = 0 i+=1 if False and self.IsReady :self.algo.MyDebug("Low:{}/{}/{}/{}/{}, High:{}/{}/{}/{}/{}, ZZ:{}/{}".format( \ str(low),str(thisisLow),str(retraceLow),str(self.zzLow[0]),str(self.shortTrendCount[0]), \ str(high),str(thisisHigh),str(retraceHigh),str(self.zzHigh[0]),str(self.longTrendCount[0]), \ str(self.zigzag[0]),str(self.trendDir))) #Update zzPoints List del self.zzPoints[:] for i in range(0, len(self.zigzag)-1): if self.zigzag[i]!=0: self.zzPoints.append(ZZPoint(self,i)) return self.IsReady #Local Low or Retracement def localLow(self): thisisLow = False retrace = False low = self.bars[0].Low for i in range(1, self.lookback): if self.bars[i].Low < low: low = self.bars[i].Low thisisLow = self.bars[0].Low == low #trendDir is not updated yet so it is the previous value if self.lastHigh != None and self.trendDir == 1 and (self.lastHigh - self.bars[0].High)>self.currentThreshold: retrace = True low = self.bars[0].Low return low, thisisLow, retrace #Local High or Retracement def localHigh(self): thisisHigh = False retrace = False high = self.bars[0].High for i in range(1, self.lookback): if self.bars[i].High > high: high = self.bars[i].High thisisHigh = self.bars[0].High == high #trendDir is not updated yet so it is the previous value if self.lastLow != None and self.trendDir == -1 and (self.bars[0].Low - self.lastLow)>self.currentThreshold: retrace = True high = self.bars[0].High return high, thisisHigh, retrace def ListZZ(self): printItems = min(70, len(self.bars)-1) i=printItems while i >= 0: if self.zzLow[i]==self.zigzag[i] and self.zigzag[i]!=0: trendCount = self.shortTrendCount[i] elif self.zzHigh[i]==self.zigzag[i] and self.zigzag[i]!=0: trendCount = self.longTrendCount[i] else: trendCount = "" self.algo.MyDebug("Date:{}, Close:{}, zzLow:{}/{}, zzHigh:{}/{}, ZZ:{}/{}".format( \ str(self.bars[i].EndTime), str(self.bars[i].Close), \ str(self.zzLow[i]),str(self.shortTrendCount[i]), \ str(self.zzHigh[i]),str(self.longTrendCount[i]), \ str(self.zigzag[i]),str(trendCount))) i-=1 return def listZZPoints(self): i=1 for level in self.zzPoints: self.algo.MyDebug("{}/{}. Value:{}, EndTime:{}, Index:{}, TrendCount:{}".format( \ str(level.zz), str(i), \ str(level.value), str(level.endTime), str(level.index), str(level.trendCount),)) i+=1 return class ZZPoint(): def __init__(self, zz, index): self.zz = zz self.index = index self.endTime = zz.bars[index].EndTime self.value = zz.zigzag[index] if self.value == zz.zzLow[index]: self.trendCount = zz.shortTrendCount[index] else: self.trendCount = zz.longTrendCount[index] return