| Overall Statistics |
|
Total Trades 6 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $6.00 |
'''
TRADING IDEA
- Decide on stocks to trade that are gapping up or down
- Trade based on opening range breakout
- Calculate a range based on high and low of the first n minutes (dynamic)
- Place Stop Limit orders based on maximum and minimum range values
- Liquidate if the price reaches the opposite end of range
- 10 minutes before market close, liquidate all positions that has not hit TP or SL
- TODO
- Take n mins consolidator closing to take trades or not
- Think of SL as ATR
- dont trade after 2 PM
Help and Resources
- TradeBarConsolidator - https://www.quantconnect.com/forum/discussion/5273/consolidators-using-qcalgorithmframework-and-setuniverseselection/p1
- Remove Consolidator - https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/Alphas/GasAndCrudeOilEnergyCorrelationAlpha.py#L189
- Placing Orders - https://www.quantconnect.com/forum/discussion/1539/how-to-place-three-way-order/p1
'''
from datetime import datetime, time
class HorizontalUncoupledAntennaArray(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 4, 13) # Set Start Date
self.SetEndDate(2020, 4, 13)
self.SetCash(10000) # Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Minute
self.SetWarmUp(120)
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
self.SetAlpha(CustomAlphaModel())
self.SetTimeZone(TimeZones.NewYork)
self.AddUniverse(self.CoarseSelectionFilter, self.FineSelectionFilter)
self.numberOfSymbolsCoarse = 1000
self.numberOfSymbolsFine = 100
self.Schedule.On(
self.DateRules.EveryDay(), \
self.TimeRules.At(15, 45), \
self.EveryDayBeforeMarketClose \
)
'''
Coarse selection function
- Only run once every day
- Sort by Daily Dollar volume
- Returns symbols with price greater than 10
- 2000 symbols returned
'''
def CoarseSelectionFilter(self, coarse):
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
return [ x.Symbol for x in sortedByDollarVolume if x.Price > 10 and x.HasFundamentalData][:self.numberOfSymbolsCoarse]
'''
Fine selection filter
- calculate market capital
- filter based on market cap
'''
def FineSelectionFilter(self, fine):
market_cap = {}
for i in fine:
market_cap[i] = (i.EarningReports.BasicAverageShares.ThreeMonths * i.EarningReports.BasicEPS.TwelveMonths * i.ValuationRatios.PERatio)
marketCapFilter = sorted([x for x in fine if market_cap[x] > 0], key=lambda x: market_cap[x], reverse=True)
return [ x.Symbol for x in marketCapFilter][:self.numberOfSymbolsFine]
def EveryDayBeforeMarketClose(self):
self.Liquidate()
class CustomAlphaModel:
def __init__(self):
self.symbolDataBySymbol = {}
self.day = 0
self.shortSymbols = []
self.buySymbols = []
self.percentQuantity = 0.01
def Update(self, algorithm, data):
insights = []
tradeBars = data.Bars
for symb in tradeBars.Keys:
if symb in self.symbolDataBySymbol and not (algorithm.Time.time().hour >= 14 and algorithm.Time.time().minute >= 0):
if self.symbolDataBySymbol[symb].highRange > 0 and self.symbolDataBySymbol[symb].lowRange > 0 and \
symb.Value not in self.buySymbols and symb.Value not in self.shortSymbols:
if self.symbolDataBySymbol[symb].currentFiveMinuteConsolidator is not None and \
self.symbolDataBySymbol[symb].currentFiveMinuteConsolidator.Close > self.symbolDataBySymbol[symb].highRange:
buyquantity = round((self.percentQuantity*algorithm.Portfolio.TotalPortfolioValue)/tradeBars[symb].Close)
algorithm.MarketOrder(symb, buyquantity)
algorithm.StopMarketOrder(symb, -1*buyquantity, self.symbolDataBySymbol[symb].lowRange)
self.buySymbols.append(symb.Value)
if self.symbolDataBySymbol[symb].currentFiveMinuteConsolidator is not None and \
self.symbolDataBySymbol[symb].currentFiveMinuteConsolidator.Close < self.symbolDataBySymbol[symb].lowRange:
sellquantity = round((self.percentQuantity*algorithm.Portfolio.TotalPortfolioValue)/tradeBars[symb].Close)
algorithm.MarketOrder(symb, -1*sellquantity)
algorithm.StopMarketOrder(symb, sellquantity, self.symbolDataBySymbol[symb].highRange)
self.shortSymbols.append(symb.Value)
for symb in algorithm.Portfolio.Keys:
if algorithm.Securities[symb].Holdings.UnrealizedProfitPercent > 0.01:
algorithm.Liquidate(symb, 'One Percent Profit')
return insights
def OnSecuritiesChanged(self, algorithm, changes):
if self.day != algorithm.Time.day:
self.day = algorithm.Time.day
symbols = [ x.Symbol for x in changes.AddedSecurities ]
for removed in changes.RemovedSecurities:
symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None)
if symbolData is not None:
symbolData.RemoveConsolidators(algorithm)
for symbol in symbols:
## Create SymbolData objects for any new assets
algorithm.Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted);
symbolData = SymbolData(algorithm, symbol)
## Assign object to a dictionary so you can access it later in the Update() method
self.symbolDataBySymbol[symbol] = symbolData
class CustomFeeModel:
def GetOrderFee(self, parameters):
fee = max(1, parameters.Security.Price
* parameters.Order.AbsoluteQuantity
* 0.00001)
return OrderFee(CashAmount(fee, 'USD'))
class SymbolData:
def __init__(self, algorithm, symbol):
self.Symbol = symbol
self.Algorithm = algorithm
self.currentFiveMinuteConsolidator = None;
self.highRange = -1
self.lowRange = -1
self.currentDay = -1
self.deviationSum = 0
self.gapPercentLow = 2
self.gapPercentHigh = 15
history = algorithm.History([symbol], 10, Resolution.Daily)
for index, row in history.iterrows():
print(row['open'], row['close'])
hiopen = row['high']- row['open']
openlo = row['open']- row['low']
self.deviationSum += min(hiopen, openlo)
self.deviation = self.deviationSum/10
self.lastClose = history.loc[symbol]['close'].iloc[len(history.index) - 1]
self.gapUpPercent = -1
self.gapDownPercent = -1
self.five_consolidator = TradeBarConsolidator(5)
self.five_consolidator.DataConsolidated += self.FiveMinuteConsolidated
self.range_consolidator = TradeBarConsolidator(65)
self.range_consolidator.DataConsolidated += self.RangeConsolidated
algorithm.SubscriptionManager.AddConsolidator(symbol, self.range_consolidator) ## Register consolidator
algorithm.SubscriptionManager.AddConsolidator(symbol, self.five_consolidator)
def FiveMinuteConsolidated(self, sender, bar):
self.currentFiveMinuteConsolidator = bar
pass
def RangeConsolidated(self, sender, bar):
if self.gapUpPercent == -1 and self.gapDownPercent == -1:
if self.lastClose < bar.Open:
# This is Gap up
self.gapUpPercent = ((bar.Open - self.lastClose)/bar.Open)*100
elif self.lastClose > bar.Open:
# This is Gap down
self.gapDownPercent = ((self.lastClose - bar.Open)/bar.Open )*100
if self.highRange == -1 and (self.gapPercentLow < self.gapUpPercent < self.gapPercentHigh or self.gapPercentLow < self.gapDownPercent < self.gapPercentHigh):
self.highRange= max(bar.High, self.lastClose) + (0*self.deviation)
if self.lowRange == -1 and (self.gapPercentLow < self.gapUpPercent < self.gapPercentHigh or self.gapPercentLow < self.gapDownPercent < self.gapPercentHigh):
self.lowRange= min(self.lastClose, bar.Low) - (0*self.deviation)
def RemoveConsolidators(self, algorithm):
algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.range_consolidator)
algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.five_consolidator)