Overall Statistics
Total Trades
227
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
-32.137%
Drawdown
0.200%
Expectancy
-0.658
Net Profit
-0.212%
Sharpe Ratio
-841.082
Probabilistic Sharpe Ratio
0%
Loss Rate
81%
Win Rate
19%
Profit-Loss Ratio
0.84
Alpha
-0.237
Beta
-0.005
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
4.1
Tracking Error
0.055
Treynor Ratio
46.441
Total Fees
$227.00
Estimated Strategy Capacity
$27000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
# TEST FOR PAPER TRADING

# 8/5 REVISIONS: GOES LONG 10SH ON 3/6 CROSS UP SIGNAL, SELLS 10SH WHEN CROSSES ELOW
# ALSO swapped line 74 for 73 to test it

# ORIG: When 3/6 crosses above signal liquidate any shorts then buy to open. 
# When 3/6 crosses below signal sell to close, and sell 200 to open short position. 
# DOES NOT YET HAVE SL OR TP - 
# UPDATE THIS WITH YOUR INFO FROM SESS WITH JOVAD

# AS WELL AS A STOP ORDER (EITHER MARKET OR LIMIT)
# NEED TO ADD UPDATES TO STOP ORDERS BASED ON ELAPSED TIME (IN ONORDEREVENT)
# NEED TO ADD PROFIT TARGET ORDERS (IN ONDATA) & MAKE SURE ALGO CANCELS THE ONE NOT FILLED
import pandas as pd
import decimal as d
# DO WE NEED TO IMPORT THE LIBRARY FOR PLOTTING?

class PaperTradingTest(QCAlgorithm):

    stopMarketTicket = None
    stopLimitTicket = None
    profitTargetTicket = None
    stopMarketFillTime = datetime.min
    stopLimitFillTime = datetime.min
    
    def Initialize(self):
        self.SetStartDate(2021, 8, 3)
        self.SetEndDate(2021, 8, 4)
        self.SetCash(100000)
        self.spy = self.AddEquity("SPY", Resolution.Minute)
        self.spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.SetWarmUp(500) # Warm up using 500 minute bars for all subscribed data
        self.entryTicket = None
        self.stoplossTicket = None
        self.profit1Ticket = None
        self.profit2Ticket = None
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 1), self.LiquidateAtClose)

# defines minute macd(3,6) with a 9 minute signal
        self.__macd = self.MACD("SPY", 3, 6, 9, MovingAverageType.Exponential, Resolution.Minute)
        self.__previous = datetime.min
        self.PlotIndicator("MACD", True, self.__macd, self.__macd.Signal)
#        self.PlotIndicator("MACD", self._macd.Current.Value, self._macd.Signal.Current.Value)
        self.PlotIndicator("SPY", self.__macd.Fast, self.__macd.Slow)
        
        
        
    def OnData(self, data):

        # 1. Plot the current SPY price to "Data Chart" on series "Asset Price"
        self.Plot("Data Chart", "Asset Price", data["SPY"].Close)
        
# NEED ALGO TO ALERT YOU WHENEVER ITS STOPPED OUT TWICE WITHIN 10 MINUTES-CONSOL OR DOWNTREND
# NEED TO PREVENT ANY BUYS AFTER 3:49PM AND LIQUIDATE ANY HOLDINGS AT MOC

        # wait for our macd to fully initialize
        if not self.__macd.IsReady: 
            return

       # only once per day - WHAT IS THIS CODE BELOW? (FROM GITHUB POST)
##        if self.__previous.date() == self.Time.date(): return

        # define a small tolerance on our checks to avoid bouncing
        tolerance = 0.0025
        holdings = self.Portfolio["SPY"].Quantity
        signalDeltaPercent = (self.__macd.Current.Value - self.__macd.Signal.Current.Value)
        
##FIRST VERSION DID NOT WORK:        signalDeltaPercent = (self.__macd.Current.Value - self.__macd.Signal.Current.Value)/self.__macd.Fast.Current.Value

