Overall Statistics |
Total Trades 126 Average Win 0.04% Average Loss -0.01% Compounding Annual Return -12.461% Drawdown 0.300% Expectancy -0.081 Net Profit -0.073% Sharpe Ratio -3.609 Probabilistic Sharpe Ratio 0% Loss Rate 76% Win Rate 24% Profit-Loss Ratio 2.86 Alpha -0.391 Beta 1.309 Annual Standard Deviation 0.024 Annual Variance 0.001 Information Ratio -55.58 Tracking Error 0.006 Treynor Ratio -0.067 Total Fees $134.87 Estimated Strategy Capacity $18000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
# KEEP AS THE "MASTER" FOR MACD CROSSOVER TRADE SIGNAL AND HLB EXIT # DO NOT REVISE THIS - COPY AND PASTE TO NEW DOC TO TEST OTHER IDEAS # NOTE: SIGNAL STILL LAGGY, ENTRIES 1 BAR LATE, so reduced multiplier from 60 to 50 but that messed up exits # BUT 59 WAS A BIT BETTER, TRY DIFF COMBINATIONS from QuantConnect.Data.Market import TradeBar from datetime import timedelta from System import * from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Indicators import * import decimal as d class AlgoConRWandHLBexit(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 8, 5) self.SetEndDate(2021, 8, 6) self.SetCash(100000) self.spy = self.AddEquity("SPY", Resolution.Second).Symbol # this was self.AddEquity("SPY", Resolution.Second) self.SetWarmUp(30000) # Warm up using 500*60 minute bars for all subscribed data self.__macd = self.MACD(self.spy, 3*59, 6*59, 9*59, MovingAverageType.Exponential, Resolution.Second) ### TESTED OTHER NUMBERS INSTEAD OF 60 TO SOLVE FOR LAGGINESS but messed up exits! self.__previous = datetime.min self.PlotIndicator("MACD", True, self.__macd, self.__macd.Signal) self.PlotIndicator(self.spy, self.__macd.Fast, self.__macd.Slow) consolidator_daily = TradeBarConsolidator(timedelta(1)) consolidator_daily.DataConsolidated += self.OnDailyData self.SubscriptionManager.AddConsolidator(self.spy, consolidator_daily) consolidator_minute = TradeBarConsolidator(60) consolidator_minute.DataConsolidated += self.OnMinuteData self.SubscriptionManager.AddConsolidator(self.spy, consolidator_minute) self.daily_rw = RollingWindow[TradeBar](2) self.minute_rw = RollingWindow[TradeBar](2) self.window = RollingWindow[TradeBar](2) self.previousBound = "None" # self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.BeforeMarketClose(self.spy, 1), self.LiquidateAtClose) def OnDailyData(self, sender, bar): self.daily_rw.Add(bar) # add daily bar to daily rolling window def OnMinuteData(self, sender, bar): self.minute_rw.Add(bar) # add minute bar to minute rolling window def OnData(self, data): if data[self.spy] is None: return self.window.Add(data[self.spy]) # adds second bars to window rolling window if not (self.window.IsReady and self.minute_rw.IsReady): # ensures consol 1min bar is ready return if not (self.window.IsReady and self.daily_rw.IsReady and self.minute_rw.IsReady): return last_close = self.window[0].Close last_low = self.window[0].Low # TRY THIS INSTEAD BELOW last_high = self.window[0].High yesterday_daily_close = self.daily_rw[1].Close prev_minute_low = self.minute_rw[0].Low ### CHANGED FROM 1 TO 0 prev_minute_high = self.minute_rw[1].High # TRADE SIGNAL: tolerance = 0.0025 holdings = self.Portfolio[self.spy].Quantity signalDeltaPercent = (self.__macd.Current.Value - self.__macd.Signal.Current.Value) # Trade signal: When MACD crosses up its signal go long if holdings <= 0 and signalDeltaPercent > tolerance: # ALT 'if not self.Portfolio.Invested' ## self.Liquidate("SPY") # first liquidate any short position before going long (no longer needed for this long-only algo) if self.previousBound == "Below" and not self.Portfolio[self.spy].IsLong: price = self.Securities[self.spy].Price quantity = self.CalculateOrderQuantity(self.spy, .95) #self.MarketOrder("SPY", quantity) # Places a market order using half of portfolio buying power (ALT USE: 'self.SetHoldings("SPY", 0.95) self.SetHoldings(self.spy, 0.95) # self.symbolDataBySymbol[self.spy].quantity = quantity ## self.LimitOrder("SPY", quantity, price + 0.02) # Places a limit entry order using half of portfolio buying power (to compare with market price) self.previousBound = "Above" elif holdings >= 0 and signalDeltaPercent < -tolerance: self.previousBound = "Below" self.Liquidate(self.spy) ### TRIED THIS INSTEAD OF return BELOW #return # deleted following code: ## self.Liquidate("SPY") ## self.SetHoldings("SPY", -0.95) self.Debug(str(self.Portfolio["SPY"].AveragePrice)) # print avg fill ### BELOW IS THE PREVIOUS MEANINGLESS TRADE SIGNAL THAT WAS USED JUST FOR TESTING: # # If low is greater than previous low and high is greater than previous high, buy to open # if prev_minute_low < last_low and prev_minute_high < last_high: # self.SetHoldings(self.spy, 1) # self.Debug('GOING LONG') # stopMarketTicket = self.StopMarketOrder("SPY", -1, prev_minute_low - 0.01) # stop loss at previous bar low less 1ct # #NOTE: INSTEAD OF ENTERING A STOP ORDER ALGO COMPARES PRICE TO THE PREVIOUS 1MIN LOW IN CODE BELOW # # If low is less than previous low, sell to open # elif prev_minute_low > last_low: # return # #self.SetHoldings("SPY", -1) # #self.Debug('GOING SHORT') ### THEN HOW DO WE CHANGING THE STOP LOSS LEVEL WHEN THE 1MIN CONSOLIDATOR FINISHES? HOWEVER NOTE THAT USING LIQUIDATE MEANS IF PRICE WENT BELOW THE PRICE THEN COMES BACK UP (AS MANY TIMES IT DOES) YOU WILL TAKE A LARGER LOSS VS OUT AT BE ### HLB EXIT TRAILS PREVIOUS LOW (WHEN PRICE GOES LOWER THAN PREVIOUS 1MIN BAR LOW IT LIQUIDATES POSITION): currBar = self.window[0].Close # this is the close of the seconds bar....maybe also try low? # pastBar = self.minute_rw[1].Low # this is the previous 1min cosnolidated bar low which is named below prev_minute_low = self.minute_rw[1].Low # self.Log("Price: {0} -> {1} ... {2} -> {3}".format(pastBar.Time, pastBar.Low, currBar.Time, currBar.Low)) THIS DID NOT WORK DUE TO TIME SO REMOVED IT # NOW JUST NEED A WAY TO STOP A PARTICULAR BAR, MAYBE HISTORY? if self.Portfolio.Invested and self.Securities[self.spy].Price < prev_minute_low: self.Liquidate() self.Debug('TEST OF NEW STOP WITHOUT STOP ORDER PLACED')