For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

# converting pinescript indicators

• Hi

I have worked on converting a Pinescript indicator to backtrader.

I will describe some findings in the process, give some tips, show a example and so on.

variables
in pinescript, variables seem to work like lines in backtrader. So for every variable used in the script I have added a line:

pinescript:

``````setupCountUp = na
``````

``````self.setup_sell = bt.LineNum(float('nan'))
``````

conditions
to use methods like `barssince`, `valuewhen` from pinescript, which allows conditions like: `setupCountUp == self.p.setup_bars` the easiest method I found is to use numpy for this.

1. convert a line to a numpy array (using get(size=SIZE) to not work on complete line but only a defined range of data)
``````def line2arr(line, size=-1):
if size <= 0:
return np.array(line.array)
else:
return np.array(line.get(size=size))
``````
1. provide methods which works as methods from pinescript: so I wrote the methods I needed.
``````def na(val):
return val != val

def nz(x, y=None):
if isinstance(x, np.generic):
return x.fillna(y or 0)
if x != x:
if y is not None:
return y
return 0
return x

cond_len = len(condition)
occ = 0
since = 0
res = float('nan')
while cond_len - (since+1) >= 0:
cond = condition[cond_len-(since+1)]
if cond and not cond != cond:
if occ == occurrence:
res = since
break
occ += 1
since += 1
return res

def valuewhen(condition, source, occurrence=0):
res = float('nan')
if since is not None:
res = source[-(since+1)]
return res
``````

rewriting

the process of rewriting the pinescript in python using backtrader was simple after this. basically it can be split up to:

1. look for vars, params, signals used in pinescript
2. define vars, params, signal in indicator
3. strip all plot related code from pinescript
4. rewrite pinescript code in indicators next method

plotting

since i only want to have signals as lines in indicator, I did not set all vars used in pinescript in the lines dict, but in init. So I had a better control of what the indicator is plotting. But the indicator had more values then just the signals, so how to handle them?
The solution was a observer, which takes care of plotting all other information, the indicator has. Here you can setup all the plot related code from pinescript.

some remaining issues

maybe some of you guys know answers for this:

Questions:

• is it possible to create lines with conditions (outside of __init__) like: self.line1 > val ?
• is there a way to get something like barssince, valuewhen natively in backtrader?

Issues:

• the code is not very good, since a lot of the code can be done better in python, but if you don't want to fiddle to much with it, this should be fine
• refactoring would make the code better readable

I hope, this will help some of you!
I will post the code of a indicator, observer and the pinescript methods in a next post below.

• below there is code for the indicator from:

• pinescript.py

``````import numpy as np

def line2arr(line, size=-1):
'''
Creates an numpy array from a backtrader line

This method wraps the lines array in numpy. This can
be used for conditions.
'''
if size <= 0:
return np.array(line.array)
else:
return np.array(line.get(size=size))

def na(val):
'''
RETURNS
true if x is not a valid number (x is NaN), otherwise false.
'''
return val != val

def nz(x, y=None):
'''
RETURNS
Two args version: returns x if it's a valid (not NaN) number, otherwise y
One arg version: returns x if it's a valid (not NaN) number, otherwise 0
ARGUMENTS
x (val) Series of values to process.
y (float) Value that will be inserted instead of all NaN values in x series.
'''
if isinstance(x, np.generic):
return x.fillna(y or 0)
if x != x:
if y is not None:
return y
return 0
return x

'''

RETURNS
Number of bars since condition was true.
REMARKS
If the condition has never been met prior to the current bar, the function returns na.
'''
cond_len = len(condition)
occ = 0
since = 0
res = float('nan')
while cond_len - (since+1) >= 0:
cond = condition[cond_len-(since+1)]
# check for nan cond != cond == True when nan
if cond and not cond != cond:
if occ == occurrence:
res = since
break
occ += 1
since += 1
return res

