Overall Statistics
Total Trades
66
Average Win
0.01%
Average Loss
-0.01%
Compounding Annual Return
-21.500%
Drawdown
0.200%
Expectancy
-0.489
Net Profit
-0.199%
Sharpe Ratio
-9.934
Probabilistic Sharpe Ratio
0%
Loss Rate
76%
Win Rate
24%
Profit-Loss Ratio
1.11
Alpha
0.016
Beta
2.443
Annual Standard Deviation
0.022
Annual Variance
0
Information Ratio
-9.424
Tracking Error
0.013
Treynor Ratio
-0.091
Total Fees
$70.43
Estimated Strategy Capacity
$4800000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
# KEEP AS THE "MASTER" FOR MACD CROSSOVER TRADE SIGNAL WITH HLB EXIT (FIRST BREAK OF HIGHER LOW)
# 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, 8)
        self.SetEndDate(2021, 8, 9)
        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*60, 6*60, 9*60, MovingAverageType.Exponential, Resolution.Second) ### TESTED OTHER NUMBERS INSTEAD OF 60 TO SOLVE FOR LAGGINESS but messed up exits! 59 BETTER
        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:   ###REPLACED THIS AND NEXT LINE WITH RAHUL CODE BELOW
#            return
        if data.ContainsKey("SPY") and data["SPY"] is not None:  ### THIS LINE IS FROM RAHUL POST AND SEEMED TO WORK IN OTHER ALGO
            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
            

### 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[0].Low   # CHANGED FROM 1 TO 0
#        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')