Overall Statistics |
Total Trades 302 Average Win 1.19% Average Loss -1.66% Compounding Annual Return 3.005% Drawdown 14.300% Expectancy 0.225 Net Profit 69.980% Sharpe Ratio 0.409 Probabilistic Sharpe Ratio 0.173% Loss Rate 28% Win Rate 72% Profit-Loss Ratio 0.71 Alpha 0.023 Beta -0.008 Annual Standard Deviation 0.054 Annual Variance 0.003 Information Ratio -0.318 Tracking Error 0.171 Treynor Ratio -2.947 Total Fees $1721.31 Estimated Strategy Capacity $420000.00 Lowest Capacity Asset SNTI XZ6355XJQ4IT |
# region imports from AlgorithmImports import * import numpy as np # endregion class HyperActiveGreenFish(QCAlgorithm): def Initialize(self): qb = self qb.SetStartDate(2005,1,5) qb.SetEndDate(2022,12,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 = self.resolution self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted self.AddUniverse(self.CoarseFilter) #, self.FineFilter) # self.SetWarmup(10, self.resolution) # days = daysSMA def CoarseFilter(self, coarse): self.coarseListReturn = [] self.coarseNames = [] # 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: #Create a new instance of SelectionData and save to universe[symbol] # First Call history to get tradebars and dataframe history_tb = self.History[TradeBar](symbol, 10, Resolution.Daily) history_df = self.History(symbol, 100, Resolution.Daily) if 'close' in history_df.columns and len(history_df) > 10 and not history_df.isnull().values.any(): self.universe[symbol] = SelectionData(history_tb, history_df, c.Price) stock = self.universe[symbol] volume = stock.average_volume(history_tb, c.Price) if volume > 8 : #5. Check ATR and closingprices, and if ok append the symbol to selected list. atr10 = stock.atr_10(history_tb, c.Price) if stock.atrIsReady and atr10 > 3: # check if price increased last 2 days higher = stock.each_day_higher(history_df) if higher: # check percent change prev 3 days # if stock.percent_up_prev_days(history_df, 3) > 6: # check RSI rsi = stock.rsi_TV(history_df, 3) if rsi > 92: self.coarseListReturn.append(symbol) self.coarseNames.append(symbol.Value) 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%') self.Log(f'UniverseStocks amount: {len(self.coarseListReturn)}') self.coarseListReturn = self.coarseListReturn[:10] return self.coarseListReturn def OnSecuritiesChanged(self, changes): self.Log(f'Date: {self.Time}. (in OnSecuritiesChanged)') # securities taken out of universe: for x in changes.RemovedSecurities: symbol = x.Symbol if symbol in self.activeStocks: self.activeStocks.remove(symbol) self.Log(f'{symbol.Value} Removed from Universe') # securities added to universe: for x in changes.AddedSecurities: symbol = x.Symbol self.activeStocks.add(symbol) self.Log(f'{symbol.Value} Added to Universe') # for symbol in self.activeStocks: if not self.Portfolio[symbol].IsShort: # checking how many positions I already chave # invested = [x.Key for x in self.Portfolio if x.Value.Invested] self.invested = [ x.Symbol.Value for x in self.Portfolio.Values if x.Invested ] # activePositions = len(invested) self.activePositions = len(self.invested) self.Log(f'{self.activePositions} already active positions invested') self.Log(f'Active positions: {self.invested}') # calculating positionsize: self.free_positions = 10 - self.activePositions if self.free_positions == 0: cashPerPosition = 0 else: cashPerPosition = 0.92*(self.Portfolio.MarginRemaining / self.free_positions) self.Log(f'Free positions: {self.free_positions}') self.Log(f'Cash pr. position: {cashPerPosition}') # GOING SHORT if self.free_positions > 0: # getting current price symbol.price = self.Securities[symbol].Price if symbol.price == 0: return # calculate number of stocks to buy amountToBuy = cashPerPosition / symbol.price stock = self.universe[symbol] 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(f'Buy-Order sent: {symbol.Value} Cost: {cashPerPosition} RSI: {stock.rsi_atEntry}') # # if empty coarseList, clear universe # if self.coarseListReturn == []: # self.activeStocks.clear() def OnData(self, data): self.Log(f'{self.Time} - DAY START') # return if warming up: if self.IsWarmingUp: return for symbol in self.activeStocks: if symbol not in data: 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) # 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 self.currentPrice = self.Securities[symbol].Price # checking status self.profitSinceEntry = 100 * self.Portfolio[symbol].UnrealizedProfitPercent # profitSoFar = stock.profit_since_entry(stock.entryPrice, currentPrice) if stock.tradingDaysSinceEntry >= 6: self.Liquidate(symbol) self.Log(f'6-Days-sell: {symbol}') # exit on profit 10% elif self.profitSinceEntry > 10: self.Liquidate(symbol) self.Log(f'Profit-sell above 10%: {symbol}') # Stop-loss elif self.currentPrice > (stock.entryPrice + (3*stock.atr10_atEntry)): self.Liquidate(symbol) self.Log(f'Stop-loss 3ATR: {symbol}') # END of DAY self.Log(f'EndOfDay: Invested in {len(self.invested)} stocks: {self.invested}') 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.universe.pop(symbol) self.free_positions += 1 self.Log(f'OrderEvent (Buy Filled): {symbol} (removed from universe(dict)') elif orderEvent.Status == OrderStatus.Canceled: self.Log('Order Canceled: %s' % (symbol.Value)) # 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_atEntry_Percent = 0 self.atrIsReady = False 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_10(self, history_tb, currentPrice): if currentPrice == 0: return 0 if history_tb is not None: for trade_bar in history_tb: self.atr10.Update(trade_bar) self.atrIsReady = self.atr10.IsReady if self.atrIsReady: self.atr10_atEntry = self.atr10.Current.Value self.atr10_atEntry_Percent = (100 * self.atr10_atEntry) / currentPrice return self.atr10_atEntry_Percent return 0 def average_volume(self, history_tb, currentPrice): if history_tb is not None: volume = 0 period = 0 for bar in history_tb: volume = volume + bar.Volume period += 1 if period > 0: self.volumeDollar10DayAverage = ((currentPrice * volume) / (period)) / 1000000 return self.volumeDollar10DayAverage else: return 0 return 0 def each_day_higher(self, history_df): if history_df is not None: close = history_df["close"] if len(close) > 5: if close[-1] > close[-2] > close[-3]: # > close[-4] > close[-5]: self.eachDayHigher = True return self.eachDayHigher else: self.eachDayHigher = False return self.eachDayHigher else: return None return None def percent_up_prev_days(self, history_df, period): if history_df is not None: close = history_df["close"] if len(close) >= period: lookBack = 0 - period - 1 self.percentUpPrevDays = (100 * (close[-1] - close[lookBack])) / close[lookBack] return self.percentUpPrevDays return 0 def rsi_TV(self, history_df, rsi_period): if history_df is not None: 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)))) self.rsi_atEntry = rsi[-1] return self.rsi_atEntry return None 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