| Overall Statistics |
|
Total Trades 148 Average Win 0.04% Average Loss -0.59% Compounding Annual Return -58.749% Drawdown 7.800% Expectancy -0.133 Net Profit -7.469% Sharpe Ratio -5.424 Probabilistic Sharpe Ratio 1.075% Loss Rate 19% Win Rate 81% Profit-Loss Ratio 0.07 Alpha -0.252 Beta -0.241 Annual Standard Deviation 0.095 Annual Variance 0.009 Information Ratio -7.324 Tracking Error 0.219 Treynor Ratio 2.132 Total Fees $173.71 |
class VentralVerticalGearbox(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 1) # Set Start Date
self.SetEndDate(2019,2, 1) # Set End Date
self.SetCash(100000) # Set Strategy Cash
# Setup universe
self.UniverseSettings.Resolution = Resolution.Minute
self.UniverseSettings.FillForward = True
self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw))
self.AddUniverse(self.SelectCoarse,self.SelectFine)
# Store yesterday's close in a dictionary for reference
self.coarseclose={}
# Only enter a trade if today's price [doubles] yesterday's close
self.targetentry=2
# Position sizing
self.numstocks = 100
# Exit when stock rises more tha [1.5] times from the entry level
# Subject to a limit of [10] times the entry level
self.stoplevel = 1.5
self.limitlevel = 10
# Keep track of stop loss orders so we can update them
self.stops = {}
# Exclude any stocks where the data is incorrect
self.excluded = ['MDIAV','SNDE','WLL','VTL','AMMA']
# Provide triggers to take action on corporate events
self.symbolchange = {}
self.split = {}
self.exitb4split= {}
# Provide a trigger to exit stale trades
self.daysintrade={}
def SelectCoarse(self, coarse):
# Penny Stock filter
myuniverse = [x for x in coarse if x.Price < 2 and x.DollarVolume < 100000]
myuniverse = [x for x in myuniverse if x.Symbol.Value not in self.excluded]
# Clear the closing price dictionary each day before re-populating it
self.coarseclose.clear()
# Save yesterday's close
for c in myuniverse:
self.coarseclose[c.Symbol] = c.Price
return [x.Symbol for x in myuniverse] # Return filtered stocks for further filtering by the SelectFine
def SelectFine(self,fine):
fine_filter = [x.Symbol for x in fine]
for f in fine:
# Reset these dictionaries each day
self.symbolchange[f.Symbol] = 0
self.split[f.Symbol] = 0
self.exitb4split[f.Symbol]= 0
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
'''
# Mark a symbol change for possible action
for kvp in data.SymbolChangedEvents:
symbol = kvp.Key
self.symbolchange[symbol] =1
# Mark a data split so you don't get a false signal on a reverse split
# Necessary because we are using raw unadjusted data
for kvp in data.Splits:
symbol = kvp.Key
value = kvp.Value
if value.Type == 0:
self.exitb4split[symbol] =1
if value.Type == 1:
self.split[symbol] =1
for kvp in data:
symbol = kvp.Key
openOrders = self.Transactions.GetOpenOrders(symbol)
open = kvp.Value.Open
# To do: cancel stale entry orders
# Optional provision to exit positions where
# Corporate actions interfere
if ((self.Securities[symbol].Invested and
self.Securities[symbol].HasData) and
(self.symbolchange[symbol] ==1 or
self.exitb4split[symbol] ==1)) :
#cancelledOrders = self.Transactions.CancelOpenOrders(symbol)
quantity = self.Portfolio[symbol].AbsoluteQuantity
if quantity < 0:
#exit_ticket = self.LimitOrder(symbol, -quantity,open, "Short Cover Split or SymbolChange")
#exit_ticket = self.MarketOrder(symbol, quantity,Tag = "Short Cover Split or SymbolChange")
self.symbolchange[symbol] =3
self.exitb4split[symbol] =3
# Optional cancellation of stale positions
if (self.Securities[symbol].Invested and
self.Securities[symbol].HasData and
#self.symbolchange[symbol] ==0 and
#self.exitb4split[symbol] ==0 and
symbol in self.daysintrade.keys()):
#self.Debug(str(self.daysintrade[symbol]))
if self.daysintrade[symbol]>=10:
#cancelledOrders = self.Transactions.CancelOpenOrders(symbol)
quantity = self.Portfolio[symbol].Quantity
if quantity < 0:
#exit_ticket = self.LimitOrder(symbol, -quantity,open, "Exit stale trade")
self.daysintrade[symbol]=0
# Short entry after sharp rise from yesterday's close
# Don't enter if the rise is an illusion caused by a reverse split
if ((not self.Securities[symbol].Invested and
not openOrders) and self.Securities[symbol].HasData and
symbol in self.coarseclose.keys() and
self.split[symbol]==0 and
self.exitb4split[symbol] ==0 and
self.symbolchange[symbol] ==0 and
open >= self.coarseclose[symbol]*self.targetentry and
self.Time.hour <= 15 and self.Time.minute <=00):
quantity = int(self.Portfolio.TotalPortfolioValue / self.numstocks / data[symbol].Close)
if quantity < 1:
continue
# Enter with limit order
enter_ticket = self.LimitOrder(symbol, -quantity,open, "Short Entry")
self.daysintrade[symbol]=1
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
order = self.Transactions.GetOrderById(orderEvent.OrderId)
#Place profit taking and stop loss orders
if order.Tag == "Short Entry":
quantity = orderEvent.AbsoluteFillQuantity
quarter = int(quantity / 4)
final = quantity - 3 * quarter
fill= orderEvent.FillPrice
symbol = orderEvent.Symbol
for i in range(3):
if i==0:
order1 = self.LimitOrder(symbol, quarter, fill * (1 + (i+1)*-0.1)," 1st Profit Taking Limit Order")
if i ==1:
order2 = self.LimitOrder(symbol, quarter, fill * (1 + (i+1)*-0.1),"2nd Profit Taking Limit Order")
if i==2:
order3 = self.LimitOrder(symbol, quarter, fill * (1 + (i+1)*-0.1),"3rd Profit Taking Limit Order")
order4 = self.LimitOrder(symbol, final, fill * 0.6,"4th Profit Taking Limit Order")
# Set stop loss
self.stops[symbol] = self.StopLimitOrder(symbol, quantity, fill * self.stoplevel, fill * self.limitlevel,"Stop Limit Order")
# If hit profit target, update stop order quantity
if (order.Tag == "1st Profit Taking Limit Order" or
order.Tag == "2nd Profit Taking Limit Order" or
order.Tag == "3rd Profit Taking Limit Order" or
order.Tag == "4th Profit Taking Limit Order"):
updateSettings = UpdateOrderFields()
updateSettings.Quantity = - self.Portfolio[orderEvent.Symbol].Quantity
if updateSettings.Quantity !=0:
self.stops[orderEvent.Symbol].Update(updateSettings)
else:
self.stops[orderEvent.Symbol].Cancel()
# Increment Stale trade counter
def OnEndOfDay(self):
for kvp in self.Portfolio:
symbol = kvp.Key
holding = kvp.Value
if holding.Invested and symbol in self.daysintrade.keys():
self.daysintrade[symbol] += 1