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)