Hello,
Apologies if this is a stupid question, but is there another way to completely liquidate a position once a certain parameter is met with an algorithm framework? I am receiving the error message " 'FadeTheGapModel' object has no attribute 'Liquidate' " when trying to backtest the below algorithm. Can I use 'self.MarketOrder(symbol, -10)' with some way of automating the "-10" value to match the current number of shares the portfolio is holding?
class QuantumHorizontalRegulators(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 5, 18) # Set Start Date
self.SetEndDate(2020, 5, 19)
self.SetCash(100000) # Set Strategy Cash
self.AddEquity("W5000", Resolution.Second)
self.scaning = False
self.lastToggle = None
self.__numberOfSymbols =100
self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None))
self.UniverseSettings.Resolution = Resolution.Second
self.AddAlpha(FadeTheGapModel(self))
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(AccumulativeInsightPortfolioConstructionModel(lambda time: None))
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("W5000", 0), self.toggleScan)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("W5000", 45), self.toggleScan)
def toggleScan(self):
self.scaning = not self.scaning
self.lastToggle = self.Time
if not self.scaning:
self.needs_reset = True
def CoarseSelectionFunction(self, coarse):
# Stocks with the most dollar volume traded yesterday
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
return [ x.Symbol for x in sortedByDollarVolume[:self.__numberOfSymbols] ]
def FineSelectionFunction(self, fine):
return [ x.Symbol for x in fine ]
class FadeTheGapModel(AlphaModel):
symbolData = {}
def __init__(self, algo):
self.algo = algo
def Update(self, algorithm, slice):
if algorithm.IsWarmingUp:
return []
# If it's the end of the day, update the yesterday close of each indicator
if not algorithm.Securities['W5000'].Exchange.ExchangeOpen:
for symbol in self.symbolData:
if symbol in slice.Bars:
self.symbolData[symbol].yest_close = slice.Bars[symbol].Close
if not self.algo.scaning:
# Reset max indicator
if self.algo.needs_reset:
for symbol in self.symbolData:
self.symbolData[symbol].max.Reset()
self.algo.needs_reset = False
return []
insights = []
insight_seconds = 99999999999
# Create insights for symbols up at least 10% on the day
for symbol in self.symbolData:
# If already invested, continue to next symbol
if algorithm.Securities[symbol].Invested or symbol not in slice.Bars or self.symbolData[symbol].max.Samples == 0:
continue
# Calculate return sign yesterday's close
yest_close = self.symbolData[symbol].yest_close
close = slice[symbol].Close
ret = (close - yest_close) / yest_close
high_of_day_break = close > self.symbolData[symbol].max.Current.Value
if ret >= 0.1 and high_of_day_break: # Up 10% on the day & breaks high of day
insights.append(Insight(symbol, timedelta(seconds=insight_seconds), InsightType.Price, InsightDirection.Up))
# Update max indicator for all symbols
for symbol in self.symbolData:
if symbol in slice.Bars:
self.symbolData[symbol].max.Update(slice.Time, slice.Bars[symbol].High)
# 2% Trailing Stop Order
for symbol in self.symbolData:
if symbol in slice.Bars and slice[symbol].Close <= 0.98*self.symbolData[symbol].max.Current.Value:
self.Liquidate[symbol]
return Insight.Group(insights)
def OnSecuritiesChanged(self, algorithm, changes):
if len(changes.AddedSecurities) > 0:
# Get history of symbols over lookback window
added_symbols = [x.Symbol for x in changes.AddedSecurities]
history = algorithm.History(added_symbols, 1, Resolution.Daily)['close']
for added in changes.AddedSecurities:
# Save yesterday's close
closes = history.loc[[str(added.Symbol.ID)]].values
if len(closes) < 1:
continue
self.symbolData[added.Symbol] = SymbolData(closes[0])
for removed in changes.RemovedSecurities:
# Delete yesterday's close tracker
self.symbolData.pop(removed.Symbol, None)
class SymbolData:
def __init__(self, yest_close):
self.yest_close = yest_close
self.max = Maximum(45*60) # 45 minutes
Thank you,
Sean
Derek Melchin
Hi Sean,
`Liquidate` is a QCAlgorithm method. So instead of calling `self.Liquidate`, we need to call `algorithm.Liquidate` when inside the alpha model class. Either way, this should not be done when following the Framework design because the entry insight is still active after the position is liquidated, causing the portfolio construction model to re-open the position. To liquidate a position properly in this situation, we should emit an insight that has a flat direction and a duration long enough to cover the duration of the entry insight.
See the attached backtest for a demonstration on closing a position by using the Liquidate and emitting an insight with a flat direction. Going forward, I'd recommend reviewing our documentation on risk management models. The MaximumDrawdownPercentPerSecurity model may accomplish exactly what you need.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
S O'Keeffe
Hi Derek,
Awesome that MaximumDrawdownPercentPerSecurity model might do the trick. The only missing piece is that I want it to be a constantly updating 2% trailing stop. Not a 2% trailing stop from my entry price. I thought I could accomplish that with
if symbol in slice.Bars and slice[symbol].Close <= 0.98*self.symbolData[symbol].max.Current.Value:
on my previous attempt. Is that constantly updating ability available with a risk model?
Alternatively, I was thinking I could just input a 'Down' insight with the above line. But that seems to have broken things in a different way in the attached backtest.
Thanks,
Sean
Derek Melchin
Hi Sean,
Yes, we can implement a trailing stop risk management model. To do so, we need to save the maximum price for each security the portfolio is invested in. I recommend using the MaximumDrawdownPercentPerSecurity implementation as a guide when developing this.
The algorithm above doesn't work as intended because we need to add
algorithm.Securities[symbol].Invested
to the condition and emit insights with a flat direction when the condition is satisfied. See the attached backtest for reference.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
S O'Keeffe
Hi Derek,
Holy cow I think you did it! Thank you so much! I forgot that we'd need to include the "algorithm.Securities[symbol].Invested" condition. And I now understand the difference between Flat and Down insights. I might come across one or two tiny questions, but I think it's basically done!
Thank you so much,
Sean
S O'Keeffe
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!