def valuewhen(condition, source, occurrence=0):
'''
Impl of valuewhen

RETURNS
Source value when condition was true
'''
res = float('nan')
if since is not None:
res = source[-(since+1)]
return res

``````

• indicator.py

``````import backtrader as bt
from utils.pinescript import line2arr, barssince, valuewhen, na, nz

class TDSequentialInd(bt.Indicator):

'''
TD Sequential

This indicator implements a flexible rendition of TD Sequentials
Reference: DeMark Indicators by Jason Perl

based on:
http://practicaltechnicalanalysis.blogspot.com/2013/01/tom-demark-sequential.html

TD Indicators:
- TD Price Flips (setup_count_up/setup_count_down = 1)
- TD Setup count up (self.setup_count_up)
- TD Setup count down (self.setup_count_down)
- TD Sell Setup (self.setup_sell: price, self.l.setup_sell: signal)
- TD Sell Setup perfected (self.setup_sell_perf: price, self.l.setup_sell_perf: signal), can be deferred
- TD Setup Trend support (self.setup_trend_support)
- TD Setup Trend resistance (self.setup_trend_resistance)
- TD Countdown up (self.countdown_up)
- TD Countdown down (self.countdown_down)
- TD Sell Countdown qualify bar (self.countdown_count_up_imp == self.p.countdown_qual_bar)
- TD Sell Countdown deferred (self.countdown_sell_defer: price, self.l.countdown_sell_defer: signal)
- TD Sell Countdown (self.countdown_sell: price, self.l.countdown_sell: signal)
- TD Buy Countdown qualify bar (self.countdown_count_down_imp == self.p.countdown_qual_bar)
- TD Countdown Recycling (self.countdown_count_up_recycle: price, self.l.countdown_count_up_recycle: signal,
self.countdown_count_down_recycle: price, self.l.countdown_count_down_recycle: signal)
Note: Only one aspect of recycling is implemented where,
if Setup Up/Down Count == 2 * 'Setup: Bars', then the
present Countdown is cancelled.
Trend momentum has intensified.
- TD Risk Level (self.risk_level)
'''

DATA_CANDLES = 200  # use up to 200 candles for analysis

'''
setupIsPerfected = 2
setupIsDeferred = 1
'''
SETUP_IS_PERFECTED = 2
SETUP_IS_DEFERRED = 1
'''
cntdwnIsQualified = 2
cntdwnIsDeferred = 1
'''
COUNTDOWN_IS_QUALIFIED = 2
COUNTDOWN_IS_DEFERRED = 1

plotinfo = dict(subplot=True)

lines = (
'setup_sell',                   # sell setup
'setup_sell_perf',              # sell setup perfected
'countdown_sell',               # sell countdown
'countdown_sell_defer',
'countdown_count_up_recycle',
'countdown_count_down_recycle',
)

params = dict(
# data source to use, either close, hgih or low (Default: close)
data_source='close',
# the last Setup count (traditionally 9).
setup_bars=9,
# defines the previous bar to compare for counting (traditionally 4).
setup_lookback_bars=4,
# defines the previous count to evaluate for a perfected setup
# (this count and the next). Traditionally 3, i.e compare
# count 6 and 7 to count 8 or count 9.
setup_perf_lookback=3,
# If enabled, allow >= or <= in price comparisons.
setup_equal_enable=False,
# If disabled, only look back to the beginning of
# to find trends, low(support for Sell) or high(resistance for Buy)
# If enabled, look back beyond this Buy/Sell Setup event to
# the previous Setup event of the same kind.
setup_trend_extend=False,
# the last Countdown count (traditionally 13).
# Called the Buy/Sell Countdown event in this code, i.e. the
# last up/down count becomes the Buy/Sell Countdown event.
countdown_bars=13,
# define previous bar to compare for counting (traditionally 2).
countdown_lookback_bars=2,
# the bar in the Countdown sequence used to qualifiy the price
# If a countdown event doesn't qualify, counting continues.
# Note: If the Qualifier Bar is set >= "countdown_lookback_bars",
# qualification is disabled. Countdown events are still
# determined, just not qualified.
countdown_qual_bar=8,
# Use aggressive comparison. E.g. for Sell Countdown,
# instead of "Price: Source" >= high of "countdown_lookback_bars",
# use high >= high of "countdown_lookback_bars". Disabled by default.
countdown_aggressive=False,
)

