| Overall Statistics |
|
Total Trades 228 Average Win 0.15% Average Loss -0.21% Compounding Annual Return 5.856% Drawdown 2.600% Expectancy 0.018 Net Profit 0.495% Sharpe Ratio 0.545 Probabilistic Sharpe Ratio 43.821% Loss Rate 40% Win Rate 60% Profit-Loss Ratio 0.71 Alpha 0 Beta 0 Annual Standard Deviation 0.081 Annual Variance 0.007 Information Ratio 0.545 Tracking Error 0.081 Treynor Ratio 0 Total Fees $556.07 Estimated Strategy Capacity $17000000.00 Lowest Capacity Asset AAPL R735QTJ8XC9X |
from AlgorithmImports import *
class IntradayBreakout(QCAlgorithm):
def Initialize(self):
self.UniverseSettings.Resolution = Resolution.Second
self.SetStartDate(2022, 3, 18)
self.SetEndDate(2022, 4, 18)
self.SetWarmUp(timedelta(days = 1))
self.SetCash(100000)
self.SetTimeZone("America/New_York")
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.stopLossPercentLong = .998
self.stopLossPercentLong2 = .999
self.takeProfitPercentLong = 1.003
self.takeProfitPercentLong2 = 1.003
self.stopLossPercentShort = 1.005
self.stopLossPercentShort2 = 1.0015
self.takeProfitPercentShort = .996
self.takeProfitPercentShort2 = .995
self.timeCheck = False
self.symbolData = {}
tickers = ["AAPL"] #just one ticker for ease of debugging
for ticker in tickers:
symbol = self.AddEquity(ticker, Resolution.Second).Symbol
self.symbolData[symbol] = SymbolData(self, symbol)
everyday = self.DateRules.EveryDay(symbol)
self.Schedule.On(everyday, self.TimeRules.BeforeMarketClose(symbol, 10), self.Liquidate)
self.Schedule.On(everyday, self.TimeRules.BeforeMarketClose(symbol, 30), self.timeCheckNight)
self.Schedule.On(everyday, self.TimeRules.AfterMarketOpen(symbol, 5), self.timeCheckMorning)
def timeCheckMorning(self):
self.timeCheck = True
def timeCheckNight(self):
self.timeCheck = False
def OnEndOfDay(self):
for data in self.symbolData.values():
data.longTrade = True
data.shortTrade = True
data.killswitch = False
data.killswitch2 = False
data.killswitch3 = False
data.vwap.Reset()
def OnData(self, slice):
if not all([data.IsReady for data in self.symbolData.values()]):
return
for symbol, data in self.symbolData.items():
if slice.Bars.ContainsKey(symbol):
data.window15sec.Add(slice.Bars[symbol])
#####self.Debug(f'{symbol} test')
for symbol, data in self.symbolData.items():
price = self.Securities[symbol].Price
stddeviationwindowlong = (3 * data.stdcalc.Current.Value)
#when I debug, clearly the calculation is wrong, because it should take 15 seconds to recieve a new bar, but it updates more frequently than that, and sometimes returns 0.00
#self.Debug(f'{symbol} {stddeviationwindowlong} stdtest')
if data.window15sec.IsReady:
windowDelta = data.window15sec[0].Close - data.window15sec[14].Open
# if I reverse the greater than operator, the algo will not place orders, presently, the algo places the same orders if lines 85-88 are commented out
# I **want** it to place orders if the windowDelta is greater than 3* the std deviation of my 15 second ranges created in symbolData
if windowDelta < stddeviationwindowlong:
if data.rsi.Current.Value < 25:
if data.vwap.Current.Value / price > 1.003:
if data.longTrade:
if self.timeCheck:
marketorderticket = self.SetHoldings(symbol, 1.0)
data.stopmarketorderticket = self.StopMarketOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.stopLossPercentLong)
data.limitorderticket = self.LimitOrder(symbol, round(-self.Portfolio[symbol].Quantity / 2), price * self.takeProfitPercentLong)
data.longTrade = False
#worry about shorts later
'''for symbol, data in self.symbolData.items():
price = self.Securities[symbol].Price
stddeviationwindowshort = (-3 * data.stdcalc.Current.Value)
if (data.smaVol15.Current.Value / data.smaVol390.Current.Value) > 2.0:
if price < data.vwap.Current.Value:
if data.window15min.IsReady:
windowDelta = data.window15min[0].Close - data.window15min[4].Open
if windowDelta < stddeviationwindowshort:
if data.shortTrade:
if self.timeCheck:
marketorderticketshort = self.SetHoldings(symbol, -0.15)
data.stopmarketorderticketshort = self.StopMarketOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.stopLossPercentShort)
data.limitorderticketshort = self.LimitOrder(symbol, round(-self.Portfolio[symbol].Quantity / 2), price * self.takeProfitPercentShort)
data.shortTrade = False'''
def OnOrderEvent(self, orderEvent):
data = self.symbolData.get(orderEvent.Symbol, None)
if not data or orderEvent.Status != OrderStatus.Filled:
return
for symbol, data in self.symbolData.items():
price = self.Securities[symbol].Price
if orderEvent.Status != OrderStatus.Filled:
return
if data.stopmarketorderticket != None and data.stopmarketorderticket.OrderId == orderEvent.OrderId:
if data.killswitch2 == True:
data.killswitch3 = True
if data.stopmarketorderticket != None and data.stopmarketorderticket.OrderId == orderEvent.OrderId:
if data.killswitch == True:
data.killswitch2 = True
if data.stopmarketorderticket != None and data.stopmarketorderticket.OrderId == orderEvent.OrderId:
data.limitorderticket.Cancel()
data.longTrade = True
data.killswitch = True
if data.limitorderticket != None and data.limitorderticket.OrderId == orderEvent.OrderId:
data.stopmarketorderticket.Cancel()
data.limitorderticket2 = self.LimitOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.takeProfitPercentLong2)
data.stopmarketorderticket2 = self.StopMarketOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.stopLossPercentLong2)
data.killswitch = False
if data.limitorderticket2 != None and data.limitorderticket2.OrderId == orderEvent.OrderId:
data.stopmarketorderticket2.Cancel()
data.longTrade = True
if data.stopmarketorderticket2 != None and data.stopmarketorderticket2.OrderId == orderEvent.OrderId:
data.limitorderticket2.Cancel()
data.longTrade = True
if data.stopmarketorderticketshort != None and data.stopmarketorderticketshort.OrderId == orderEvent.OrderId:
data.limitorderticketshort.Cancel()
if data.limitorderticketshort != None and data.limitorderticketshort.OrderId == orderEvent.OrderId:
data.stopmarketorderticketshort.Cancel()
data.limitorderticketshort2 = self.LimitOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.takeProfitPercentShort2)
data.stopmarketorderticketshort2 = self.StopMarketOrder(symbol, -self.Portfolio[symbol].Quantity, price * self.stopLossPercentShort2)
if data.limitorderticketshort2 != None and data.limitorderticketshort2.OrderId == orderEvent.OrderId:
data.stopmarketorderticketshort2.Cancel()
if data.stopmarketorderticketshort2 != None and data.stopmarketorderticketshort2.OrderId == orderEvent.OrderId:
data.limitorderticketshort2.Cancel()
class SymbolData:
def __init__(self, algorithm, symbol):
fifteensecConsolidator = TradeBarConsolidator(timedelta(seconds = 15))
self.subscribe = algorithm.SubscriptionManager.AddConsolidator(symbol, fifteensecConsolidator)
self.consolidateddd = algorithm.Consolidate(symbol, timedelta(seconds = 15), self.fifteensecbarhandler)
self.high = algorithm.SMA(symbol, 1, Resolution.Minute, Field.High)
self.low = algorithm.SMA(symbol, 1, Resolution.Minute, Field.Low)
self.RegisterIndicator = algorithm.RegisterIndicator(symbol, self.high, fifteensecConsolidator)
self.RegisterIndicator = algorithm.RegisterIndicator(symbol, self.low, fifteensecConsolidator)
self.range = IndicatorExtensions.Minus(self.high,self.low)
period = 1560 #15sec x 4 = 1 min, 390 min in session
self.std = StandardDeviation(period)
# should calculate the std deviation of the range of the 15 second bars and return updated calculation every 15 seconds when a new bar arrives and the oldest is discarded
self.stdcalc = IndicatorExtensions.Of(self.range,self.std)
# window that updates every second, the idea is that at any point, the change in price over the most recent 15 seconds could be statistically outside of normal range
self.window15sec = RollingWindow[TradeBar](15)
self.vwap = algorithm.VWAP(symbol, 390, Resolution.Minute)
self.rsi = algorithm.RSI(symbol, 20, Resolution.Minute)
#one trade per security at a time
self.longTrade = True
self.shortTrade = True
self.stopmarketorderticket = None
self.stopmarketorderticket2 = None
self.limitorderticket = None
self.limitorderticket2 = None
self.stopmarketorderticketshort = None
self.stopmarketorderticketshort2 = None
self.limitorderticketshort = None
self.limitorderticketshort2 = None
def fifteensecbarhandler(self, consolidated):
pass
@property
def IsReady(self):
return all
([stdcalc.IsReady for stdcalc in self.stdcalc.values()])
([window15sec.IsReady for window15 in self.window15sec.values()])