Overall Statistics |
Total Trades 349 Average Win 1.67% Average Loss -3.23% Compounding Annual Return 12.986% Drawdown 43.200% Expectancy 0.081 Net Profit 43.562% Sharpe Ratio 0.471 Probabilistic Sharpe Ratio 13.825% Loss Rate 29% Win Rate 71% Profit-Loss Ratio 0.52 Alpha 0.155 Beta -0.099 Annual Standard Deviation 0.314 Annual Variance 0.098 Information Ratio 0.183 Tracking Error 0.388 Treynor Ratio -1.498 Total Fees $2437.36 Estimated Strategy Capacity $9700000.00 Lowest Capacity Asset CJJDD ULZ5AW2MP4DH |
# region imports from AlgorithmImports import * import numpy as np # endregion class HyperActiveGreenFish(QCAlgorithm): def Initialize(self): qb = self qb.SetStartDate(2020,1,1) qb.SetCash(100000) qb.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash) self.SetBenchmark("SPY") self.resolution = Resolution.Daily # self.rebalanceTime = datetime.min self.activeStocks = set() # to keep track of stocks self.universe = {} self.free_positions = 10 self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted self.AddUniverse(self.CoarseFilter) #, self.FineFilter) # qb.SetWarmup(5, self.resolution) # days = daysSMA def CoarseFilter(self, coarse): self.coarseListReturn = [] self.coarseListReturn.clear() sortedByDollarVolume = sorted(coarse, key = lambda x: x.DollarVolume, reverse=True) # filteredByVolume = [c for c in sortedByDollarVolume if c.DollarVolume > 25000000] coarseUniverse = [c for c in sortedByDollarVolume if c.Price > 5] for c in coarseUniverse: symbol = c.Symbol #2. Check if we've created an instance of SelectionData for this symbol if symbol not in self.universe: #3. Create a new instance of SelectionData and save to averages[symbol] # 1. Call history to get tradebars history_tb = self.History[TradeBar](symbol, 10, Resolution.Daily) self.universe[symbol] = SelectionData(history_tb, None, c.Price) stock = self.universe[symbol] if stock.volumeDollar10DayAverage > 8 : #5. Check ATR and closingprices, and if ok append the symbol to selected list. if stock.atr_is_ready() and stock.atr10_atEntry_Percent > 2.5: history_df = self.History(symbol, 100, Resolution.Daily) if 'close' in history_df.columns and not history_df.isnull().values.any(): #history_df.index[-1] == self.Time self.universe[symbol] = SelectionData(history_tb, history_df, c.Price) stock = self.universe[symbol] # check if price increased last 2 days if stock.eachDayHigher: # check percent change prev 3 days # if stock.percentUpPrev3Days > 6: # check RSI if stock.rsi_atEntry > 92: self.coarseListReturn.append(symbol) self.coarseListReturn = sorted(self.coarseListReturn, key = lambda x: self.universe[x].rsi_atEntry, reverse=True) self.Log('COARSE: RSI: 92 - Volume avg: 8 - ATR: 2.5 - higher: 2 days - up 3 days:NaN%') return self.coarseListReturn[:10] def OnSecuritiesChanged(self, changes): for x in changes.RemovedSecurities: if x.Symbol in self.activeStocks: self.activeStocks.remove(x.Symbol) for x in changes.AddedSecurities: if x.Symbol not in self.activeStocks: self.activeStocks.add(x.Symbol) def OnData(self, data): # return if warming up: if self.IsWarmingUp: return if self.activeStocks == [] and not self.Portfolio.Invested: return if self.Portfolio.MarginRemaining < 500: return # checking inventory in Universe for universe in self.UniverseManager.Values: if universe is UserDefinedUniverse: continue symbols = universe.Members.Keys symbol_list = [] symbol_names = [] for symbol in symbols: symbol_names.append(symbol.Value) symbol_list.append(symbol) # self.Log(symbol_list) for symbol in self.activeStocks: if symbol not in data: return # checking how many positions I already chave invested = [x.Key for x in self.Portfolio if x.Value.Invested] activePositions = len(invested) # calculating positionsize: self.free_positions = 10 - activePositions if self.free_positions == 0: cashPerPosition = 0 else: cashPerPosition = 0.92*(self.Portfolio.MarginRemaining / self.free_positions) # GOING SHORT for symbol in self.activeStocks: stock = self.universe[symbol] # getting current price symbol.price = self.Securities[symbol].Price if not self.Portfolio[symbol].IsShort and self.free_positions > 0: # calculate number of stocks to buy amountToBuy = cashPerPosition / symbol.price self.orderTicket = self.MarketOrder(symbol, -amountToBuy) # save ticket to class stock.order_ticket(self.orderTicket) #initialising profit-max-log stock.max_profit(0) self.Log('BuyOrder: %s Cost: %f' % (symbol.Value, cashPerPosition)) # COVERING SHORT portfolioHoldings = list(self.Portfolio.keys()) for symbol in portfolioHoldings: if self.Portfolio[symbol].IsShort: stock = self.universe[symbol] # logging new day in tradingdays since entry stock.tradingdays_since_entry() # getting current price currentPrice = self.Securities[symbol].Price # COVER: daysSinceEntry = (self.Time - stock.entryDate).days profitSinceEntry = 100 * self.Portfolio[symbol].UnrealizedProfitPercent profitSoFar = stock.profit_since_entry(stock.entryPrice, currentPrice) if stock.tradingDaysSinceEntry >= 6: # Update the order tag # updateSettings = UpdateOrderFields() # tag = 'Days-Exit. Profit:{:.2f}, Days since entry: {:.2f} '.format(profitSinceEntry, daysSinceEntry) # response = ticket.UpdateTag(updateSettings) self.Liquidate(symbol) self.Log('6-Days-sell') # exit on profit 10% elif profitSinceEntry > 10: # Update the order tag # updateSettings = UpdateOrderFields() # tag = 'Profit-Exit. Profit: %f , Days since entry: %f ' % (profitSinceEntry, daysSinceEntry) # response = ticket.UpdateTag(tag) self.Liquidate(symbol) self.Log('Profit-sell above 10%') # Stop-loss elif currentPrice > (stock.entryPrice + (3*stock.atr10_atEntry)): self.Liquidate(symbol) self.Log('Stop-loss 3ATR') # END of DAY def OnOrderEvent(self, orderEvent): symbol = orderEvent.Symbol stock = self.universe[symbol] order_id = orderEvent.OrderId ticket = self.Transactions.GetOrderTicket(order_id) if orderEvent.Status == OrderStatus.Submitted: # Update the order tag # updateSettings = UpdateOrderFields() tag = f'Submitted: RSI: {stock.rsi_atEntry:.2f} , ATR: {stock.atr10_atEntry:.2f}' #% (stock.rsi_atEntry, stock.atr10_atEntry) response = ticket.UpdateTag(tag) stock.order_date_submitted(self.Time) elif orderEvent.Status == OrderStatus.Filled: if orderEvent.Direction == OrderDirection.Sell: stock.entry_date(self.Time) # logging entry price stock.entry_price(orderEvent.FillPrice) # set stop-order stock.entry_quantity(orderEvent.FillQuantity) # Update the order tag updateSettings = UpdateOrderFields() updateSettings.Tag = "OnOrderEvent: Sell Filled" response = ticket.Update(updateSettings) self.free_positions -= 1 if orderEvent.Direction == OrderDirection.Buy: stock.exit_date(self.Time) # logging exit price stock.exit_price(orderEvent.FillPrice) open_orders = self.Transactions.GetOpenOrders(symbol) if not len(open_orders) == 0: self.Transactions.CancelOpenOrders(symbol) # Update the order tag updateSettings = UpdateOrderFields() updateSettings.Tag = "OnOrderEvent: Buy Filled" response = ticket.Update(updateSettings) self.free_positions += 1 elif orderEvent.Status == OrderStatus.Canceled: pass # class SelectionData(object): class SelectionData(): def __init__(self, history_tb, history_df, currentPrice): rsi_period = 3 self.rsi_atEntry = None self.atr10_atEntry = None self.atr10 = AverageTrueRange(10, movingAverageType=MovingAverageType.Exponential) self.orderTickets = [] self.tradingDaysSinceEntry = 0 #4. Loop over the history data and update the ATR, save current value for trade_bar in history_tb: self.atr10.Update(trade_bar) if self.atr_is_ready(): self.atr10_atEntry = self.atr10.Current.Value self.atr10_atEntry_Percent = (100 * self.atr10_atEntry) / currentPrice # calculate average volume if history_tb is not None: self.volumeDollar10DayAverage = self.average_volume(history_tb, currentPrice) # Calculate RSI 3 (TradingView-edition) and eacDayHigher if history_df is not None: self.rsi_atEntry = self.rsi_TV(history_df, 3) self.eachDayHigher = self.each_day_higher(history_df) # calculate percent change in price previous days if history_df is not None: self.percentUpPrev3Days = self.percent_up_prev_days(history_df, 3) def atr_is_ready(self): return self.atr10.IsReady def average_volume(self, history_tb, currentPrice): volume = 0 period = 0 for bar in history_tb: volume = volume + bar.Volume period += 1 if period > 0: self.volumeAverageDollarInMill = ((currentPrice * volume) / (period)) / 1000000 return self.volumeAverageDollarInMill else: return 0 def each_day_higher(self, history_df): close = history_df["close"] if close[-1] > close[-2] > close[-3]: # > close[-4] > close[-5]: return True else: return False def percent_up_prev_days(self, history_df, period): close = history_df["close"] lookBack = 0 - period - 1 self.percentUpPrevDays = (100 * (close[-1] - close[lookBack])) / close[lookBack] return self.percentUpPrevDays def rsi_TV(self, history_df, rsi_period): delta = history_df["close"].diff() up = delta.copy() up[up < 0] = 0 up = pd.Series.ewm(up, alpha=1/rsi_period).mean() down = delta.copy() down[down > 0] = 0 down *= -1 down = pd.Series.ewm(down, alpha=1/rsi_period).mean() rsi = np.where(up == 0, 0, np.where(down == 0, 100, 100 - (100 / (1 + up / down)))) rsi = rsi[-1] return rsi def tradingdays_since_entry(self): self.tradingDaysSinceEntry += 1 # return self.tradingDays def order_date_submitted(self, submittedDate): self.submittedDate = submittedDate def entry_date(self, entryDate): self.entryDate = entryDate def exit_date(self, exitDate): self.exitDate = exitDate def max_profit(self, maxProfit): self.maxProfit = maxProfit def entry_price(self, entryPrice): self.entryPrice = entryPrice def exit_price(self, exitPrice): self.exitPrice = exitPrice def order_ticket(self, orderTicket): self.orderTickets.append(orderTicket) def entry_quantity(self, entryQuantity): self.entryQuantity = entryQuantity def profit_since_entry(self, entryPrice, currentPrice): self.profitSoFar = (-100 * (currentPrice - entryPrice)) / entryPrice return self.profitSoFar