Hello,
I'm a total newbie to Quantconnect and am still struggling with how the classes are built in the Quantconnect API, so please forgive me if there are stupid mistakes in my code.
The strategy I want to design is to coarse-filter for stocks with their CCI being oversold (<-100) to create a universe of candidates to buy from, until I hold 10 Stocks. If the CCI of any of the holdings gets overbought (>100), I want to sell that position.
What mainly challenges me in this scenario is, that other than in the common EMA-examples, the CCI function needs a complete bar to update (and not just the current price). However, the history(symbol)-function just spits out a pandas dataframe and I assume that I cannot just feed this rowwise into cci.Update()?
The other challenge is, that I don't know how to attach the current cci value to each of my Portfolio holdings, so I don't have to initialize it with 20 history bars again each time I check the current cci value in self.OnData()...
Of course, if there are any other stupid mistakes in my code, please feel free to critizise me as well - I'm not very used to the structure of object oriented programs, mainly did functional coding in Python before...
Thanks a lot in advance for any answers, Johannes
---
Since Somehow I cannot find my backtests in the "Select a Backtest" Drawdown (maybe because they throw errors?), I will just paste my code in here:
class EMAMomentumUniverse(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 7)
self.SetEndDate(2019, 4, 1)
self.SetCash(100000)
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction)
self.cciscan = { }
def CoarseSelectionFunction(self, universe):
selected = []
universe = sorted(universe, key=lambda c: c.DollarVolume, reverse=True)
universe = [c for c in universe if c.Price > 10][:100]
for coarse in universe:
symbol = coarse.Symbol
if symbol not in self.cciscan:
history = self.History(symbol, 20, Resolution.Daily)
self.cciscan[symbol] = SelectionData(symbol, history)
self.cciscan[symbol].update(self.History(symbol, 1, Resolution.Daily).FirstOrDefault())
if self.cciscan[symbol].is_ready() and self.cciscan[symbol].cci < -100:
selected.append(symbol)
return selected[:10]
def OnData(self, data):
if self.Portfolio.keys < 10:
# here I want to buy another symbol from the "CCI-Oversold"-universe
self.SetHoldings(data[1].Symbol, 0.1)
# if CCI gets overbought -> Liquidate that Position
for security in self.Portfolio.securities.Symbol:
history = self.History(symbol, 20, Resolution.Daily)
if SelectionData(security.Symbol, history).cci > 100: # somehow throwing an error
self.Liquidate(security.Symbol)
class SelectionData():
def __init__(self, symbol, history):
self.Symbol = symbol
self.cci = CommodityChannelIndex(20, MovingAverageType.Simple);
#4. Loop over the history data and update the indicators
for bar in history.itertuples():
#need some way to update CCI with history - currently throws an error.
self.cci.Update(history)
#self.cci.Update(bar.Index[1], bar.close)
def is_ready(self):
return self.cci.IsReady
def update(self, bar):
self.cci.Update(bar)
Rahul Chowdhury
Hi J f,
CCI is a bar indicator which means it must be updated with Bar data. When we iterate over our history dataframe, the items are not TradeBar objects, rather they are just rows of OHLC data. We need to convert that data into a TradeBar object before we attempt to update our CCI indicator.
for bar in history.itertuples(): tbar = TradeBar(bar.Index[1], self.symbol, bar.open, bar.high, bar.low, bar.close, bar.volume) self.cci.Update(tbar)
For the same reason, we should update our indicators within OnData, where we can access TradeBar data for our symbols.Let's also take advantage of OnSecuritiesChanged, which fires each time a new security enters or leaves our universe. We can use this event handler method to enter positions.
def OnSecuritiesChanged(self, changes): for security in changes.AddedSecurities: self.SetHoldings(security.Symbol, 0.1)
And then we can watch our invested symbols to see if any are overbought and liquidate them.
def OnData(self, data): for symbol, selectionData in self.cciscan.items(): if data.Bars.ContainsKey(symbol): selectionData.update(data.Bars[symbol]) if self.Portfolio[symbol].Invested and selectionData.cci.Current.Value > 100: self.Liquidate(symbol)
J f
Thank you very much! This helps a lot. Much appreciated!
J f
One more question though: What kind of object is the changes.AddedSecurities - Object? Does it contain a .symbols attribute?
And how can one find out about the properties of each of the objects in Quantconnect? Seems like note everything is listed in the API-bar in the editor?
Rahul Chowdhury
Hey J f,
changes is a SecurityChanges Object and changes.AddedSecurities is a list of securities, specifically the new securities which were added by universe selection. You can use the API bar, but you can also search through the Lean repository for relevant files and classes. There is also information in the documentation on security changes.
Best
Rahul
J f
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!