def __init__(self):
super().__init__()

# data source to use
if self.p.data_source == 'high':
self.source = self.data_high
elif self.p.data_source == 'low':
self.source = self.data_low
else:
self.source = self.data_close

# setup prices
self.setup_price_up = (
self.source(0) > self.source(-self.p.setup_lookback_bars))
self.setup_price_down = (
self.source(0) < self.source(-self.p.setup_lookback_bars))
self.setup_price_equal = (
self.source(0) == self.source(-self.p.setup_lookback_bars))

# countdown prices
if self.p.countdown_aggressive:
self.countdown_price_up = (
self.data_high(0)
>= self.data_high(-self.p.countdown_lookback_bars))
self.countdown_price_down = (
self.data_low(0)
<= self.data_low(-self.p.countdown_lookback_bars))
else:
self.countdown_price_up = (
self.source(0)
>= self.data_high(-self.p.countdown_lookback_bars))
self.countdown_price_down = (
self.source(0)
<= self.data_low(-self.p.countdown_lookback_bars))

# indicators
self.tr = bt.ind.TrueRange()

# values
self.setup_sell = bt.LineNum(float('nan'))
self.setup_count_up = bt.LineNum(float('nan'))
self.setup_count_down = bt.LineNum(float('nan'))
self.setup_sell_perf_price = bt.LineNum(float('nan'))
self.setup_sell_perf = bt.LineNum(float('nan'))
self.setup_sell_count = bt.LineNum(float('nan'))
self.setup_trend_support = bt.LineNum(float('nan'))
self.setup_trend_resistance = bt.LineNum(float('nan'))
self.countdown_count_up = bt.LineNum(float('nan'))
self.countdown_count_down = bt.LineNum(float('nan'))
self.countdown_count_up_imp = bt.LineNum(float('nan'))
self.countdown_count_down_imp = bt.LineNum(float('nan'))
self.countdown_count_up_recycle = bt.LineNum(float('nan'))
self.countdown_count_down_recycle = bt.LineNum(float('nan'))
self.countdown_sell = bt.LineNum(float('nan'))
self.countdown_sell_defer = bt.LineNum(float('nan'))
self.countdown_sell_qual_price = bt.LineNum(float('nan'))
self.risk_level = bt.LineNum(float('nan'))

def next(self):

#
# ---- TD Setups ----------------------------
#

if self.p.setup_equal_enable:
if (self.setup_price_up
or (self.setup_count_up[-1]
and self.setup_price_equal)):
self.setup_count_up = nz(self.setup_count_up[-1]) + 1
if (self.setup_price_down
or (self.setup_count_down[-1]
and self.setup_price_equal)):
self.setup_count_down = nz(self.setup_count_down[-1]) + 1
else:
if self.setup_price_up:
self.setup_count_up = nz(self.setup_count_up[-1]) + 1
if self.setup_price_down:
self.setup_count_down = nz(self.setup_count_down[-1]) + 1

if self.setup_count_up == self.p.setup_bars:
setupCountUp = line2arr(self.setup_count_up, self.DATA_CANDLES)
priceSource = line2arr(self.source, self.DATA_CANDLES)
self.setup_sell = valuewhen(
setupCountUp == self.p.setup_bars,
priceSource,
0)

if self.setup_count_down == self.p.setup_bars:
setupCountDown = line2arr(self.setup_count_down, self.DATA_CANDLES)
priceSource = line2arr(self.source, self.DATA_CANDLES)
setupCountDown == self.p.setup_bars,
priceSource,
0)

line2arr(self.setup_sell, self.DATA_CANDLES))
if self.setup_count_up == self.p.setup_bars:
val = None
setupCountUp = line2arr(self.setup_count_up, self.DATA_CANDLES)
high = line2arr(self.data.high, self.DATA_CANDLES)
cond1 = valuewhen(
setupCountUp == (self.p.setup_bars
- self.p.setup_perf_lookback),
high, 0)
cond2 = valuewhen(
setupCountUp == (self.p.setup_bars
- self.p.setup_perf_lookback + 1),
high, 0)
if (cond1 >= cond2):
val = cond1
else:
val = cond2
self.setup_sell_perf_price = val
else:
self.setup_sell_perf_price = nz(self.setup_sell_perf_price[-1])

if self.setup_count_down == self.p.setup_bars:
val = None
setupCountDown = line2arr(self.setup_count_down, self.DATA_CANDLES)
low = line2arr(self.data.high, self.DATA_CANDLES)
cond1 = valuewhen(
setupCountDown == (self.p.setup_bars
- self.p.setup_perf_lookback),
low, 0)
cond2 = valuewhen(
setupCountDown == (self.p.setup_bars
- self.p.setup_perf_lookback + 1),
low, 0)
if cond1 >= cond2:
val = cond1
else:
val = cond2
else:

val = None
if self.setup_count_up == self.p.setup_bars:
high = line2arr(self.data_high, self.DATA_CANDLES)
setupCountUp = line2arr(
self.setup_count_up, self.DATA_CANDLES)
if (
(valuewhen(setupCountUp == (self.p.setup_bars-1), high, 0)
>= self.setup_sell_perf_price)
or (valuewhen(setupCountUp == self.p.setup_bars, high, 0)
>= self.setup_sell_perf_price)):
val = self.SETUP_IS_PERFECTED
else:
val = self.SETUP_IS_DEFERRED
if self.data_high >= self.setup_sell_perf_price:
val = self.SETUP_IS_PERFECTED
else:
if val is not None:

