An Order instance (let's call it order) carries an attribute data which is the asset on which the order has been issued. The ticker name should be available as: order.data._name (where _name is the name you have assigned when using adddata)
Names are actually a late addition to backtrader, because one of the underlying design concepts is that the development of a trading idea should, ideally, not be bound to a specific asset.
A signal / indicator has (like a Strategy) receives an array of the available data feeds in the environment in which they are instantiated (an Indicator can be instantiated inside a Strategy or inside another Indicator)
If your Strategy has 4 data feeds, the indicator will default to receiving 4 data feeds, which will be available in the iterable self.datas and as self.data0, ..., self.data3. Most indicators actually only use 1 data feed. One can actually declare that a given indicator must receive more than 1 data feed.
See: Blog - Crossing over numbers
Notice that it defaults to receiving the data feeds from the environment.. Because if the user specifies which data feeds go by passing them to the instantiation, those will be the ones passed.
Another reason not to rely on names is that an indicator can receive another indicator as data feed. For example:
sma_on_sma = bt.ind.SMA(bt.ind.SMA(self.data1, period=10), period=20)
The outer SMA receives the inner SMA as data feed. And this data feed carries no name, even if it's calculating the simple moving average of self.data1. We still don't know what self.data1 is. It could be an RSI for example. See:
lines = ('sma_on_sma',)
self.lines.sma_on_sma = bt.ind.SMA(bt.ind.SMA(self.data1, period=10), period=20)
lines = ('myline',)
self.lines.myline = MyInd1(self.data, bt.ind.RSI(self.data))
MyInd2 passes 2 data feeds, one is the main data feed received (which can be anythin) and the 2nd is the RSI. As such MyInd1 receives an RSI instance as self.data1, but it doesn't know it. It simply does an SMA on it and then a 2nd SMA
If your signal is going to be name-bound, it will be limited to real data feeds which carry a _name attribute and will fail to be generic for any kind of actual data feed, like in the examples above.
lines = ('name_bound',)
if self.data0._name == 'my-preferred-name':
self.lines.name_bound = bt.ind.SMA(self.data3)
self.lines.name_bound = bt.ind.SMA(self.data2)
At least data0 must be a real data feed with a name in that example.
@Taewoo-Kim said in Data Analysis all wrong?:
limiting # of posts I can per day
Spam anti-measures limit # of posts within short time spans (not per day)
Now i get this error (on the same code base)
Probably because downgrading to pyfolio-0.5.1 doesn't downgrade the packages with which it works. In this case and due to the changes in the pyfolio API, it will take sometime to get it working again.
@JohnShiu said in Extracting variables defined in strategy:
(1) Is it possible to extract the variables defined in the strategy, e.g. indicator_log, in analyzer?
See Docs - Analyzers
Each analyzer has automatically an attribute named strategy which obviously points to the strategy in which the analyzer is inserted.
Shifting is implemented with the (x) operators in lines objects.
See: Docs - Concepts and look for delayed. The use of the term delayed was meant to indicate that looking into the future is not possible. But it is possible for indicators to project values into the future (like Ichimoku does), although it is seldom used.
newline = self.rsi + self.rsi(-1)
Notice how the first term doesn't use the (x) notation and with it it simply takes the current value. One could of course also do it formally as:
newline = self.rsi(0) + self.rsi(-1)
which is functionally 100% equivalent but not 100% memory equivalent because self.rsi(0) will create an extra buffer for the copies of itself.
RSI has a single line, so there is not much need to address the specific lines and by simply using the indicator itself the 1st line in the indicator is automatically used.
But for indicators with multiple lines like a MACD, one can also do things like:
newline = self.macd + self.macd(-1) # 1st line
newline = self.macd.lines.macd + self.macd.lines.macd(-1) # the 1st line is also called macd
newline = self.macd + self.macd.lines.signal(-1) # sum of present 1st line + delayed line "signal"
As with ta-lib the best approach would be to autogenerate indicators/functions for the things in those packages.
ta-lib provides that meta-information. In the case of standard libraries, the signature can be parsed but the return values cannot.
New changes have only uncovered a typo in the ta-lib integration by which auto-generated wrappers had the module itself in the __module__ attribute instead of the module name. Corrected in the development branch
Operations like self.x = (self.data.high + self.data.low) / 2.0 generate a lazily evaluated lines object.
Even this generates that: self.y = self.data.close > self.data.open
Here the  operator is used to access the values provided by lines objects like the generated self.x or self.y or standard lines like self.data.high
if self.data.close > self.data.open:
Thanks to operator overloading the following is equivalent also in next
if self.data.close > self.data.open:
But unlike in __init__, this comparison generates a bool.
One can generate a complete indicator just by using operations and logic in __init__. See for example an old friend of everybody like MACD (removing documentation and plotting preparation boilerplate)
lines = ('macd', 'signal',)
params = (
('period_me2', 26), ('period_signal', 9),
me1 = self.p.movav(self.data, period=self.p.period_me1)
me2 = self.p.movav(self.data, period=self.p.period_me2)
self.lines.macd = me1 - me2
self.lines.signal = self.p.movav(self.lines.macd, period=self.p.period_signal)
No need to use scalar operations/logic in next. Everything is defined in terms of lines objects.
Some indicators may need next. Some help from things defined in __init__ can be used. For example the ZeroLagIndicator
alias = ('ZLIndicator', 'ZLInd', 'EC', 'ErrorCorrecting',)
lines = ('ec',)
params = (
self.ema = MovAv.EMA(period=self.p.period)
self.limits = [-self.p.gainlimit, self.p.gainlimit + 1]
# To make mixins work - super at the end for cooperative inheritance
leasterror = MAXINT # 1000000 in original code
bestec = ema = self.ema # seed value 1st time for ec
price = self.data
ec1 = self.lines.ec[-1]
alpha, alpha1 = self.ema.alpha, self.ema.alpha1
for value1 in range(*self.limits):
gain = value1 / 10
ec = alpha * (ema + gain * (price - ec1)) + alpha1 * ec1
error = abs(price - ec)
if error < leasterror:
leasterror = error
bestec = ec
self.lines.ec = bestec
Here the base ema for the calculations is defined in __init__ but the core of the operations which is a loop over several values is best done in next.
The code by @randyt is 100% ok. Some versions ago and to reduce the number of needed imports, the main package gives direct access to subpackages, so you may also do
import backtrader as bt
hi_bar = bt.indicators.Highest(...)
of for even less typing
import backtrader as bt
hi_bar = bt.ind.Highest(...)
@skytrading54 said in indicator using a value from the data other than close:
I believe most of the built in indicators are using close value from the data for computation, for example SMA indicator is using close values of all the bars to derive sma... (is this correct)
This is wrong. The SMA uses the value from the 1st line of the data feed passed to it. For regular data feeds containing prices this happens to be the close price because this is the de-facto industry standard.
See this post from yesterday where you can see that you can pass anything which is a line to any indicator:
Community - Laguerre RSI
Just pass self.data.high or self.data.volume to the indicator of your choice.
The exception are indicators which rely on specific price points like the Stochastic which uses 3 of the for usual price components (high, low, close ... leaving open unused)
The final implementation, already pushed to the development branch, respects the original implementation by Ehlers and rather than expecting the price component to have high and low it takes simply self.data (which unless changed is the close from regular data feeds)
Using it for the midpoint of the bar is easy:
midpoint = (self.data.high + self.data.low) / 2.0
lrsi = bt.ind.LRSI(midpoint)
The advantage of not having specifics like high and low in the code is that the LRSI can be calculated for anything which is a data feed or descendant of ... you could create the LRSI of a SimpleMovingAverage
In case anyone would be tempted to try it, here a pre-packaged CumulativeRSI indicator.
lines = ('cumrsi',)
params = (('period', 14), ('count', 2),)
alias = ('CumRSI',)
rsi = bt.ind.RSI(self.data, period=self.p.period)
self.lines.cumrsi = bt.indicators.SumN(rsi, period=self.p.count)
Another strategy using Super Trend and SAR
WHEN TO ENTER A TRADE:
Enter: a BUY trade when SUPERTREND is in a bullish trend(line is green), and SAR gives a BUY signal. (Don't enter a SELL trade if SUPERTREND is bullish and SAR gives a SELL signal.
Enter: a SELL trade when SUPERTREND is in a bearish trend(line is red), and SAR gives a SELL signal. (Don't enter a BUY trade if SUPERTREND is bearish and SAR gives a BUY signal.
Get out of a BUY trade when SAR give a SELL signal, and the opposite for a SELL trade.
order_target_percent is not aimed at stop-loss. The order_target_xxx family of methods allow to size an order using for example expected value or percentage of value but don't set stop losses.
There is no need for __init__ in that sample. The logic for the orders is 100% in next and is explained here: