Overall Statistics |
Total Trades 34 Average Win 5.89% Average Loss -2.40% Compounding Annual Return 13.427% Drawdown 9.600% Expectancy 1.030 Net Profit 47.893% Sharpe Ratio 1.226 Probabilistic Sharpe Ratio 59.821% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 2.45 Alpha 0.094 Beta 0.268 Annual Standard Deviation 0.116 Annual Variance 0.013 Information Ratio -0.189 Tracking Error 0.193 Treynor Ratio 0.531 Total Fees $64.25 Estimated Strategy Capacity $71000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
false_bool = False if false_bool: from AlgorithmImports import * from collections import deque import configs as cfg from datetime import timedelta class EnergeticBlueDonkey(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 1) self.SetEndDate(2021, 2, 5) self.SetCash(100000) self.symbol = self.AddEquity('SPY', Resolution.Minute).Symbol self.gc = GoldenCross(cfg.fast_sma_period, cfg.slow_sma_period) # makes it so we emulate Daily Resolution on Minute Resolution # this is necessary since we are dealing with options, which only work on Minute or finer data self.curr_day = -1 self.SetWarmUp(cfg.slow_sma_period + 3, Resolution.Daily) def OnData(self, data:Slice): if self.curr_day == self.Time.day or not data.Bars.ContainsKey(self.symbol): return self.curr_day = self.Time.day self.gc.Update(data[self.symbol]) if self.IsWarmingUp or not self.gc.IsReady: return self.Plot('GoldenCross', 'Value', self.gc.Value) # buy if golden cross + entry timing # liquidate if death cross if self.gc.Value == 2: self.SetHoldings(self.symbol, 1) elif self.gc.Value == 0: self.SetHoldings(self.symbol, 0) class GoldenCross: def __init__(self, fast_period:int, slow_period:int): ''' GoldenCross indicator .Value = 0 -> not golden cross or death cross .Value = 1 -> golden cross formed, entry not .Value = 2 -> entry formed after golden cross ''' self.Value = 0 self.fast_sma = SimpleMovingAverage(fast_period) self.slow_sma = SimpleMovingAverage(slow_period) # fast sma - slow sma self.sma_diffs = deque(maxlen=3) def dq_rdy(self, vals:deque): ''' returns True iff the deque is has maxlen elements ''' return len(vals) == vals.maxlen def Update(self, input:TradeBar): ''' updates the Golden Cross indicator with a new bar of data returns self.IsReady ''' self.Time = input.EndTime close = input.Close self.fast_sma.Update(self.Time, close) self.slow_sma.Update(self.Time, close) if not self.slow_sma.IsReady: # since the slow_sma takes more values, if its ready # the fast_sma must be ready return False self.sma_diffs.append( self.fast_sma.Current.Value - self.slow_sma.Current.Value ) if not self.dq_rdy(self.sma_diffs): return False is_crossed = ( self.sma_diffs[2] > 0 and self.sma_diffs[1] < 0 and self.sma_diffs[0] < 0 ) # if the fast just recently rises above the slow is_death_crossed = ( self.sma_diffs[2] < 0 and self.sma_diffs[1] > 0 and self.sma_diffs[0] > 0 ) # if the fast just recently dips above the slow if is_death_crossed: self.Value = 0 if self.Value <= 0 and is_crossed: self.Value = 1 elif self.Value == 1 and cfg.entry_condition(close, self.fast_sma.Current.Value, self.slow_sma.Current.Value) : self.Value = 2 return True def Warmup(self): pass @property def IsReady(self): ''' returns True iff the indicator is ready to use ''' return self.dq_rdy(self.sma_diffs)
#BEGIN GoldenCross configurations fast_sma_period = 5 slow_sma_period = 20 assert(fast_sma_period < slow_sma_period) # entry condition after cross has formed def entry_condition(curr_price:float, fast_sma:float, slow_sma:float)->bool: ''' return True iff entry condition is met ''' sma_avg = (fast_sma + slow_sma) / 2 # 4% within average of two SMAs return abs(1-(curr_price / sma_avg)) < .04 #END GoldenCross configurations