>= self.SETUP_IS_PERFECTED or (not na(self.setup_sell))):
val = None
if self.setup_count_down == self.p.setup_bars:
low = line2arr(self.data_low, self.DATA_CANDLES)
setupCountDown = line2arr(
self.setup_count_down, self.DATA_CANDLES)
if (
(valuewhen(setupCountDown == (self.p.setup_bars-1), low, 0)
(valuewhen(setupCountDown == self.p.setup_bars, low, 0)
val = self.SETUP_IS_PERFECTED
else:
val = self.SETUP_IS_DEFERRED
if self.data_low >= self.setup_sell_perf_price:
val = self.SETUP_IS_PERFECTED
else:
if val is not None:

self.setup_sell_perf = self.source
if self.setup_sell:
if self.p.setup_trend_extend and self.setup_sell_count[-1] > 0:
extend = int((
((self.setup_sell_count[-1]
- (self.setup_sell_count[-1] % self.p.setup_bars))
/ self.p.setup_bars)
+ 1) * self.p.setup_bars)
self.setup_trend_support = min(
self.data_low.get(size=extend))
else:
self.setup_trend_support = min(
self.data_low.get(size=self.p.setup_bars))
else:
self.setup_trend_support = nz(self.setup_trend_support[-1])

if self.p.setup_trend_extend and self.setup_buy_count[-1] > 0:
extend = int((
/ self.p.setup_bars)
+ 1) * self.p.setup_bars)
self.setup_trend_resistance = max(
self.data_high.get(size=extend))
else:
self.setup_trend_resistance = max(
self.data_high.get(size=self.p.setup_bars))
else:
self.setup_trend_resistance = nz(self.setup_trend_resistance[-1])

if self.setup_count_up == (2 * self.p.setup_bars):
setupCountUp = line2arr(self.setup_count_up, self.DATA_CANDLES)
priceSource = line2arr(self.source, self.DATA_CANDLES)
self.countdown_count_up_recycle = (
valuewhen(
(setupCountUp == (2 * self.p.setup_bars)),
priceSource,
0)
)
if self.setup_count_down == (2 * self.p.setup_bars):
setupCountDown = line2arr(self.setup_count_down, self.DATA_CANDLES)
priceSource = line2arr(self.source, self.DATA_CANDLES)
self.countdown_count_down_recycle = (
valuewhen(
(setupCountDown == (2 * self.p.setup_bars)),
priceSource,
0)
)

if na(self.countdown_price_up):
self.countdown_count_up = self.countdown_count_up[-1]
else:
val = None
if not (
or (self.source < self.setup_trend_support))
or (not na(self.countdown_count_up_recycle))):
if not na(self.setup_sell):
if self.countdown_price_up:
val = 1
else:
val = 0
elif not na(self.countdown_count_up[-1]):
if self.countdown_price_up:
val = self.countdown_count_up[-1] + 1
else:
val = self.countdown_count_up[-1]
if val is not None:
self.countdown_count_up = val
if na(self.countdown_price_down):
self.countdown_count_down = self.countdown_count_down[-1]
else:
val = None
if not (
(not na(self.setup_sell)
or (self.source > self.setup_trend_resistance))
or (not na(self.countdown_count_down_recycle))):
if self.countdown_price_down:
val = 1
else:
val = 0
elif not na(self.countdown_count_down[-1]):
if self.countdown_price_down:
val = self.countdown_count_down[-1] + 1
else:
val = self.countdown_count_down[-1]
if val is not None:
self.countdown_count_down = val

if self.countdown_price_up:
self.countdown_count_up_imp = self.countdown_count_up
if self.countdown_price_down:
self.countdown_count_down_imp = self.countdown_count_down

if self.p.countdown_qual_bar < self.p.countdown_bars:

if self.countdown_count_up_imp == self.p.countdown_qual_bar:
cntdwnCountUpImp = line2arr(
self.countdown_count_up_imp,
self.DATA_CANDLES)
priceSource = line2arr(
self.source,
self.DATA_CANDLES)
self.countdown_sell_qual_price = valuewhen(
cntdwnCountUpImp == self.p.countdown_qual_bar,
priceSource,
0)
else:
self.countdown_sell_qual_price = self.countdown_sell_qual_price[-1]

if self.countdown_count_down_imp == self.p.countdown_qual_bar:
cntdwnCountDownImp = line2arr(
self.countdown_count_down_imp,
self.DATA_CANDLES)
priceSource = line2arr(
self.source,
self.DATA_CANDLES)
cntdwnCountDownImp == self.p.countdown_qual_bar,
priceSource,
0)
else:

>= self.COUNTDOWN_IS_QUALIFIED)
or na(self.countdown_count_up[-1])):
val = None
cntdwnCountUpImp = line2arr(
self.countdown_count_up_imp, self.DATA_CANDLES)
high = line2arr(self.data_high, self.DATA_CANDLES)
if self.countdown_count_up_imp == self.p.countdown_bars:
if (valuewhen(
cntdwnCountUpImp == self.p.countdown_bars,
high,
0)) >= self.countdown_sell_qual_price:
val = self.COUNTDOWN_IS_QUALIFIED
else:
val = self.COUNTDOWN_IS_DEFERRED
if self.countdown_count_up_imp > self.p.countdown_bars:
if (valuewhen(
cntdwnCountUpImp > self.p.countdown_bars,
high,
0)) >= self.countdown_sell_qual_price:
val = self.COUNTDOWN_IS_QUALIFIED
else:
else:
if val is not None:

