Overall Statistics
Total Trades
178
Average Win
2.86%
Average Loss
-1.47%
Compounding Annual Return
63.554%
Drawdown
10.800%
Expectancy
0.127
Net Profit
15.663%
Sharpe Ratio
2.076
Probabilistic Sharpe Ratio
61.586%
Loss Rate
62%
Win Rate
38%
Profit-Loss Ratio
1.95
Alpha
0.721
Beta
0.079
Annual Standard Deviation
0.361
Annual Variance
0.131
Information Ratio
0.997
Tracking Error
0.378
Treynor Ratio
9.473
Total Fees
$178.00
Estimated Strategy Capacity
$410000.00
Lowest Capacity Asset
HSGX VW390IT7P4YT
from datetime import datetime, date, time
from datetime import timedelta
class TradeStrategyTest(QCAlgorithm):
  
  def Initialize(self):
       self.SetStartDate(2021,3, 1)  #Set Start Date
       self.SetEndDate(2021,6,14)    #Set End Date
       self.SetCash(30000)           #Set Strategy Cash
       self.AddEquity("OCGN", Resolution.Minute)
       self.RSI1 = RelativeStrengthIndex(14, MovingAverageType.Wilders)
       self.RSI2 = RelativeStrengthIndex(14, MovingAverageType.Wilders)
       self.SetWarmUp(210)
       self.Securities["OCGN"].FeeModel = ConstantFeeModel(1.00)
       self.ticket = None # Flag for position status
       self.previous_day_close = self.Securities["OCGN"].Price
       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")
       
       # Create our consolidators
       Con1 = TradeBarConsolidator(timedelta(minutes=15))
       Con2 = TradeBarConsolidator(timedelta(minutes=2))
       
       # Register our Handlers
       Con1.DataConsolidated += self.On_W1
       Con2.DataConsolidated += self.On_W2
   
       
           
       # Finally add our consolidators to the subscription
       # manager in order to receive updates from the engine
       RSI1_Sub = self.SubscriptionManager.AddConsolidator("OCGN", Con1)
       RSI2_Sub = self.SubscriptionManager.AddConsolidator("OCGN", Con2)
           
       
  def OnData(self, data):
           
         
               
           # Set local variables
           close = self.Securities["OCGN"].Close
           quantity = self.CalculateOrderQuantity("OCGN",1)
           AskPrice = self.Securities["OCGN"].AskPrice
           BidPrice = self.Securities["OCGN"].BidPrice
           Spread = (AskPrice - BidPrice)
   
           # Warm up Codition
           if self.IsWarmingUp or not data.Bars.ContainsKey("OCGN") 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 = data["OCGN"].Close
               self.expiry = Expiry.EndOfDay(self.Time)
           
           
           self.previous_bar_close = data["OCGN"].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["OCGN"]
           
           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
           
           # DEBUG FLAGS - IMPORTANT!!!!
           
           
           #self.Debug(f"Equity Data: {data['OCGN']}")
           #self.Debug(f"RSI2: {self.RSI2.Current.Value}")
           #self.Debug(f"Time: {self.Time}")
           #self.Debug(f"UTC Time: {self.UtcTime}")
           
           # 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["OCGN"].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.Current.Value <=70 and self.RSI1.Current.Value >=20) and (self.RSI2.Current.Value >=0 and self.RSI2.Current.Value <=25)): 
                   self.ticket = self.MarketOrder("OCGN", quantity, tag ="Market Buy") and self.Debug(f"RSI1 Value: {self.RSI1.Current.Value}, RSI2 Value: {self.RSI2.Current.Value}") 
   
                   # Place Profit take and Stop Loss orders then reset to None
                   self.LimitOrder("OCGN", -quantity, close * 1.03, tag = "Profit Take")
                   self.StopMarketOrder("OCGN", -quantity, close * 0.99, tag = "Stopped Out")
                   self.ticket = None
   
               # 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()
                   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
       self.Plot('RSI', 'W1', self.RSI1.Current.Value)
       self.Plot('RSI', 'W2', self.RSI2.Current.Value)
  def On_W1(self,sender,bar):
       '''
       This method will be called every time a new 30 minute bar is ready. 
  
       bar = The incoming Tradebar. This is different to the data object in OnData()
       '''
       self.RSI1.Update(bar.Time,bar.Close)
       
       #self.Plot('RSI', 'W2', self.RSI2.Current.Value)
   
  def On_W2(self,sender,bar):
       '''
       This method will be called every time a new 30 minute bar is ready. 
  
       bar = The incoming Tradebar. This is different to the data object in OnData()
       '''
       self.RSI2.Update(bar.Time,bar.Close)
       #self.Plot('RSI', 'W1', self.RSI1.Current.Value)