| Overall Statistics |
|
Total Trades 52 Average Win 0.05% Average Loss -0.03% Compounding Annual Return -25.747% Drawdown 0.400% Expectancy -0.344 Net Profit -0.244% Sharpe Ratio -8.838 Probabilistic Sharpe Ratio 0% Loss Rate 77% Win Rate 23% Profit-Loss Ratio 1.84 Alpha -0.075 Beta -0.136 Annual Standard Deviation 0.021 Annual Variance 0 Information Ratio -6.048 Tracking Error 0.165 Treynor Ratio 1.362 Total Fees $393.99 |
clr.AddReference('QuantConnect.Research')
from QuantConnect.Research import QuantBook
class TachyonMultidimensionalChamber(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 11, 15) # Set Start Date
self.SetEndDate(2020, 11, 17) # Set End Date
self.SetCash(400000) # Set Strategy Cash
self.AddUniverse(self.CoarseSelectionFunction)
self.SetSecurityInitializer(self.SecurityInitializer)
self.UniverseSettings.ExtendedMarketHours = True
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.UniverseSettings.Leverage = 4
#self.UniverseSettings.Resolution = Resolution.Hour #can comment/change this out
for s in self.Securities:
self.Debug(self.Securities[s])
#variables to keep track of
self.sd = {} #all symbol data
self.hod = {}
self.lod = {}
self.open = {}
self.buffer = 0 #only enter trades after N buffer after open
self.ema5_past = {}
self.oh_past5 = {}
#self.pmhigh = {}
#self.pmlow = {}
self.day = self.Debug(self.Time.weekday())
def SecurityInitializer(self, security):
security.SetLeverage(4)
def CoarseSelectionFunction(self, universe):
selected = []
for coarse in universe:
if coarse.Volume > 70000000 and coarse.Value > 10 and coarse.HasFundamentalData:
symbol = coarse.Symbol
selected.append(symbol)
return selected #list of objects of type Symbol
def OnSecuritiesChanged(self, changed):
for security in changed.AddedSecurities:
symbol = security.Symbol
if symbol not in self.sd:
self.sd[symbol] = SymbolData(self, symbol)
for security in changed.RemovedSecurities:
symbol = security.Symbol
self.sd.pop(symbol, None)
################
# my functions #
################
#sizing
def positionSize(self, stop, currPrice, dollarSize):
nShares = int(dollarSize / abs(stop - currPrice))
return nShares
###get stop
#most recent of oh_past5 is index 0, furthest is [-1]
def getStop(self, oh_past5, vwap, atr):
#init and parse
firstToCross = -1000
low_postCross = 2000000000.0
#get first candle that crossed
for i in range(0, 5):
high, low = oh_past5[i].split()
high = float(high)
if high > vwap:
firstToCross = i
#low of all candles post-cross
for i in range(0, i):
high, low = oh_past5[i].split()
low = float(low)
if low < low_postCross:
low_postCross = low
return low_postCross + (.5*atr)
#reset VWAP
def OnEndOfDay(self):
for s in self.sd:
self.sd[s].vwap.Reset()
###########
# on data #
###########
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
'''
if self.IsWarmingUp:
return
tradeBars = data.Bars #OHLC of past time interval
for s in self.sd:
self.Debug(self.Time.weekday())
#reset VWAP on new day
if self.day != self.Time.weekday(): #new day
self.day = self.Time.weekday()
self.vwap = self.VWAP(s, 2000, Resolution.Minute)
#highs and lows of candles in past 5 minutes (even premarket)
if data.ContainsKey(s):
if s not in self.oh_past5:
self.oh_past5[s] = []
if len(self.oh_past5[s]) < 5:
self.oh_past5[s].append(str(tradeBars[s].High) + " " + str(tradeBars[s].Low))
elif len(self.oh_past5[s]) == 5:
del self.oh_past5[s][-1]
self.oh_past5[s].insert(0, str(tradeBars[s].High) + " " + str(tradeBars[s].Low))
if self.IsMarketOpen(s) and data.ContainsKey(s):
#this block should not execute in practice since PM data should always be used
if s not in self.hod:
self.hod[s] = -1.0
self.lod[s] = 2000000000.0
self.open[s] = -1.0
#get hod and lod
if tradeBars[s].High > self.hod[s]:
self.hod[s] = tradeBars[s].High
if tradeBars[s].Low < self.lod[s]:
self.lod[s] = tradeBars[s].Low
if self.open[s] == -1.0:
self.open[s] = tradeBars[s].Open
################
# trade setups #
################
### consolidation above VWAP, short-term trend up, has not traveled much of a daily ATR, in upper part of daily range --> ORB up
vwap = self.sd[s].vwap.Current.Value
ema5 = self.sd[s].ema5.Current.Value
ema9 = self.sd[s].ema9.Current.Value
atr5 = self.sd[s].atr5.Current.Value
#day_atr = self.atrDaily[d].Current.Value
price = float(tradeBars[s].Close)
hod = float(self.hod[s])
lod = float(self.lod[s])
ema5_past = self.ema5_past[s]
day_range = hod - lod
#position size in dollars
size = 50.0
if self.Portfolio[s].Invested == False and self.Portfolio.MarginRemaining > size and self.buffer >= 1:
if ema5 > vwap and (price > (lod + (.45 * day_range))) and ema5 > ema5_past and (price - vwap) < atr5 and self.Time.hour < 15: #and day_range < day_atr and price > vwap:
#long
self.Debug(self.Time)
self.Debug("New long")
stop = self.getStop(self.oh_past5[s], vwap, atr5)
shares = self.positionSize(stop, price, size)
self.Debug("margin remaining: " + str(self.Portfolio.MarginRemaining))
self.Debug(s)
#self.Debug("nShares: " + str(shares))
self.Debug("currPrice: " + str(price))
self.Debug("stop: " + str(stop))
self.Debug("buying power used: " + str(shares*price))
self.Debug("VWAP: " + str(vwap))
self.Debug("")
self.MarketOrder(s, shares)
stop = -1*shares
self.StopMarketOrder(s, stop, lod)
elif self.Portfolio[s].Invested == True:
#a "good" trade
if ema5 < ema9 and price < ema9 and ema5 < ema5_past:
self.Liquidate(s)
self.Debug(self.Time)
self.Debug("Sold " + str(s))
#is failing, and stop out before hitting S/L
#has consolidated for too long without moving up
#take partials:
#HOD (or other significant S/R) and PM high at least are reasonable
#>daily ATR
#close all at 3:55 if any positions are open
#should change this to time to market close
#use BeforeMarketClose()
if self.Time.hour >= 15 and self.Time.minute >= 55:
self.Liquidate()
#increase buffer during market hours
self.buffer += 1
#not during market hours, reset various
else:
self.hod[s] = -1.0
self.lod[s] = 2000000000.0
self.open[s] = -1.0
self.buffer = 0
#past period ema5
self.ema5_past[s] = self.sd[s].ema5.Current.Value
class SymbolData:
def __init__(self, algorithm, symbol):
#algorithm.VWAP(symbol, 10000, Resolution.Minute)
self.vwap = algorithm.VWAP(symbol, 2000, Resolution.Minute) #60*24 = 1440 minutes in a day
self.ema5 = algorithm.EMA(symbol, 5, Resolution.Minute)
self.ema9 = algorithm.EMA(symbol, 9, Resolution.Minute)
self.atr5 = algorithm.ATR(symbol, 5, Resolution.Minute)
hist = algorithm.History(symbol, 10, Resolution.Minute).loc[symbol]
for idx, bar in hist.iterrows():
tradeBar = TradeBar(idx, symbol, bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(minutes=1))
self.ema5.Update(idx, bar.close)
self.ema9.Update(idx, bar.close)
self.atr5.Update(tradeBar)
self.vwap.Update(tradeBar)