>= self.COUNTDOWN_IS_QUALIFIED)
or na(self.countdown_count_down[-1])):
val = None
cntdwnCountDownImp = line2arr(
self.countdown_count_down_imp, self.DATA_CANDLES)
low = line2arr(self.data_low, self.DATA_CANDLES)
if self.countdown_count_down_imp == self.p.countdown_bars:
if (valuewhen(
cntdwnCountDownImp == self.p.countdown_bars,
low,
val = self.COUNTDOWN_IS_QUALIFIED
else:
val = self.COUNTDOWN_IS_DEFERRED
if self.countdown_count_down_imp > self.p.countdown_bars:
if (valuewhen(
cntdwnCountDownImp > self.p.countdown_bars,
low,
val = self.COUNTDOWN_IS_QUALIFIED
else:
else:
if val is not None:
else:
if self.countdown_count_up == self.p.countdown_bars:
if self.countdown_count_down == self.p.countdown_bars:

if self.countdown_count_up_imp:
if self.countdown_count_down_imp:
priceSource = line2arr(
self.source, self.DATA_CANDLES)
self.countdown_sell = valuewhen(
priceSource,
0)
priceSource = line2arr(
self.source, self.DATA_CANDLES)
self.countdown_sell_defer = valuewhen(
priceSource,
0)

priceSource = line2arr(
self.source, self.DATA_CANDLES)
priceSource,
0)
priceSource = line2arr(
self.source, self.DATA_CANDLES)
priceSource,
0)

if self.setup_sell or self.countdown_count_up_recycle:
high = line2arr(self.data_high, self.p.setup_bars)
tr = line2arr(self.tr, self.p.setup_bars)
highest = max(high)
self.risk_level = highest + valuewhen(high == highest, tr, 0)
low = line2arr(self.data_low, self.p.setup_bars)
tr = line2arr(self.tr, self.p.setup_bars)
lowest = min(low)
self.risk_level = lowest - valuewhen(low == lowest, tr, 0)
elif self.countdown_sell:
high = line2arr(self.data_high, self.p.countdown_bars)
tr = line2arr(self.tr, self.p.countdown_bars)
highest = max(high)
self.risk_level = highest + valuewhen(high == highest, tr, 0)
low = line2arr(self.data_low, self.p.countdown_bars)
tr = line2arr(self.tr, self.p.countdown_bars)
lowest = min(low)
self.risk_level = lowest - valuewhen(low == lowest, tr, 0)
else:
self.risk_level = nz(self.risk_level[-1], self.data_low)

# setup_sell
self.l.setup_sell = 0
if not na(self.setup_sell):
self.l.setup_sell = 1
# setup_sell_perf
self.l.setup_sell_perf = 0
if not na(self.setup_sell_perf):
self.l.setup_sell_perf = 1
# countdown_sell
self.l.countdown_sell = 0
if not na(self.countdown_sell):
self.l.countdown_sell = 1
# countdown_sell_defer
self.l.countdown_sell_defer = 0
if not na(self.countdown_sell_defer):
self.l.countdown_sell_defer = 1
# countdown_count_up_recycle
self.l.countdown_count_up_recycle = 0
if not na(self.countdown_count_up_recycle):
self.l.countdown_count_up_recycle = 1
# countdown_count_down_recycle
self.l.countdown_count_down_recycle = 0
if not na(self.countdown_count_down_recycle):
self.l.countdown_count_down_recycle = 1
``````

• observer.py

``````import backtrader as bt

class TDSequentialObs(bt.observers.Observer):

plotinfo = dict(plot=True,
subplot=False,
plotlegend=False,
plotlinelegend=False,
plotlinelabels=False,
plotvaluetags=False,
plotlinevalues=True,
plotabove=True,
plotname="TD Sequential",)

lines = (
'seq_up',               # up count marker
'seq_down',             # down count marker
's_sell',               # setup sell marker
's_sell_perf',          # setup sell perf marker
's_sell_perf_price',
's_trend_support',
's_trend_resistance',
'cd_seq_up',            # countdown up marker
'cd_seq_down',          # countdown down marker
'cd_sell',              # countdown sell marker
'cd_sell_defer',        # countdown sell defer marker
'cd_cnt_up_recycle',    # countdown up recycle marker
'cd_cnt_down_recycle',  # countdown down recycle marker
'risk_level'
)

plotlines = dict(
seq_up=dict(
marker='*', markersize=4.0, color='black', ls='',),
seq_down=dict(
marker='*', markersize=4.0, color='black', ls='',),
marker='2', markersize=5.0, color='green',),
s_sell=dict(
marker='1', markersize=5.0, color='maroon',),
marker='^', markersize=5.0, color='green',),
s_sell_perf=dict(
marker='v', markersize=5.0, color='maroon',),
cd_seq_up=dict(
marker='*', markersize=5.0, color='blue', ls='',),
cd_seq_down=dict(
marker='*', markersize=5.0, color='blue', ls='',),
marker='^', markersize=10.0, color='green',),
cd_sell=dict(
marker='v', markersize=10.0, color='maroon',),
marker='\$D\$', markersize=5.0, color='red', ls='',),
cd_sell_defer=dict(
marker='\$D\$', markersize=5.0, color='red', ls='',),
cd_count_up_recycle=dict(
marker='\$R\$', markersize=5.0, color='blue', ls='',),
cd_count_down_recycle=dict(
marker='\$R\$', markersize=5.0, color='blue', ls='',),
risk_level=dict(lw=2.0, ls='--',),
)

