Overall Statistics
Total Trades
62
Average Win
0.55%
Average Loss
-0.26%
Compounding Annual Return
0.266%
Drawdown
3.700%
Expectancy
0.013
Net Profit
0.084%
Sharpe Ratio
0.083
Probabilistic Sharpe Ratio
30.575%
Loss Rate
68%
Win Rate
32%
Profit-Loss Ratio
2.14
Alpha
0.009
Beta
-0.019
Annual Standard Deviation
0.044
Annual Variance
0.002
Information Ratio
-2.21
Tracking Error
0.135
Treynor Ratio
-0.188
Total Fees
$62.00
Estimated Strategy Capacity
$690000.00
Lowest Capacity Asset
HSGX VW390IT7P4YT
from datetime import datetime, date, time
from datetime import timedelta
import pandas as pd

class TradeStrategyTest(QCAlgorithm):
    
   def Initialize(self):
        self.SetStartDate(2021,3, 1)  #Set Start Date
        self.SetEndDate(2021,6,21)    #Set End Date
        self.SetCash(30000)           #Set Strategy Cash
        self.SetWarmUp(210)
        self.ticket = None # Flag for position status
        self.expiry = self.Time
        self.openPrice = -1   # Pointer to store opening price
        self.lowestPrice = -1 # pointer to store latest daily high
        
        # Set TimeZone
        self.SetTimeZone("America/New_York")
        
        # Add Equities
        self.Equities = ['OCGN', 'CLOV']

        # Add Equities from .csv file - UNDER TESTING NOT COMPLETE!!!!
        # Create a dataframe from csv
        #self.Equities = pd.read_csv('test.csv', delimiter=',')
        
        self.Indicators = dict()
        self.Charts = dict()
        self.Consolidators = dict()
        
        # Set resoltuion for Equity data
        for Symbol in self.Equities:
            
            self.Consolidators[Symbol] = dict()
            self.AddEquity(Symbol, Resolution.Second)
                    
            # Create our consolidators
            self.Consolidators[Symbol]['Con1'] = TradeBarConsolidator(timedelta(minutes=15))
            self.Consolidators[Symbol]['Con2'] = TradeBarConsolidator(timedelta(minutes=2))
                
            # Register our Handlers
            self.Consolidators[Symbol]['Con1'].DataConsolidated += self.On_W1
            self.Consolidators[Symbol]['Con2'].DataConsolidated += self.On_W2
            
            self.Indicators[Symbol] = dict()
            self.Indicators[Symbol]['RSI'] = dict()
                
            self.Indicators[Symbol]['RSI']['15'] = RelativeStrengthIndex(Symbol, 14, MovingAverageType.Wilders)
            self.Indicators[Symbol]['RSI']['2'] = RelativeStrengthIndex(Symbol, 14, MovingAverageType.Wilders)
            # Register the indicaors with our stock and consolidator
            self.RegisterIndicator(Symbol, self.Indicators[Symbol]['RSI']['15'], self.Consolidators[Symbol]['Con1'])
            self.RegisterIndicator(Symbol, self.Indicators[Symbol]['RSI']['2'], self.Consolidators[Symbol]['Con2'])
            
            # Finally add our consolidators to the subscription
            # manager in order to receive updates from the engine
            self.SubscriptionManager.AddConsolidator(Symbol, self.Consolidators[Symbol]['Con1'])
            self.SubscriptionManager.AddConsolidator(Symbol, self.Consolidators[Symbol]['Con2'])
        
        # Set Fee Model
        for Symbol in self.Equities:
            
            self.Securities[Symbol].FeeModel = ConstantFeeModel(1.00)
        
   def OnData(self, data):
        
        # Loop through our equities
        for Symbol in self.Equities:
            
            # Set local variables
            close = self.Securities[Symbol].Close
            quantity = self.CalculateOrderQuantity(Symbol,1*0.2)
            AskPrice = self.Securities[Symbol].AskPrice
            BidPrice = self.Securities[Symbol].BidPrice
            Spread = (AskPrice - BidPrice)
            self.RSI1 = self.Indicators[Symbol]['RSI']['15'].Current.Value
            self.RSI2 = self.Indicators[Symbol]['RSI']['2'].Current.Value
        
            # Warm up Condition
            if self.IsWarmingUp or not data.Bars.ContainsKey(Symbol):# or not self.RSI1.IsReady or not self.RSI2.IsReady: 
                return
            
            # Setup Open and Close Prices and Bars
            if self.Time >= self.expiry:
                self.previous_day_close = self.Securities[Symbol].Close
                self.expiry = Expiry.EndOfDay(self.Time)
            
            self.previous_bar_close = data[Symbol].Close
            change_from_close = (((self.previous_bar_close - self.previous_day_close) / self.previous_bar_close)*100) 
      
            #Obtain Low of Day and Update 
            bar = data[Symbol]
            
            if not bar.IsFillForward and self.lowestPrice < 0:
                self.openPrice = bar.Open
                self.lowestPrice = bar.Low
            
            if self.lowestPrice < 0:
                return
            
            price = bar.Low
            if price < self.lowestPrice:  # If we observe a new low
                self.lowestPrice = price
            
         
            # IMPORTANT!!! Time variables to set open/close times and compare them to current time.
            
            # Convert times to variables (necessary for time comparison)
            currentTime = self.Time
            openTime = time(9,30)
            closeTime = time(12,0)
            
            # Convert string to format that can be compared (comparison does not work if you do not do this)
            # These comparisons are to test it works, before implementing them in the # Buy Conditions function below
            # It is OK to comment them out here, as they are just for testing. Real function below.
            #currentTime.strftime('%H%M') >= openTime.strftime('%H%M')
            #currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')
            
            # Buy Conditions 
            if not self.Securities[Symbol].Invested and self.ticket is None and (currentTime.strftime('%H%M') >= openTime.strftime('%H%M') and currentTime.strftime('%H%M') <= closeTime.strftime('%H%M')):
               # 
                # If buy conditions are satisfied then place MarketOrder
                if  ((self.RSI1 <=70 and self.RSI1 >=20) and (self.RSI2 >0 and self.RSI2 <=25) and (self.previous_bar_close <= self.lowestPrice)): 
                    self.ticket = self.MarketOrder(Symbol, quantity, tag ="Market Buy") 
    
                    # Place Profit take and Stop Loss orders then reset to None
                    self.LimitOrder(Symbol, -quantity, close * 1.03, tag = "Profit Take")
                    self.StopMarketOrder(Symbol, -quantity, close * 0.99, tag = "Stopped Out")
                    self.ticket = None
            else:
                # Close position if open for more than 15 minutes and set ticket to None
                if  self.ticket is not None and (self.Time > self.ticket.Time + timedelta(minutes = 15)):
                    self.Liquidate(Symbol)
                    self.ticket = None
                    
   # Cancel remaining order if limit order or stop loss order is executed 
   def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        
        if order.Status == OrderStatus.Filled:
            if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket:
                self.Transactions.CancelOpenOrders(order.Symbol)
                
        if order.Status == OrderStatus.Canceled:
            self.Log(str(orderEvent))
            
   #Reset Daily :Pointer
   def OnEndOfDay(self, symbol):
       self.lowestPrice = -1
       
       # DEBUG FLAGS - IMPORTANT!!!
       #self.Plot('RSI', 'W1', self.RSI1.Current.Value)
       #self.Plot('RSI', 'W2', self.RSI2.Current.Value)

   # Update Consolidator
   def On_W1(self,sender,bar):
       '''
       This method will be called every time a new 15 minute bar is ready.  
       bar = The incoming Tradebar. This is different to the data object in OnData()
       '''
       #self.RSI1.Update(bar.Time,bar.Close)       
       Symbol = str(bar.get_Symbol())
       self.Plot(Symbol+' RSI', 'W1', self.Indicators[Symbol]['RSI']['15'].Current.Value)
   
   # Update Consolidator   
   def On_W2(self,sender,bar):
       '''
       This method will be called every time a new 2 minute bar is ready. 
       bar = The incoming Tradebar. This is different to the data object in OnData()
       '''
       #self.RSI2.Update(bar.Time,bar.Close)
       Symbol = str(bar.get_Symbol())
       self.Plot(Symbol+' RSI', 'W2', self.Indicators[Symbol]['RSI']['2'].Current.Value)