# IF MACD IS > SIGNAL GO LONG 
# ***THIS NEEDS TO BE CODED TO ENSURE THAT MACD WAS BELOW THE SIGNAL AND IS NOW CROSSING ABOVE IT

        if holdings <= 0 and signalDeltaPercent > tolerance: # had 'if not self.Portfolio.Invested' but can't use that if want to go short
            # first liquidate any short position before going long
            self.Liquidate("SPY")
            # SUBMIT ENTRY BUY ORDER - MARKET OR LIMIT
#            self.SetHoldings("SPY", 0.95) # Buys @ market with position sizing at 95% to leave buffer whether cash or margin trading
            self.MarketOrder("SPY", 10) 
            # OR USE THE FOLLOWING LIMIT ORDER AFTER YOU TEST THIS AS IS:
            ##  self.LimitOrder("SPY", 1000, self.LastPrice + 0.02)  # Places an order for 1000 shares of SPY HOWEVER WE SETHOLDINGS AT 95% SO SHOULDN'T WE NOT USE 1000?

            # If our macd is less than our signal, then let's go short ***NOTE THIS IS INCORRECT, YOU DON'T WANT TO LIQUIDATE BUT REVERSE THE POSITION - HOW TO DO?
        elif holdings >= 0 and signalDeltaPercent < -tolerance:
            self.Liquidate("SPY")
            self.MarketOrder("SPY", -10)  
            
        # Prints (Debug) the AveragePrice of the filled buy order
            self.Debug(str(self.Portfolio["SPY"].AveragePrice))

            self.__previous = self.Time

        # BELOW IS TWO OPTIONS FOR STOP ORDER - COMMENT OUT WHICHEVER YOU DON'T WANT
        # IMPORTANT: YOU NEED YOUR EXIT ORDERS TO ACCOUNT FOR PARTIAL FILLS ON ENTRY SO USE QuantityFilled (total fill for order ticket) 

        # OPTION A: STOP MARKET ORDER
            #1. Create stop loss through a stop market order 2ct below the low prior to the entry bar
#            self.stopMarketTicket = self.StopMarketOrder("SPY", -1000, self.Securities["SPY"].Low - 0.02)  # NOTE THIS WORKED IN PAPER TRADING SO WE'LL USE THIS FOR NOW
            
        # OPTION B: STOP LIMIT ORDER
##            low = self.Securities["SPY"].Low  # ASSUMING THIS IS THE LOW OF THE BAR PRIOR TO THE ENTRY BAR
##            stopPrice = low - 0.01 # Trigger stop limit when price falls 1ct below the low of prior bar that has closed
##            limitPrice = low - 0.02  # Sell equal or better than 2cts below the low of prior bar
##            stopLimitTicket = self.StopLimitOrder("SPY", 1000, stopPrice, limitPrice) #QUESTION: DO I NEED "self." before this line?

# NEED TO SET PROFIT TARGET ORDERS (we set profitTargetTicket in initialize) AND INSTRUCT ALGO TO CANCEL THE ONE THAT'S NOT FILLED
# TARGET LOGIC:  HLB UNTIL 2D OR 3D BAR CLOSE THEN ON 3/6 BREAK UNDER SIGNAL? TRY USING 3/6 BREAK AND JUST LIQUIDATING MANUALLY IF YOU SEE THE NEED