params = dict(
td_seq=None,
bardist=0.0006,
plot_setup_count=True,
plot_setup_signals=True,
plot_setup_tdst=True,
plot_countdown_count=True,
plot_countdown_signals=True,
plot_risk_level=True
)

def __init__(self):
self.td_seq = self.p.td_seq
self.countdown_up_prev = None
self.countdown_down_prev = None

def _plotlabel(self):
return [None]

def _get_line(self, name):
line = getattr(self.lines, name, False)
return line

def next(self):
# setup signals
if self.p.plot_setup_signals:
# setup buy / sell signal
self.l.s_buy = self.data_low - (3 * self.p.bardist)
if self.td_seq.l.setup_sell:
self.l.s_sell = self.data_high + (3 * self.p.bardist)
# setup perfected buy / sell signal
self.l.s_buy_perf = self.data_low - (4 * self.p.bardist)
if self.td_seq.l.setup_sell_perf:
self.l.s_sell_perf = self.data_high + (4 * self.p.bardist)

# setup count
if self.p.plot_setup_count:
# setup count up sequence
if self.td_seq.setup_count_up > 0:
self.l.seq_up = self.data_low + self.p.bardist
# setup count down sequence
if self.td_seq.setup_count_down > 0:
self.l.seq_down = self.data_high - self.p.bardist

# setup trend support / resistance (TDST)
if self.p.plot_setup_tdst:
self.l.s_trend_support = self.td_seq.setup_trend_support
self.l.s_trend_resistance = self.td_seq.setup_trend_resistance

# countdown count
if self.p.plot_countdown_count:
# countdown count up sequence
if self.td_seq.countdown_count_up_imp >= 0:
if self.td_seq.countdown_count_up_imp != self.countdown_up_prev:
self.l.cd_seq_up = self.data_low + (2 * self.p.bardist)
self.countdown_up_prev = self.td_seq.countdown_count_up_imp
# countdown count down sequence
if self.td_seq.countdown_count_down_imp >= 0:
if self.td_seq.countdown_count_down_imp != self.countdown_down_prev:
self.l.cd_seq_down = self.data_high - (2 * self.p.bardist)
self.countdown_down_prev = self.td_seq.countdown_count_down_imp

# countdown signals
if self.p.plot_countdown_signals:
self.l.cd_buy = self.data_low - (4 * self.p.bardist)
if self.td_seq.l.countdown_sell:
self.l.cd_sell = self.data_high + (4 * self.p.bardist)

self.l.cd_buy_defer = self.data_low - (3 * self.p.bardist)
if self.td_seq.l.countdown_sell_defer:
self.l.cd_sell_defer = self.data_high + (3 * self.p.bardist)

