Overall Statistics |
Total Trades 41 Average Win 0.02% Average Loss -0.05% Compounding Annual Return -0.181% Drawdown 0.400% Expectancy -0.181 Net Profit -0.239% Sharpe Ratio -0.805 Probabilistic Sharpe Ratio 1.127% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 0.42 Alpha -0.002 Beta 0 Annual Standard Deviation 0.002 Annual Variance 0 Information Ratio -0.487 Tracking Error 0.258 Treynor Ratio -9.588 Total Fees $120.45 |
#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=0.5 # Only enter a trade if today's price [doubles/halves etc] 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 = 30 # 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 > 1000000 and \ x.DollarVolume < 5000000 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"].iloc[-1]/df["close"].iloc[-self.momlength])-1 # reverse = true/false - former for high momentum, latter for least momentum sortedbyMomentum = sorted(mom.items(), key=lambda x: x[1], reverse=True)[: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 * 0.95) 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()