# COMPARE ABOVE WITH BELOW:
##            quantity = self.CalculateOrderQuantity("SPY", 0.95) # IS THIS NECESSARY IF WE'RE USING SETHOLDINGS?
##            order = self.LimitOrder(self.symbol, quantity, self.LastPrice + 0.02)  # ENTRY ORDER:HAVE TO MAKE SURE WE GO LONG VIA LIMIT ORDER AT ASK+0.02 WHEN SIGNAL TRIGGERS SO IS "LAST" THE "ASK"???
##            sl = self.StopLimitOrder(self.symbol, -self.FillQuantity, self.Low - 0.02) # STOP LOSS 2CT BELOW LOW OF 1MINUTE BAR PRIOR TO ENTRY
##            tp = self.LimitOrder(self.symbol, -self.FillQuantity, self.FillPrice + 2.00)  # PROFIT TARGET $2 (TO REVISE BASED ON STRAT) - I THINK SELF.FILLQUANTITY IS CORRECT FOR WHATEVER # SHARES I HOLD BASED ON BUY ORDER FILL
##            self.StopLimitOrder(self.Symbol, -1, self.Low - .02) # STOP LOSS - MAKE SURE THIS REFERS TO BAR PRIOR TO ENTRY - I THINK HAVE TO ACCESS VIA ROLLING WINDOW
##                # IF YOU USE ABOVE CODE YOU NEED TO SUBMIT WITH CANCEL OF SL OR TP WHICHEVER IS NOT FILLED - HOW TO DO???
## END OF COMPARISON CODE               



        
        
    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status != OrderStatus.Filled:
            # save orderEvent to lastOrderEvent
            self.lastOrderEvent = orderEvent
            #print out order ID
            self.Debug(orderEvent.OrderId)
            # Printing the security fill prices.
            self.Debug(self.Securities["SPY"].Close)

# TAKE FROM OCO STOP LOSS PROJECT THE OCO LOGIC (2 TAKE PROFITS)
# NEED TO UPDATE STOP ORDER WITH FOLLOWING STOP LOGIC (i think I need rolling window to keep the entry trade bar):
# FIRST UPDATE=WHEN ENTRY BAR CLOSES MOVE STOP TO THE FILL PRICE  + .01 (?) [NO NEED TO REPLACE W/MKT ORDER IF SKIPS PAST YOUR STOP LIMIT AS YOU CAN JUST LIQUIDATE]
# COULD MAKE ABOVE CONDITIONAL TO PRICE BEING <50sma AND WHEN PRICE>50SMA USE FOLLOWING: WHEN ENTRY BAR CLOSES: STOP ENTRY BAR LOW + .01
# SECOND UPDATE=3 OR 4 MIN AFTER FIRST UPDATE MOVE STOP TO HIGH OF ENTRY BAR (OR HIGH LESS .02 IN CASE IT'S TESTED, BUT THAT SHOULD OCCUR IN PRIOR BAR IF IT DOES AT ALL)
# (LATER THE BAR, THE MORE TIME YOU ALLOW FOR CONSOLIDATION WHICH COULD LEAD TO PRICE BREAKOUT)

        # NEED TO UPDATE BELOW IF YOU'RE USING OPTION B FOR STOP ORDER ABOVE
        #2. Check if we hit our stop loss (Compare the orderEvent.Id with the stopMarketTicket.OrderId)
        #   It's important to first check if the ticket isn't null (i.e. making sure it has been submitted)
        # CODE RELATING TO OPTION A ABOVE FOLLOWED BY B (USE OPTION A OR B AS USED ABOVE AND COMMENT OUT THE OTHER):
##        if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId:

# FOLLOWING STOP ADJUSTMENT FROM GITHUB FILE HERE https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/OrderTicketDemoAlgorithm.py
# NEED TO LEARN HOW TO PROPERLY REVISE THIS 
            # if neither order has filled, bring in the stops by a penny
##            newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01
##            newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01
##            self.Log("Updating stops - Long: {0:.2f} Short: {1:.2f}".format(newLongStop, newShortStop))

        if self.stopLimitTicket is not None and self.stopLimitTicket.OrderId == orderEvent.OrderId:

            #3. Store datetime (CHOOSE BELOW ACCORDING TO OPTION A OR B USED ABOVE AND COMMENT OTHER ONE OUT)
##            self.stopMarketFillTime = self.Time
            self.stopLimitFillTime = self.Time
            self.Debug(self.stopMarketFillTime)


    def LiquidateAtClose(self):
        if self.Securities["SPY"].Price is not None:
           self.Liquidate()   #  this was original code but below may be better however quantity needs to be driven by what's remaining in portfolio
 ##           self.MarketOnCloseOrder("SPY", 100)