Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$0.00
class WeeklyScreener(QCAlgorithm):

    MAX_INVESTED_SYMBOLS = 10
    TAKE_PROFIT_PERCENTAGE = 0.05
    STOP_LOSS_PERCENTAGE = 0.02
    
    Symbols_To_Protect = dict()
    averages = dict()

    def Initialize(self):
        self.SetStartDate(2019, 2, 21)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        
        # At each end of week, liquidate
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday), self.TimeRules.At(15, 45), Action(self.Liquidate))


    def OnData(self, data):

        all_symbols = [ x.Value for x in self.Securities.Keys ]
        invested_symbols = [ x.Symbol.Value for x in self.Portfolio.Values if x.Invested ]
        
        self.Debug("ALL SYMBOLS: " + str(all_symbols))
        self.Debug("INVESTED SYMBOLS: " + str(invested_symbols))

        # If portfolio already full, return        
        if len(invested_symbols) == self.MAX_INVESTED_SYMBOLS: return
        # Else get diff between all and invested 
        new_symbols = list(set(all_symbols)-set(invested_symbols))
        # If no new symbols, return
        if len(new_symbols) == 0: return
        
        # Add new positions
        for symbol in new_symbols:
            
            # if data.ContainsKey(symbol) is False: continue
            # if data[symbol] is None: continue
            
            # # TODO: If day > wednesday, return
            # if data[symbol].Time > 3: return
            
            # BUY
            self.SetHoldings(symbol, 1/self.MAX_INVESTED_SYMBOLS)
            
            # Handle risk management in OnOrderEvent
            self.Symbols_To_Protect[symbol] = True
    
    
    
    # Creates stop losses and take profits for opened long orders
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        
        # If it's an order to protect and that it's filled
        if self.Symbols_To_Protect.get(order.Symbol.Value, None) is not None and orderEvent.Status == OrderStatus.Filled:
            # Remove order from dict
            del self.Symbols_To_Protect[order.Symbol.Value]
            # SET TAKE PROFIT
            take_profit_price = order.Price * (1 + self.TAKE_PROFIT_PERCENTAGE)
            self.StopMarketOrder(order.Symbol, -order.QuantityFilled, take_profit_price)
            # SET STOP LOSS 
            stop_loss_price = order.Price * (1 - self.STOP_LOSS_PERCENTAGE)
            self.StopMarketOrder(order.Symbol, -order.QuantityFilled, stop_loss_price)
            
    # this event fires whenever we have changes to our universe
    def OnSecuritiesChanged(self, changes):
        self.Debug("Universe change event Added: " + str([s.Symbol.Value for s in changes.AddedSecurities]))
        
    def CoarseSelectionFunction(self, coarse):
        # We are going to use a dictionary to refer the object that will keep the moving averages
        for cf in coarse:
            if cf.Symbol not in self.averages:
                self.averages[cf.Symbol] = SymbolData(cf.Symbol, self)

            # Updates the SymbolData object with price and volume
            avg = self.averages[cf.Symbol]
            avg.update(cf.EndTime, cf.Price, cf.Volume)
        
        # Filter the values of the dict: we only want up-trending securities 
        values = list(filter(lambda x: x.is_uptrend, self.averages.values()))
        selected_symbols = [ x.symbol for x in values[:self.MAX_INVESTED_SYMBOLS] ]
        return selected_symbols
        
    def FineSelectionFunction(self, fine):
        selected_symbols = [f.Symbol for f in fine]
        # self.Debug(str([s.Value for s in selected_symbols]))
        return selected_symbols
        
        
class SymbolData:
    def __init__(self, symbol, algo):
        self.algo = algo
        self.symbol = symbol
        self.priceEMA = ExponentialMovingAverage(10)
        self.volEMA = ExponentialMovingAverage(10)
        self.rsi = RelativeStrengthIndex(14)
        self.macd = MovingAverageConvergenceDivergence(12,26,9)
        self.priceWin = RollingWindow[float](10)
        self.is_uptrend = False

    def update(self, time, price, volume):
        self.priceWin.Add(price)
        self.priceEMA.Update(time, price)
        self.volEMA.Update(time, volume)
        self.rsi.Update(time, price)
        self.macd.Update(time, price)
        if self.rsi.IsReady and self.macd.IsReady and self.priceWin.IsReady:
          rsi = self.rsi.Current.Value
          signal = self.macd.Signal.Current.Value
          macd = self.macd.Current.Value
          self.is_uptrend = rsi < 60 and rsi > 40 and (signal > macd) and price > self.priceWin[1]
        #   if self.is_uptrend:
        #       self.algo.Debug("---- IS UPTREND ---- " + self.symbol.Value + ": RSI=" + str(rsi) + " MACD=" + str(macd) + " SIGNAL=" + str(signal))
        # if self.priceEMA.IsReady and self.volEMA.IsReady and self.priceWin.IsReady:
        #     EMAprice = self.priceEMA.Current.Value
        #     EMAvol = self.volEMA.Current.Value
        #     # current price > 10-days moving average price
        #     # current volume > 10-days moving average volume
        #     # current price > yesterday's close
        #     # current price > 10-days maximum price 
        #     self.is_uptrend =  price > EMAprice and volume > EMAvol and price > self.priceWin[1] and price > max(self.priceWin)