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)