Overall Statistics
#https://www.quantconnect.com/forum/discussion/7867/tim-sykes-and-penny-stocks/p1
class CalibratedUncoupledCoreWave(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 11, 10)  # Set End Date
        self.SetCash(100000)          # Set Strategy Cash
        
        # Setup universe
        self.UniverseSettings.Resolution = Resolution.Minute 
        self.AddUniverse(self.SelectCoarse,self.SelectFine)
        self.AddEquity("SPY", Resolution.Minute)
        
        # Liquidate all positions before the market close each day
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 30), self.ClosePositions)
        
        self.coarseclose={}    # Store yesterday's close in a dictionary for reference
        self.traded={}         # Ensure only 1 trade per day per stock
        self.targetentry=2  # Only enter a trade if today's price doubles yesterday's close
        self.stops = {}        # Keep track of stop loss orders so we can update them
        
        self.coarsefilter = 100 # return the top x stocks sorted by momentum
        self.histlength = 25 # lngth of history to call to ensure sufficient data
        self.momlength = 21 # lookback period for momentum

    def SelectCoarse(self, coarse):
        # Penny Stock filter
        myuniverse = [x for x in coarse if x.HasFundamentalData and \
                                           x.DollarVolume > 1000 and \
                                           x.DollarVolume < 500000 and \
                                           x.Price > 0 and x.Price <= 5.0]

        self.coarseclose.clear()  # Clear the dictionary each day before re-populating it
        stocks = {x.Symbol: x for x in myuniverse} 
        histStocks=list(stocks.keys())
        history = self.History(histStocks, self.histlength, Resolution.Daily)
        mom={}
        
        for stock in histStocks:
            if stock in history.index:
                df = history.loc[stock].dropna()
                if df.empty or len(df)<self.histlength:
                    continue
                #mom[stock]=((df["close"].pct_change()).std())*15.87*100
                mom[stock]=(df["close"].iloc[-1]/df["close"].iloc[-self.momlength])-1
                
        sortedbyMomentum = sorted(mom.items(), key=lambda x: x[1], reverse=False)[:self.coarsefilter]
        
        # Save yesterday's close
        for c in myuniverse:
            self.coarseclose[c.Symbol] = c.AdjustedPrice
        
        return [x[0] for x in sortedbyMomentum[:self.coarsefilter]] # Return filtered stocks for further filtering by the SelectFine
        
    def SelectFine(self,fine):
        ''' 
        This function takes the stock of the CoarceFundamental function and narrow the list adding specific fundamental filters
        Largely to ensure that only common stock is traded and not EG Preference Shares or ETFs
        '''
        # Primary share, common stock, not limited partnership, and not ADR
        fine_filter = [x.Symbol for x in fine if x.SecurityReference.IsPrimaryShare == 1 and \
                                                 x.SecurityReference.SecurityType == 'ST00000001' and \
                                                 x.CompanyReference.IsLimitedPartnership == 0 and \
                                                 x.SecurityReference.IsDepositaryReceipt == 0 ]
        self.traded.clear()
        self.traded = {k: 0 for k in fine_filter}
        return fine_filter

    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        Arguments:
            data: Slice object keyed by symbol containing the stock data
        '''
        for kvp in data.Bars:
            symbol = kvp.Key
            close = kvp.Value.Close
            
            # Entry conditions:
            #  - We have no position in this stock
            #  - We haven't traded this symbol today
            #  - Bar closed above our target
            #  - Before 10:30am
            #  - Large dollar volume for this bar
            if (self.Portfolio[symbol].Quantity == 0.0 and
                symbol in self.traded.keys() and
                self.traded[symbol] == 0 and              
                close >= self.coarseclose[symbol]*self.targetentry and 
                self.Time.hour <= 15 and self.Time.minute <=29 and   
                close * kvp.Value.Volume >= 5000): 
                
                # Signal today's entry
                self.traded[symbol] = 1

                # Determine position size
                quantity = int(self.Portfolio.TotalPortfolioValue / 100 / data[symbol].Close) #self.CalculateOrderQuantity(symbol, 0.01) // 2
                if quantity < 4:
                    continue
                
                # Enter with market order
                enter_ticket = self.MarketOrder(symbol, -quantity)
                
                # Set profit targets 
                quarter = int(quantity / 4)
                final = quantity - 3 * quarter
                for i in range(3):
                    order = self.LimitOrder(symbol, quarter, enter_ticket.AverageFillPrice * (1 + (i+1)*0.05))
                    updateSettings = UpdateOrderFields()
                    updateSettings.Tag = "pt"
                    order.Update(updateSettings)
                order = self.LimitOrder(symbol, final, enter_ticket.AverageFillPrice * 1.2)
                order.Update(updateSettings)
            
                # Set stop loss
                self.stops[symbol] = self.StopMarketOrder(symbol, -quantity, enter_ticket.AverageFillPrice * 1.05)
                updateSettings.Tag = "sl"
                self.stops[symbol].Update(updateSettings)

    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.Filled: 
            order = self.Transactions.GetOrderById(orderEvent.OrderId)
            if order.Tag == 'pt':  # If hit profit target, update stop order quantity
                updateSettings = UpdateOrderFields()
                updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity
                self.stops[orderEvent.Symbol].Update(updateSettings)

            elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders
                self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price")

    def ClosePositions(self):        
        if self.Portfolio.Invested:
            self.Transactions.CancelOpenOrders()
            self.Liquidate()