# countdown_up_recycle/down_recycle
if self.td_seq.l.countdown_count_up_recycle:
self.l.cd_cnt_up_recycle = self.data_low + (3 * self.p.bardist)
if self.td_seq.l.countdown_count_down_recycle:
self.l.cd_cnt_down_recycle = self.data_high - (3 * self.p.bardist)

# risk level
if self.p.plot_risk_level:
self.l.risk_level = self.td_seq.risk_level
``````

• strategy.py

``````class StrategyTDSequential(bt.Strategy):

def __init__(self):
'''Initialization '''
self.td_seq = TDSequentialInd()
False,
TDSequentialObs,
self.data,
td_seq=self.td_seq,
plot_setup_signals=True,
plot_setup_count=True,
plot_setup_tdst=True,
plot_countdown_signals=True,
plot_countdown_count=True,
plot_risk_level=True)

``````

• I had to strip all comments from the indicator since it was too big to post, if anyone is interested in complete code, just send me a message.

• Hello, I just started with backtrader recently and have a noob question. I'm not really converting pine script (that was the original plan though) but I have been programming in pine for about 2 years so i'm very used to it. I'm trying to program in a similar way using backtrader.

This is probably super simple for you guys, but how does one make a variable in backtrader which acts like open/high/low/close whereas you can reference its' current candle value as var and 10 candles back would be var[-10] and so on? I thought I could do it as a line, but when I try to set myLine it doesn't let me. I need to be able to set its' current value and change it, and have that change apply to it when you reference it using myLine[-1] and myLine[-2] etc. while moving through the candles. Any insight would really be helpful because these docs are very extensive (which I very much appreciate) but the terminology gets confusing and i'm having trouble figuring this out. Once I solve this I can create most of my strategy very quickly because I can then program like i'm using pine. Thanks in advance for any help or advice anyone can offer.

• @Wayne-Filkins check out the code above, there is an example how to do this.

it would be this, what you want, I think:

``````self.line = bt.LineNum(float('nan'))
``````

without the need to define the lines dict.

Also check out the pinescript.py where I added some methods from pinescript.

• Yes if I had scrolled further in your code blocks I probably would have figured that out. Hadn't had coffee yet haha. I saw some of the functions you made like nz() and barssince(). This is something I was wanting to do but would probably take me forever so really thankful you are sharing them!

• @dasch Hey man if you have a minute, i'm trying to get bases (basically like looking 50 candles back and comparing that candle to the 50 before it and 50 after it to see if it's the lowest) and it's not quite working. I declared the base in init() with:

``````self.base = bt.LineNum(float('1.0'))
``````

So basically in next() it starts out as 1, and I loop through the last 100 candles to try to compare to see if any are lower, and if any are lower it changes it to 0 (so it's not a pivot). This is the code in next():

``````        for i in range(-2 * 50, -1):
if self.datalow[-2 * 50] in locals():
if self.datalow[i] < self.datalow[-1 * 50]:
self.base = 0.0

if self.base == 1.0:
print("we have a pivot!")
``````

So yeah basically i'm trying to compare the last 100 candles to close[-50], and do this on every iteration of next(). Any idea why this isn't working? self.base just stays as 1.0 the whole time. Thanks in advance for anyone who can help with this!

• @dasch I did all that in strategy.py. Do I have to make an indicator.py and observer.py too to do stuff like this?

• @dasch NM I got it figured out! base didn't have  on it, and the thing that was really messing it up was the "in locals()" thing wasn't working so I had to do a counter so that it didn't get the out of range error on the first 50 candles, since you know it can't look back 50 candles at the beginning if there aren't that many. Had to do that stuff a lot in pine script, always had a bunch of various counters lol. Do you know how I can plot these base/pivots with a dot on the low of the candle?

• @dasch Can you send the complete code so I can see the comments? It's pretty confusing. Idk how to send a message on this goofy platform though

• Sure, send me an email. Mine email is in my profile.

});