The idea of capturing your own ticks is in theory sound, but has always practical problems. And that's why companies that collect and curate data make money out of it.
Collecting ticks or minute bars or anything else with backtrader is just a python statement away (see for example the trace that run with 2 instruments collecting 1-minute bars during approximately 35 minutes)
The data points could have been stored somewhere (hierarchically or not) instead of simply having printed them out.
An extra fix has been pushed to the development branch, because @randyt also checked it out and found a typo in the part which services historical data downloads between 2 dates (the part initially tested, downloads as much as possible and not between two given dates, which may happend during a disconnection/reconnection cycle)
Nothing prevents you from querying the Sizer which has been added to the strategy.
See the getsizer method of the strategy
Since you have subclassed Sizer (or SizerBase, which is the same), you control any extra interfaces/methods which may help you in the querying.
With that in mind, the Sizer concept was developed with the other direction in mind and that's why inside the Sizer there is a strategy member attribute, which allows the sizer to query whatever interfaces the strategy may have. The rationale behind that: abstracting the Strategy from any logic related to position sizing, allowing it to concentrate on the logic of entering/exiting the market.
The Sizerattributes are describe here: https://www.backtrader.com/docu/sizers/sizers.html#backtrader.Sizer
Over the strategy attribute you can also reach the broker, to for example query the actual net liquidation value
value = self.strategy.broker.get_value()
That would mean that there are actually no transactions happening during those seconds. With 2 possibilities in mind:
You have joined the data feed during a not so active market phase
You are testing against the demo account setup of TWS
Take into account that the data feed provided by IB for Forex is not like the data feed for equities, for example. Appearances of the BID price will be recorded as the indication for price oscillation. This is what also others do.
In any case you may also run the sample with --debug and you will have the chance to see all messages delivered by TWS to your client (literally all), which may help you follow the details of the price formation in Forex
You may change BID to ASK or MID (check the IB documentation for possible values) by manipulating the what parameter during the creation of an IBData data feed in backtrader.
Read here: Data feeds reference
If your number 1 goal is:
Learn Python so I can make use of Python to code my strategy
You shouldn't probably be using btrun which is meant to abstract things and won't help with learning.
Non code reports are difficult to look into. In your case the last lines give an insight:
File "c:\users\charl\anaconda3\lib\site-packages\backtrader\feeds\btcsv.py", line 44, in _loadline
y = int(dttxt[0:4])
ValueError: invalid literal for int() with base 10: '14/0'
You are loading a csv data with the BactraderCSVData data feed and the field which is being encountered is 14/0 which cannot be parsed.
If that csv file is ok you best options:
Use GenericCSVData (see here) and configure the fields according to reality
Load it with pandas.read_csv and then use a PandasData (see here)data feed also configuring the fields
But if you are learning I would recommend focusing on easy and tested samples (and the quickstart), rather than feeding in your own data sources.
As mentioned by @ab_trader orders are executed on the same bar if you set coc=True in the broker. If not the bar is considered closed and order execution is deferred until the next bar. Two orders issued on the same bar with Market execution policy should be executed also on the same bar (be it the next or the current bar)
The system does a check of submitted orders before accepting them. To avoid for example having 2 consecutive orders which would go over the actual cash reserves on the same bar. If tries to take into account that if a long position is closed cash will be re-injected into the system to let you operate with another buy.
Deactivation of the check can be achieved in the broker with checksubmit=False
Sizers are called internally by buy and sell (close uses the actual existing position) but a Sizer can be subclassed to provide complex functionality.
In that documentation page there are examples of how the modification of _getsizing achieves policies, like for example reversing a position or keeping it long only.
It's not fool proof. Use safediv offers you an option to still use the Stochastic on data which is not probable meant for.
The safediv parameter plays together with safezero which has a default value of 0.0. This is a choice and can be changed by you. Because once you enter in the territory of indetermination, assigning a value to an operation which hasn't got a value means making a compromise.
There isn't documentation as such, given the differences found in different platforms.
Oanda (https://www.oanda.com) was the last integration and was at the same time an effort to try to streamline the interfaces and make it as generic as possible. I would have a look at it.
But being your provider Windows based and with OCX in play I guess you'll have to play with comtypes. Although there are, at least, 2 major COM Windows implementations, you are basically on your own. Visual Chart (https://www.visualchart.com) is also Windows based and uses COM and the following still holds:
win32com (pywin32) launched OutOfMemoryError exceptions because it wouldn't fully understand a type.
Although the type was properly decoded, it was clearly going to be a headache and meant fiddling directly with C++ for any needed modification (the major problem being other people compiling pywin32 from scratch or installing a DLL from a non-trusted source, not to mention the lack of proper support for GCC based compilers under Windows that Python exhibits)
comtypes being pure Python was more up to the task of being a target. It still failed to properly decode complex arrays, so a fork of comptypes was needed for full support.
See the fork: https://github.com/mementum/comtypes
See the pull request (from Apr 9) quietly waiting for integration in the master: https://github.com/enthought/comtypes/pull/104
Hopefully your provider is using simpler types and nothing breaks with the standard distributions.
For implementation, the following pattern was chosen:
Store: is an entity which should be the only one talking to the data/broker provider, be it interfacing with sockets or COM
Both the data feed and the broker rely on calling the store and receiving events from it (yes it has to run in a separate thread) to make the information available to the platform.
The suggestion would be to look at the patterns in the Oanda store and associated broker and feeds.
They are plotted. The problem arises because matplotlib makes assumptions about situations where the difference between the top and bottom of a plot is too small and could lead to problems.
The plot below plots the BuySell observer on a different axis to make it clear: I am here. But cannot be seen with the data. The right axis ticks (Y) show values like 3.5 and the top of the axis (already inside the observer) indicates that everything has to be multiplied by 1000 (3.065e3)
The scale is wrong obviously because it goes from 0 to 3.065
Probably your case too.
As explained above order importing is not a feature of the the platform.
If you have the data in that format:
there are several approaches:
Make a custom data feed with it containing only 2 fields
trigger (or any other name you wish)
In this case you would add the data feed to the system and could at each moment in time and synchronized with the data read if an order has to be executed or not
Read that input with something like a pandas.read_csv module and during each call to next in the strategy, look for the corresponding datetime index in the dataframe to see if an order has to be executed or not.