Overall Statistics |
Total Trades 707 Average Win 0.04% Average Loss -0.03% Compounding Annual Return -5.876% Drawdown 0.600% Expectancy 0.185 Net Profit -0.523% Sharpe Ratio -6.919 Probabilistic Sharpe Ratio 0.401% Loss Rate 46% Win Rate 54% Profit-Loss Ratio 1.19 Alpha -0.05 Beta 0.024 Annual Standard Deviation 0.006 Annual Variance 0 Information Ratio -2.76 Tracking Error 0.124 Treynor Ratio -1.82 Total Fees $707.00 Estimated Strategy Capacity $7400000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
# region imports from AlgorithmImports import * # endregion # Import datetime import datetime class DancingBrownSheep(QCAlgorithm): def Initialize(self): # # # User input section # # # # Set Start Date self.SetStartDate(2021, 3, 1) # Set End Date self.SetEndDate(2021, 4, 1) # Set Strategy Cash self.SetCash(100000) # Timeframe (in minutes) time_frame = 5 # List of stocks list_of_stocks = ["SPY"] # EMA 1 EMA_one = 9 # EMA 2 EMA_two = 13 # EMA 3 EMA_three = 21 # EMA 4 EMA_four = 200 # RSI RSI_period = 14 # Warmup days warmup_days = 10 # # # End # # # # Symbols dictionary self.symbols_list = [] # EMA 10 dictionary self.EMA_ten_dict = {} # EMA 20 dictionary self.EMA_twenty_dict = {} # EMA 30 dictionary self.EMA_thirty_dict = {} # EMA 40 dictionary self.EMA_forty_dict = {} # RSI 14 dictionary self.RSI_dict = {} # Previous EMA storage self.previous_ema_one_ema_three_difference = {} # Rolling RSI dictionary self.rolling_rsi_dict = {} # Rolling bars storage self.rolling_bars_storage = {} # Call tradable list for signal 1 self.call_signal_one_tradable_list = [] # Call tradable list for signal 2 - 1 self.call_signal_two_tradable_list_one = [] # Call tradable list for signal 2 - 2 self.call_signal_two_tradable_list_two = [] # Put tradable list for signal 1 self.put_signal_one_tradable_list = [] # Put tradable list for signal 2 - 1 self.put_signal_two_tradable_list_one = [] # Put tradable list for signal 2 - 2 self.put_signal_two_tradable_list_two = [] # Counter self.counter = 0 # Data storage self.data_storage = 0 # Loop through list for ticker in list_of_stocks: # Initialize AAPL symbol = self.AddEquity(ticker, Resolution.Minute).Symbol # Option option = self.AddOption(symbol) # Set filter option.SetFilter(-2, +2, timedelta(days = 0), timedelta(days = 7)) # Save stock symbol self.symbols_list.append(symbol) # Create 5-minute consolidator five_min_cons = TradeBarConsolidator(timedelta(minutes=time_frame)) # Assign FiveMinuteHandler as the receiver of consolidated bars for our consolidator object five_min_cons.DataConsolidated += self.FiveMinuteHandler # Subscribe consolidator object to be automatically updated with minute bars self.SubscriptionManager.AddConsolidator(symbol, five_min_cons) # Create EMA-10 self.EMA_ten_dict[symbol] = ExponentialMovingAverage(EMA_one) # Register indicator self.RegisterIndicator(symbol, self.EMA_ten_dict[symbol], five_min_cons) # Create EMA-20 self.EMA_twenty_dict[symbol] = ExponentialMovingAverage(EMA_two) # Register indicator self.RegisterIndicator(symbol, self.EMA_twenty_dict[symbol], five_min_cons) # EMA 30 dictionary self.EMA_thirty_dict[symbol] = ExponentialMovingAverage(EMA_three) # Register indicator self.RegisterIndicator(symbol, self.EMA_thirty_dict[symbol], five_min_cons) # EMA 40 dictionary self.EMA_forty_dict[symbol] = ExponentialMovingAverage(EMA_four) # Register indicator self.RegisterIndicator(symbol, self.EMA_forty_dict[symbol], five_min_cons) # RSI 14 dictionary self.RSI_dict[symbol] = RelativeStrengthIndex(RSI_period) # Register indicator self.RegisterIndicator(symbol, self.RSI_dict[symbol], five_min_cons) # Add to previous EMA difference storage self.previous_ema_one_ema_three_difference[symbol] = [] # Add to rolling RSI dictionary self.rolling_rsi_dict[symbol] = [] # Add to rolling bars storage self.rolling_bars_storage[symbol] = [] # Warmup period self.SetWarmUp(timedelta(days = warmup_days)) def OnData(self, data: Slice): # Save data self.data_storage = data.OptionChains def FiveMinuteHandler(self, sender, bar): # If not warmup if not self.IsWarmingUp: # Add 1 to counter self.counter += 1 # If counter == number of symbols in algorithm if self.counter == len(self.symbols_list): # Reset counter self.counter = 0 # Symbol symbol = bar.Symbol # Add bar to storage self.rolling_bars_storage[symbol].append(bar) # Add EMA difference to list self.previous_ema_one_ema_three_difference[symbol].append(self.EMA_ten_dict[symbol].Current.Value - self.EMA_thirty_dict[symbol].Current.Value) # Update RSI storage self.rolling_rsi_dict[symbol].append(self.RSI_dict[symbol].Current.Value) # If stored more than 3 if len(self.rolling_bars_storage[bar.Symbol]) > 3: # Cut length self.rolling_bars_storage[bar.Symbol] = self.rolling_bars_storage[bar.Symbol][-3:] # Cut length self.previous_ema_one_ema_three_difference[symbol] = self.previous_ema_one_ema_three_difference[symbol][-3:] # Cut length self.rolling_rsi_dict[symbol] = self.rolling_rsi_dict[symbol][-3:] # If counter is 0 and stored at least 3 rolling bars if self.counter == 0 and len(self.rolling_rsi_dict[symbol]) >= 3: # Current time current_time = datetime.time(self.Time.hour, self.Time.minute) # 9:45 am time nine_forty_five = datetime.time(9, 45) # 2:45 pm time two_forty_five = datetime.time(14, 45) # If current time is between 9:45 and 2:45 pm if current_time >= nine_forty_five and current_time < two_forty_five: # Invested invested_options = [] # Get invested options options_holdings = [x for x in self.Portfolio if x.Value.Type == SecurityType.Option and x.Value.Invested] # Calls stock_calls = {} # Puts stock_puts = {} # Loop invested for kvp in options_holdings: # Get ID option_ID = kvp.Key.ID # Get value option_value = kvp.Value # Get underlying asset underlying_asset_symbol = option_ID.Symbol # Get option right option_right = option_ID.OptionRight # Get option symbol option_symbol = option_value.Symbol # If call if option_right == 0: # Add to call list stock_calls[underlying_asset_symbol] = option_symbol # If put elif option_right == 1: # Add to put list stock_puts[underlying_asset_symbol] = option_symbol # Loop through stocks for symbol in self.symbols_list: # If value in calls list if symbol.Value in stock_calls: # Add to invested invested_options.append(symbol) # Value value = symbol.Value # Average price average_price = self.Portfolio[stock_calls[value]].AveragePrice # Current price current_price = self.Securities[stock_calls[value]].AskPrice # If current price 10% or lower from average price if current_price <= average_price * 0.9: # Close all positions self.Liquidate(stock_calls[value], tag = "Stop loss - selling all holdings") # Pop stock_calls.pop(value) # Else else: # If close of stock less than or equal to EMA 2 if self.rolling_bars_storage[symbol][-1].Close <= self.EMA_twenty_dict[symbol].Current.Value: # Close all positions self.Liquidate(stock_calls[value], tag = "Call exit 2 - selling all holdings") # Pop stock_calls.pop(value) # Else else: # If close below EMA 10 if self.rolling_bars_storage[symbol][-1].Close <= self.EMA_ten_dict[symbol].Current.Value: # Quantity quantity = self.Portfolio[stock_calls[value]].Quantity # If quantity is 1 if quantity == 1: # Sell self.Liquidate(stock_calls[value], tag = "Call exit 1 - selling all holdings") # Pop stock_calls.pop(value) # Else else: # Total cost of position cost_of_position = average_price * quantity # Value of position value_of_position = current_price * quantity # Profit profit = value_of_position - cost_of_position # If profit greater than 0 if profit > 0: # Profit / current price quantity_to_keep = profit / current_price # Quantity to sell quantity_to_sell = int(quantity - quantity_to_keep) # If quantity_to_sell greater than or equal to 1 if quantity_to_sell >= 1: # Liquidate self.MarketOrder(stock_calls[value], -quantity_to_sell, tag = "Call exit 1 - selling breakeven") # If value iin calls list elif symbol.Value in stock_puts: # Add to invested invested_options.append(symbol) # Value value = symbol.Value # Average price average_price = self.Portfolio[stock_puts[value]].AveragePrice # Current price current_price = self.Securities[stock_puts[value]].AskPrice # If current price 10% or lower from average price if current_price <= average_price * 0.9: # Close all positions self.Liquidate(stock_puts[value], tag = "Stop loss - selling all holdings") # Pop stock_puts.pop(value) # Else else: # If close of stock greater than or equal to EMA 2 if self.rolling_bars_storage[symbol][-1].Close >= self.EMA_twenty_dict[symbol].Current.Value: # Close all positions self.Liquidate(stock_puts[value], tag = "Put exit 2 - selling all holdings") # Pop stock_puts.pop(value) # Else else: # If close greater EMA 10 if self.rolling_bars_storage[symbol][-1].Close >= self.EMA_ten_dict[symbol].Current.Value: # Quantity quantity = self.Portfolio[stock_puts[value]].Quantity # If quantity is 1 if quantity == 1: # Sell self.Liquidate(stock_puts[value], tag = "Put exit 1 - selling all holdings") # Pop stock_puts.pop(value) # Else else: # Total cost of position cost_of_position = average_price * quantity # Value of position value_of_position = current_price * quantity # Profit profit = value_of_position - cost_of_position # If profit greater than 0 if profit > 0: # Profit / current price quantity_to_keep = profit / current_price # Quantity to sell quantity_to_sell = int(quantity - quantity_to_keep) # If quantity_to_sell greater than or equal to 1 if quantity_to_sell >= 1: # Liquidate self.MarketOrder(stock_puts[value], -quantity_to_sell, tag = "Put exit 1 - selling breakeven") # If length of call tradable list 2 greater than 0 if len(self.call_signal_two_tradable_list_two) > 0: # Loop for symbol in self.call_signal_two_tradable_list_two: # Add to invested invested_options.append(symbol) # Call Entry 2 if ( # If open greater than EMA 10 self.rolling_bars_storage[symbol][-1].Open > self.EMA_ten_dict[symbol].Current.Value and # If RSI above 45 self.rolling_rsi_dict[symbol][-1] > 45 and # If current RSI above previous RSI value self.rolling_rsi_dict[symbol][-1] > self.rolling_rsi_dict[symbol][-2] and # If current RSI above 2 previous RSI value self.rolling_rsi_dict[symbol][-1] > self.rolling_rsi_dict[symbol][-3] ): # Loop data for kvp in self.data_storage: # Stock symbol stock_symbol = kvp.Value.Underlying.Symbol # If stock symbol in tradable list if stock_symbol == symbol: # Get option chain chain = kvp.Value # Sort call contracts with nearest strike and expiration contract = sorted(sorted(sorted(chain, \ key = lambda x: abs(chain.Underlying.Price - x.Strike)), \ key = lambda x: x.Expiry, reverse=False), \ key = lambda x: x.Right, reverse=False)[0] # Buy 1 contract self.MarketOrder(contract.Symbol, 10, True, "Call entry 2") # Break break # Clear list self.call_signal_two_tradable_list_two.clear() # If length of put tradable list 2 greater than 0 if len(self.put_signal_two_tradable_list_two) > 0: # Loop for symbol in self.put_signal_two_tradable_list_two: # Add to invested invested_options.append(symbol) # Put entry 2 if ( # If open less than EMA 10 self.rolling_bars_storage[symbol][-1].Open < self.EMA_ten_dict[symbol].Current.Value and # If RSI below 55 self.rolling_rsi_dict[symbol][-1] < 55 and # If current RSI below previous RSI value self.rolling_rsi_dict[symbol][-1] < self.rolling_rsi_dict[symbol][-2] and # If current RSI below 2 previous RSI value self.rolling_rsi_dict[symbol][-1] < self.rolling_rsi_dict[symbol][-3] ): # Loop data for kvp in self.data_storage: # Stock symbol stock_symbol = kvp.Value.Underlying.Symbol # If stock symbol in tradable list if stock_symbol == symbol: # Get option chain chain = kvp.Value # Sort call contracts with nearest strike and expiration contract = sorted(sorted(sorted(chain, \ key = lambda x: abs(chain.Underlying.Price - x.Strike)), \ key = lambda x: x.Expiry, reverse=False), \ key = lambda x: x.Right, reverse=True)[0] # Buy 1 contract self.MarketOrder(contract.Symbol, 10, True, "Put entry 2") # Break break # Clear list self.put_signal_two_tradable_list_two.clear() # If length call tradable list 1 greater than 0 if len(self.call_signal_one_tradable_list) > 0: # Loop data for kvp in self.data_storage: # Stock symbol stock_symbol = kvp.Value.Underlying.Symbol # If stock symbol in tradable list if stock_symbol in self.call_signal_one_tradable_list: # Add to invested invested_options.append(symbol) # If open of stock above EMA 10 if self.rolling_bars_storage[stock_symbol][-1].Open > self.EMA_ten_dict[stock_symbol].Current.Value: # Get option chain chain = kvp.Value # Sort call contracts with nearest strike and expiration contract = sorted(sorted(sorted(chain, \ key = lambda x: abs(chain.Underlying.Price - x.Strike)), \ key = lambda x: x.Expiry, reverse=False), \ key = lambda x: x.Right, reverse=False)[0] # Buy 1 contract self.MarketOrder(contract.Symbol, 10, True, "Call entry 1") # Break break # Clear self.call_signal_one_tradable_list.clear() # If length put tradable list 1 greater than 0 if len(self.put_signal_one_tradable_list) > 0: # Loop data for kvp in self.data_storage: # Stock symbol stock_symbol = kvp.Value.Underlying.Symbol # If stock symbol in tradable list if stock_symbol in self.put_signal_one_tradable_list: # Add to invested invested_options.append(symbol) # If open of stock above EMA 10 if self.rolling_bars_storage[stock_symbol][-1].Open < self.EMA_ten_dict[stock_symbol].Current.Value: # Get option chain chain = kvp.Value # Sort put contracts with nearest strike and expiration contract = sorted(sorted(sorted(chain, \ key = lambda x: abs(chain.Underlying.Price - x.Strike)), \ key = lambda x: x.Expiry, reverse=False), \ key = lambda x: x.Right, reverse=True)[0] # Buy 1 contract self.MarketOrder(contract.Symbol, 10, True, "Put entry 1") # Clear self.put_signal_one_tradable_list.clear() # If length call tradable list 2 greater than 0 if len(self.call_signal_two_tradable_list_one) > 0: # Loop for symbol in self.call_signal_two_tradable_list_one: # Add to invested invested_options.append(symbol) # Price less than or equal to EMA 10 if self.rolling_bars_storage[symbol][-1].Close <= self.EMA_ten_dict[symbol].Current.Value: # Add to call signal 2 list 2 self.call_signal_two_tradable_list_two.append(symbol) # Clear self.call_signal_two_tradable_list_one.clear() # If length put tradable list 2 greater than 0 if len(self.put_signal_two_tradable_list_one) > 0: # Loop for symbol in self.put_signal_two_tradable_list_one: # Add to invested invested_options.append(symbol) # Price greater than or equal EMA 10 if self.rolling_bars_storage[symbol][-1].Close >= self.EMA_ten_dict[symbol].Current.Value: # Add to put signal 2 list 2 self.put_signal_two_tradable_list_two.append(symbol) # Clear self.put_signal_two_tradable_list_one.clear() # Loop through stocks for symbol in self.symbols_list: # If stock not invested if symbol not in invested_options: # Call entry logic 1 if ( # Previous EMA difference is negative self.previous_ema_one_ema_three_difference[symbol][-2] < 0 and # If current EMA diffeerence is positive self.previous_ema_one_ema_three_difference[symbol][-1] > 0 and # If EMA 1 is above EMA 4 self.EMA_ten_dict[symbol].Current.Value > self.EMA_forty_dict[symbol].Current.Value and # If current candle close above EMA 1 self.rolling_bars_storage[symbol][-1].Close > self.EMA_ten_dict[symbol].Current.Value and # If RSI above 45 self.rolling_rsi_dict[symbol][-1] > 45 # If current RSI above previous RSI value and self.rolling_rsi_dict[symbol][-1] > self.rolling_rsi_dict[symbol][-2] # If current RSI above 2 previous RSI value and self.rolling_rsi_dict[symbol][-1] > self.rolling_rsi_dict[symbol][-3] ): # Add to call tradable list 1 # self.call_signal_one_tradable_list.append(symbol) self.MarketOrder(symbol, 1, tag = " Call 1 ") # Call entry logic 2 elif ( # If EMA 1 above EMA 3 self.EMA_ten_dict[symbol].Current.Value > self.EMA_thirty_dict[symbol].Current.Value and # If EMA 1 above EMA 4 self.EMA_ten_dict[symbol].Current.Value > self.EMA_forty_dict[symbol].Current.Value and # If EMA 2 above EMA 4 self.EMA_twenty_dict[symbol].Current.Value > self.EMA_forty_dict[symbol].Current.Value and # If EMA 3 above EMA 4 self.EMA_thirty_dict[symbol].Current.Value > self.EMA_forty_dict[symbol].Current.Value and # If price above EMA 1 self.rolling_bars_storage[symbol][-1].Close > self.EMA_ten_dict[symbol].Current.Value ): # Add to call tradable list 2 # self.call_signal_two_tradable_list_one.append(symbol) self.MarketOrder(symbol, 1, tag = " Call 2 ") # Put entry logic 1 elif ( # If previous EMA difference positive self.previous_ema_one_ema_three_difference[symbol][-2] > 0 and # If current EMA diffeerence is negative self.previous_ema_one_ema_three_difference[symbol][-1] < 0 and # If EMA 1 is below EMA 4 self.EMA_ten_dict[symbol].Current.Value < self.EMA_forty_dict[symbol].Current.Value and # If current candle close below EMA 1 self.rolling_bars_storage[symbol][-1].Close < self.EMA_ten_dict[symbol].Current.Value and # If RSI below 55 self.rolling_rsi_dict[symbol][-1] < 55 and # If current RSI below previous RSI value self.rolling_rsi_dict[symbol][-1] < self.rolling_rsi_dict[symbol][-2] and # If current RSI below 2 previous RSI value self.rolling_rsi_dict[symbol][-1] < self.rolling_rsi_dict[symbol][-3] ): # Add to put signal 1 list # self.put_signal_one_tradable_list.append(symbol) self.MarketOrder(symbol, 1, tag = " Put 1 ") # Put entry logic 2 elif ( # If EMA 1 below EMA 3 self.EMA_ten_dict[symbol].Current.Value < self.EMA_thirty_dict[symbol].Current.Value and # If EMA 1 below EMA 4 self.EMA_ten_dict[symbol].Current.Value < self.EMA_forty_dict[symbol].Current.Value and # If EMA 2 below EMA 4 self.EMA_twenty_dict[symbol].Current.Value < self.EMA_forty_dict[symbol].Current.Value and # If EMA 3 below EMA 4 self.EMA_thirty_dict[symbol].Current.Value < self.EMA_forty_dict[symbol].Current.Value and # If price below EMA 1 self.rolling_bars_storage[symbol][-1].Close < self.EMA_ten_dict[symbol].Current.Value ): # Add to put tradable list 2 # self.put_signal_two_tradable_list_one.append(symbol) self.MarketOrder(symbol, 1, tag = " Put 2 ") # If current time is exactly 2:45 pm elif current_time == two_forty_five: # Sell everything self.Liquidate(tag = "Liquidated at 2:45 pm") # Reset self.call_signal_one_tradable_list.clear() # Call tradable list for signal 2 - 1 self.call_signal_two_tradable_list_one.clear() # Call tradable list for signal 2 - 2 self.call_signal_two_tradable_list_two.clear() # Put tradable list for signal 1 self.put_signal_one_tradable_list.clear() # Put tradable list for signal 2 - 1 self.put_signal_two_tradable_list_one.clear() # Put tradable list for signal 2 - 2 self.put_signal_two_tradable_list_two.clear()