Overall Statistics
Total Trades
3
Average Win
0.01%
Average Loss
0%
Compounding Annual Return
1.191%
Drawdown
0.000%
Expectancy
0
Net Profit
0.019%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-16.794
Tracking Error
0.059
Treynor Ratio
0
Total Fees
$9.75
import pandas as pd
#https://www.quantconnect.com/forum/discussion/7867/tim-sykes-and-penny-stocks/p1
class CalibratedUncoupledCoreWave(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 5, 1)  # Set Start Date
        #self.SetEndDate(2020, 3, 11)  # 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.volumeprior = {}  # Last x days' average dollar volume
        self.stddev={}         # Standard Deviation
        self.minmax={}         # Ratio of Hi Lo of last x days' close price    
        self.cumvol={}         # Cumulative volume throughout trading day    
        self.traded={}         # Ensure only 1 trade per day per stock
        self.targetentry=1.05  # 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 = 50 # return the top x stocks sorted by [momentum] etc
        self.histlength = 60 # lngth of history to call to ensure sufficient data


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

        self.coarseclose.clear()  # Clear the dictionary each day before re-populating it
        self.volumeprior.clear()
        self.stddev.clear()
        self.minmax.clear()
        stocks = {x.Symbol: x for x in myuniverse} 
        histStocks=list(stocks.keys())
        history = self.History(histStocks, self.histlength, Resolution.Daily)

        scam={}
        for stock in histStocks:
            if stock in history.index:
                df = pd.DataFrame()
                if not history.loc[stock].empty:
                    df = history.loc[stock].dropna()
                if df.empty or len(df)<self.histlength:
                    continue
                # Some alternative filters
                self.volumeprior[stock] = df['volume'][-5:].mean()
                self.stddev[stock]=((df["close"][-5:].pct_change()).std())*15.87*100
                self.minmax[stock]=((df["close"][-10:].max()/df["close"][-10:].min())-1)*100
                if self.minmax[stock] < 10.00  and self.volumeprior[stock]< 50000 :
                    scam[stock]= self.minmax[stock]
                
        #scammed=[key for (key, value) in sorted(scam.items(),reverse=False)] 
        scammed=[key for (key, value) in scam.items()]

        
        # Save yesterday's close
        for c in myuniverse:
            self.coarseclose[c.Symbol] = c.AdjustedPrice
        
        return scammed[:] # 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}
        self.cumvol.clear()
        self.cumvol = {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
            if symbol in self.cumvol.keys():
                 self.cumvol[symbol]=self.cumvol[symbol]+(kvp.Value.Close*kvp.Value.Volume)
            
            # Entry conditions:
            #  - We have no position in this stock
            #  - We haven't traded this symbol today
            #  - Bar closed above our target
            #  - Before 10:30am
            #  - Cumulative Volume for the day exceeds average total dollar volume for past x days
            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 <= 10 and self.Time.minute <=30 and   
                self.cumvol[symbol] >= self.volumeprior[symbol]): 
                
                # 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.20)
                order.Update(updateSettings)
            
                # Set stop loss
                self.stops[symbol] = self.StopMarketOrder(symbol, -quantity, enter_ticket.AverageFillPrice * 0.50)
                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()