Hello QuantConnect Community! We have a question, we are trying to do is choosing a universe of 10 companies, and consolidating the bars in bars of 30 minutes. Save the first bar from 9 to 9:30 and if the price is above the High of the first bar (opening bar) then it will buy. Then we create an trailing stop that closes the transactions (0.95 from the highest price) by selling if the price falls by 5% (0.95 of the high). The problem is that the trailing stop is not being executed, and is apparently consolidating only 2 tickers. I leave my code here, I hope you can help me!
class DynamicOptimizedContainmentField(QCAlgorithm):
def Initialize(self):
self.stopMarketTicket = None
self.stopMarketOrderFillTime = datetime.min
self.highestPrice = 0
self.SetStartDate(2021, 3, 26) # Set Start Date\
self.SetEndDate(2021, 4, 26)
self.SetCash(1000000) # Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Minute
self.AddUniverse(self.SelectCoarse)
self.UniverseSettings.Leverage = 2
self.symbols = {}
self.stopPrice = self.GetParameter("stop")
def SelectCoarse(self, coarse):
sortedCoarse = sorted(coarse, key=lambda c:c.DollarVolume, reverse=True)
return [c.Symbol for c in sortedCoarse][:10]
def OnSecuritiesChanged(self, changes):
for security in changes.AddedSecurities:
symbol = security.Symbol
if symbol not in self.symbols:
self.symbols[symbol] = SymbolData(self, symbol)
self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.At(13,30), self.ClosePositions)
for security in changes.RemovedSecurities:
symbol = security.Symbol
if symbol in self.symbols:
symbolData = self.symbols.pop(symbol, None)
self.SubscriptionManager.RemoveConsolidator(symbol, symbolData.consolidator)
def OnData(self, data):
for symbol, symbol_data in self.symbols.items():
if symbol_data.openingBar is None: continue
if not data.Bars.ContainsKey(symbol):
continue
if data.Bars[symbol].Close > symbol_data.openingBar.High and not self.Securities[symbol].Invested:
quantity = self.CalculateOrderQuantity(symbol, 0.08) # orders 8% of portfolio
self.MarketOrder(symbol, quantity)
self.stopMarketTicket = self.StopMarketOrder(symbol, -self.Portfolio[symbol].Quantity, data[symbol].Close * 0.95)
continue
if self.Securities[symbol].Invested and data.Bars[symbol].Close > symbol_data.highestPrice:
self.highestPrice = self.Securities[symbol].Close
updateFields = UpdateOrderFields()
updateFields.StopPrice = self.highestPrice * 0.95
self.stopMarketTicket.Update(updateFields)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled:
self.symbols[orderEvent.Symbol].openingBar = None
return
if self.symbols[orderEvent.Symbol].stopMarketTicket is not None and self.symbols[orderEvent.Symbol].stopMarketTicket.OrderId == orderEvent.OrderId:
self.symbols[orderEvent.Symbol].stopMarketOrderFillTime = self.Time
def ClosePositions(self):
for symbolData in self.symbols.values():
symbolData.openingBar = None
self.Liquidate() # liquidate entire portfolio
def OnDataConsolidated(self, sender, bar):
self.Log(f"Bar at {self.Time} for {bar.Symbol}")
if bar.Time.hour == 9 and bar.Time.minute == 30:
self.symbols[bar.Symbol].openingBar = bar
class SymbolData:
def __init__(self, algorithm, symbol):
self.algorithm = algorithm
self.symbol = symbol
self.consolidator = TradeBarConsolidator(timedelta(minutes = 30))
self.consolidator.DataConsolidated += self.OnDataConsolidated
self.openingBar = None
algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
def OnDataConsolidated(self, sender, bar):
self.algorithm.Debug(f"Data Consolidatoed for {self.symbol} at {bar.EndTime} with bar: {bar}")
Varad Kabade
Hi Yaros,
In the above algorithm, the OnDataConsolidated method should be part of the SymbolData class. The algorithm is using the same reference for StopMarket orders, so they are overwritten. We don't need to create a Scheduled event for each Symbol when we are liquidating our portfolio. We have made the following changes.
Note that here the first consolidated bar will be available at 10.00 Am instead of 9.30. Refer to the attached backtest.
Best,
Varad Kabade
Yaros
Thank you very much Varad kabade,
I would like to ask something about the algorithm. I'm trying to get the market orders be executed during a specific period of time, for example between 11 - 11:30. If the conditional on the OnData definition is not done within this time range, then no market order is executed.
I've been trying this function but couldn't make it to work, I'll leave the error message below, any will will be appreciated.
This is the Error Message:
During the algorithm initialization, the following exception has occurred: Trying to dynamically access a method that does not exist throws a TypeError exception.
To prevent the exception, ensure each parameter type matches those required by the 'int'>) method. Please checkout the API documentation. at Initialize self.Schedule.On(self.DateRules.EveryDay() in main.py:line 21 TypeError :
No method matches given arguments for AfterMarketOpen: (, )
I also attach all the code. Thanks a lot for the help.
Varad Kabade
Hi Yaros,
The OnData method is an event handler for our data subscriptions; it cannot be used as a Scheduled event. To resolve this issue, we can create another method to check the trade conditions. In the strategy mentioned above, a better way would be to implement a condition statement using timestamps to check for the trade condition in OnData itself. Refer to the following code snippet
We have implemented the above change in your algorithm; refer to the attached backtest.
Best,
Varad Kabade
Yaros
Thank you very much again Varad
The backtest was running well but when I tried with an earlier Start Date to run the backtest again I was getting this error message:
Runtime Error: Trying to retrieve an element from a collection using a key that does not exist in that collection throws a KeyError exception. To prevent the exception, ensure that the key exist in the collection and/or that collection is not empty. at OnOrderEvent self.symbols[orderEvent.Symbol].openingBar = None === at Python.Runtime.PyObject.Invoke(PyTuple args in main.py:line 69 KeyError : (,)
I look for some help in other discutions and I found this
So I tried to add the block of code I leave below to complement the whole code and I got a new error message:
Runtime Error: IndexOutOfRangeException : Index was outside the bounds of the array. at QuantConnect.Extensions.CreateType(PyObject pyObject) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Extensions.cs:line 2600 at QuantConnect.Algorithm.QCAlgorithm.History(PyObject type, PyObject tickers, Int32 periods, Nullable`1 resolution) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Algorithm/QCAlgorithm.Python.cs:line 842 at OnOrderEvent hist = self.History(self.symbols in main.py:line 64 IndexOutOfRangeException : Index was outside the bounds of the array. at QuantConnect.Extensions.CreateType(PyObject pyObject) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Common/Extensions.cs:line 2600 at QuantConnect.Algorithm.QCAlgorithm.History(PyObject type, PyObject tickers, Int32 periods, Nullable`1 resolution) in /LeanCloud/CI.Builder/bin/Debug/src/QuantConnect/Lean/Algorithm/QCAlgorithm.Python.cs:line 842
Again any help will be greatly appreciated
Louis Szeto
Hi Yaros
Sorry for the delay. We could not fully debug without the entire code for reproducing the problem. Yet, if the whole History call returns an empty frame, there would be no level in .index so it is not hashable. Thus, prior to the other conditions, checking the dataframe isn’t empty shall be done first:
For further assistance, please attach your full code snippets.
Best
Louis Szeto
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.
Yaros
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!