| 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()