There is no need for logic there and that's why there is none. The entry order (aka main side) is protected automatically with a stop-loss side and a take-profit side (the high/low side depends on the direction entry), which are only active if the entry order is executed.
Once the stop-loss/take-profit sides become active, the execution of any of them automatically cancels the other.
If you decide to exit the market with an unrelated order, you have to manually cancel any of the sides (the other will be cancelled automatically) as in
self.cancel(order_stop_loss_side) # this automatically cancels the take-profit side too
@backtrader Good catch! I will find a better name for it. I think for the frequency and nature of the strategy, it works perfectly fine, especially when dividend/reinvestments are taken into account in these slow moving strategies.
Thanks for the continued help so far. It is great that you spend so much time to help the community in addition to the good work done to develop the framework.
I tried comparing using the script and initially got the same results. I.e The same values were being output. Eventually, later in the day I managed to get it working but if I am honest, I am not sure how. One thing I did was sync my clock with an NTP server as my time was a little off. However I can't imagine how that would have helped though.
BTW - The exec statements in my script are there just to import custom indicators under the same name name (i.e ind1) so I can easily switch the indicator used in my config file without going into the code. There might be a better way of doing that but that is where experience comes into play. :)
The platform makes no assumptions as to what it is in the _trades attribute when it starts. If something is there because you have pickled it and later unpickled it back into the attribute it will be used.
Orders is probably a different beast, because they travel between the broker and the strategys and are also in trades.
Saving and restoring the position (although it is attempted to read it from the broker) would be needed, because the actual position from the algorithm doesn't have to match what you have in the broker. You may have opened/closed positions manually.
One of the goals of backtrader was (and still is) to remain as much as possible Pure Python and only dependent on the standard packages available in the standard python distribution.
Being the only exception matplotlib for plotting purposes. There is actually no need to have it installed if only backtesting is wished.
Other dependencies like the ones needed for example for statsmodels were added by means of declarations (or meta-programming) which allow defining an Indicator as needing that dependency.
Using enums will be considered when a major rework of the platform is done and dependencies (or maybe python 2.7 support is dropped altogether) are included.
To print the name of bt.Order.Submitted you can query the order instance:
status_text = order.getstatusname(bt.Order.Submitted)
Additionally: at the start of backtesting the len of any data will be 0. The len increases with the delivery of each bar.
Other platforms choose to give you an index to the current bar being delivered. Using len is meant to be pythonic.
This topic somehow got down the ladder without being considered. Let's get down the right ladder which is getting to the core of the question.
order_target_size is meant to go to a target, not to execute an order for a specific given size
Sizer instances are meant to calculate the size of an order.
One can of course access the strategy and/or broker attributes and implement a targeting sizer.
Unlike some other concepts in the backtrader ecosystem, the Sizer instances don't automatically try to locate the surrounding environment (i.e.: finding out where it is being instantiated and therefore in which Strategy instance it is being created)
If the question is being rightly understood, this is an advantage in this situation. Accessing the logic could be done like this:
Create the desired Sizer manually in the strategy in which is going to be executed (and keep a reference to it, of course)
Manually set the broker attribute of the sizer: thesizer.broker = self.broker
Accessing the logic would be done by means of calling:
thesizer.getsizing(self, data, isbuy) # where isbuy is a boolean with `True` for long and `False` for short
You are using replay and mixing several data feeds. The current core is known (@RandyT has been the driving force) to have some weaknesses with replay.
The suggestion for such a mix would be to use resample
@you said in next() called multiple times within timeframe and bid/ask availability:
With IB LIVE data, when using resample and replay e.g. (data, timeframe=Minutes, compression=5), if there is really no market event triggered within the 5 minutes e.g. from 10:00am to 10:05am, no bar will be generated at 10:05am as next() has not been called due to lack of data trigger. Is there available option or standard mechanism that can force generation of the 10:05am bar or it has to be implemented locally? I thought I have the qcheck=0.5 default to force this but seems it has no effect.
qcheck does only indicate how often the feed will wake up to see if it can deliver. If there is nothing, nothing can be delivered and nothing will be delivered. The data feed (or the resampler) cannot fabricate data out of thin air.
With IB LIVE data, when using replay(data, timeframe=Minutes, compression=5), next() will be called for each tick received within the 5 mins compression period with the same len(self.data0) value e.g. len(self.data0)=299 between 10:00am to 10:05am. Is there a standard mechanism in next() that I can dintinguish the intra-period ticks and the end of period tick? Or do I need to check the event time e.g. look for event time at e.g. 10:05:00.000000
You can only know that the resampling of the specific bar is over when the len changes. The replay mechanism cannot know when the replay is going to be over. It will be over either because an incoming timestamp goes over the next boundary or because the real-time clock has gone over the boundary (plus more or less the value of qcheck)
With IB LIVE data, when using resample(data, timeframe=Minutes, compression=1), sometime next() will be called multiple times at start of a minute e.g. 10:00am but with exact same values (open, high, low, close and indicators). I have checked the time and sales in IB and there is no trades happened from 09:59:59am and 10:00:01am so wonder what may trigger the duplicate next() call
If the assumption is that this could be an error, one would need a sample and data.
But this is most likely due to a timestamp coming in which belongs to the previous minute, but cannot be delivered with the previous minute because that one is out. In realtime, that timestamp is delivered with the next minute. Looking at historical data gives a different view of course