Hi !
Following tutorials, I wanted to implement a simple strategy: if a currency price declines: open a Long position with trailing SL. If it increases: open a Short positon with trailing SL.
Unfortunately only opening short positions works correctly. When I want to combine both rules, I receive this error:
object has no attribute 'Long' at OnData if self.Long is not None and self.Securities ["SPY"].Close > self.highestSPYPrice: at Python.Runtime.PythonException.ThrowLastAsClrException() at Python.Runtime.PyObject.Invoke(PyTuple args in main.py: line 40
I can't get my head around this error, as each trailing SL is just the opposite of the other one. Probably there is a minor mistake somewhere, which I can't detect on my own. Could you please help me with this?
# region imports
from AlgorithmImports import *
# endregion
class UpgradedYellowScorpion(QCAlgorithm):
stopMarketTicket = None
stopMarketOrderFillTime = datetime.min
highestSPYPrice = -1
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2022, 1, 1)
self.SetCash(10000)
spy = self.AddEquity("SPY" , Resolution.Daily)
spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.Pair = "SPY"
self.symbols = [self.Pair]
self.prevPrices = { symbol : RollingWindow[TradeBar](7) for symbol in self.symbols }
def OnData(self, data):
for symbol in self.symbols:
if data.ContainsKey(symbol):
self.prevPrices[symbol].Add( data[symbol] )
if not all([ window.IsReady for window in self.prevPrices.values() ]):
return
Pair1_window = self.prevPrices[self.Pair]
Pair1_1D = Pair1_window[1].Close
Pair1_0D = Pair1_window[0].Close
# short position
if not self.Portfolio.Invested and Pair1_0D > Pair1_1D:
self.Short = self.MarketOrder("SPY", -1)
self.stopMarketTicket = self.StopMarketOrder("SPY", 1, 1.1 * self.Securities["SPY"].Close)
if self.Short is not None and self.Securities ["SPY"].Close < self.highestSPYPrice:
self.highestSPYPrice = self.Securities ["SPY"].Close
updateFields = UpdateOrderFields()
updateFields.StopPrice = self.highestSPYPrice * 1.1
self.stopMarketTicket.Update(updateFields)
# long position
if not self.Portfolio.Invested and Pair1_0D < Pair1_1D:
self.Long = self.MarketOrder("SPY", 1)
self.stopMarketTicket = self.StopMarketOrder("SPY", -1, 0.9 * self.Securities["SPY"].Close)
if self.Long is not None and self.Securities ["SPY"].Close > self.highestSPYPrice:
self.highestSPYPrice = self.Securities ["SPY"].Close
updateFields = UpdateOrderFields()
updateFields.StopPrice = self.highestSPYPrice * 0.9
self.stopMarketTicket.Update(updateFields)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled:
return
if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId:
self.stopMarketOrderFillTime = self.Time
Nico Xenox
Hey sebul,
you have to add the variables to initialize. Otherwise if it passes trough the if statements it wont recognize the variable. In other words you have to declare it first.
Hope it helps;)
Sebul
Hi Nico Xenox 😊
Thank you so much for your quick reply! Although adding the variable solved this issue, I am still struggling with an issue which I forgot to mention previously.
My “short side” of the strategy doesn't shift the StopMarketOrder, even though from what I see the code is written correctly. The “long side” of the code works perfectly fine. In the short side I Place a StopMarketOrder. If the current asset price if lower than the previous high: shift the pending order accordingly. Probably here again a minor issue is hidden somewhere, but despite analysing the code line after line I can't detect it.
Do you know what could be causing such an unexpected behaviour?
(Here is a version of the code only with the short side of the strategy:)
Nico Xenox
Hey sebul,
I'm not sure if you forgot to paste a part of the code but as you can see on the image the variable for highestSPYPrice doesnt change (stays -1).
And as you can see the closing price has to be smaller than the highestSPYPrice for it to enter the if statement and update the order.
You would have to update the variable for your strategy (e.g):
Meaning that you will have to adjust the highestSPYPrice to your liking.
highestSPY Price without variable changeSebul
Hey Nico Xenox,
Thank you, I really appreciate your help with my issue.
I am sorry but I am still a bit confused, I might be misunderstanding something. Using your edited code, StopMarketOrder for a short position is being shifted constantly, both on the way up and down. (see 1st screenshot).
If I understand correctly, it should only be changing its value when the price of a security declines and stay the same if it increases.
Here is a plotted chart of the “long side” of the Trailing strategy. So we are opening a Long position with a Trailing SL a few % below highest price. Here as we can see, StopMarketOrder is only shifted when the price decines, so behaves correctly.
The code to the “long side” is identical to the “short side” which I have been struggling with, apart from adjusting < and > signs and SL values. Do you know what could be the reason of such issue with one side of the strategy? Or is it working correctly and I am just missing something ?
Nico Xenox
Hey sebul,
as I already mentioned the variable ‘self.highestSPYPrice’ is the problem here. To show the problem:
green triangles: represent the update of your orderSo in your case the blue line has to be bigger in value than the green one. and after july 10 that isnt the case anymore. Because the black value which is the old ‘self.highestSPYPrice’ isnt anymore above the green line and so it doesnt update your orders.
You have to re-think the short strategy and maybe compare them together.
I will attach a backtest where you can change the values from long to short and see how your strategy reacts. From there you will notice what you will have to change so that it fits your strategy.
I added different chart to the backtesting where you will see the active stocks etc. There you can see that you nearly always are invested into the stock. Thats why you have a positive return.
Hope it helps ;)
Sebul
Nico Xenox
Ohh yes it makes perfect sense now, thank you so much for clarification! :)
Sebul
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!