Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    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

    General Code/Help
    2
    15
    532
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      dasch last edited by

      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
      

      backtrader ind.__init__:

      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
      
      def barssince(condition, occurrence=0):
          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')
          since = barssince(condition, occurrence)
          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.

      1 Reply Last reply Reply Quote 2
      • D
        dasch last edited by

        below there is code for the indicator from:
        https://www.tradingview.com/script/t08BkTIg-TD-Sequential/

        1 Reply Last reply Reply Quote 0
        • D
          dasch last edited by

          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
          
          
          def barssince(condition, occurrence=0):
              '''
              Impl of barssince
          
              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
              + added occurrence
          
              RETURNS
              Source value when condition was true
              '''
              res = float('nan')
              since = barssince(condition, occurrence)
              if since is not None:
                  res = source[-(since+1)]
              return res
          
          
          1 Reply Last reply Reply Quote 1
          • D
            dasch last edited by

            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:
                https://www.tradingview.com/script/t08BkTIg-TD-Sequential/
                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 Buy Setup (self.setup_buy: price, self.l.setup_buy: signal)
                - TD Buy Setup perfected (self.setup_buy_perf: price, self.l.setup_buy_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 Buy Countdown deferred (self.countdown_buy_defer: price, self.l.countdown_buy_defer: signal)
                - TD Buy Countdown (self.countdown_buy: price, self.l.countdown_buy: signal)
                - 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_buy',                    # buy setup
                    'setup_sell_perf',              # sell setup perfected
                    'setup_buy_perf',               # buy setup perfected
                    'countdown_sell',               # sell countdown
                    'countdown_buy',                # buy countdown
                    'countdown_sell_defer',
                    'countdown_buy_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
                    # this Buy/Sell Setup event
                    # 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
                    # of the Buy/Sell Countdown event(traditionally 8).
                    # 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__()
                    self.addminperiod(self.p.setup_lookback_bars + 1)
            
                    # 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_buy = 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_mask = bt.LineNum(float('nan'))
                    self.setup_sell_perf = bt.LineNum(float('nan'))
                    self.setup_buy_perf_price = bt.LineNum(float('nan'))
                    self.setup_buy_perf_mask = bt.LineNum(float('nan'))
                    self.setup_buy_perf = bt.LineNum(float('nan'))
                    self.setup_sell_count = bt.LineNum(float('nan'))
                    self.setup_buy_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.countdown_sell_qual_mask = bt.LineNum(float('nan'))
                    self.countdown_sell_qual_mask_imp = bt.LineNum(float('nan'))
                    self.countdown_buy = bt.LineNum(float('nan'))
                    self.countdown_buy_defer = bt.LineNum(float('nan'))
                    self.countdown_buy_qual_price = bt.LineNum(float('nan'))
                    self.countdown_buy_qual_mask = bt.LineNum(float('nan'))
                    self.countdown_buy_qual_mask_imp = 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[0]
                            or (self.setup_count_up[-1]
                                and self.setup_price_equal[0])):
                            self.setup_count_up[0] = nz(self.setup_count_up[-1]) + 1
                        if (self.setup_price_down[0]
                            or (self.setup_count_down[-1]
                                and self.setup_price_equal[0])):
                            self.setup_count_down[0] = nz(self.setup_count_down[-1]) + 1
                    else:
                        if self.setup_price_up[0]:
                            self.setup_count_up[0] = nz(self.setup_count_up[-1]) + 1
                        if self.setup_price_down[0]:
                            self.setup_count_down[0] = nz(self.setup_count_down[-1]) + 1
            
                    if self.setup_count_up[0] == self.p.setup_bars:
                        setupCountUp = line2arr(self.setup_count_up, self.DATA_CANDLES)
                        priceSource = line2arr(self.source, self.DATA_CANDLES)
                        self.setup_sell[0] = valuewhen(
                            setupCountUp == self.p.setup_bars,
                            priceSource,
                            0)
            
                    if self.setup_count_down[0] == self.p.setup_bars:
                        setupCountDown = line2arr(self.setup_count_down, self.DATA_CANDLES)
                        priceSource = line2arr(self.source, self.DATA_CANDLES)
                        self.setup_buy[0] = valuewhen(
                            setupCountDown == self.p.setup_bars,
                            priceSource,
                            0)
            
                    self.setup_sell_count[0] = barssince(
                        line2arr(self.setup_sell, self.DATA_CANDLES))
                    self.setup_buy_count[0] = barssince(
                        line2arr(self.setup_buy, self.DATA_CANDLES))
                    if self.setup_count_up[0] == 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[0] = val
                    else:
                        self.setup_sell_perf_price[0] = nz(self.setup_sell_perf_price[-1])
            
                    if self.setup_count_down[0] == 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
                        self.setup_buy_perf_price[0] = val
                    else:
                        self.setup_buy_perf_price[0] = nz(self.setup_buy_perf_price[-1])
            
                    if not (nz(self.setup_sell_perf_mask[-1])
                            >= self.SETUP_IS_PERFECTED or (not na(self.setup_buy[0]))):
                        val = None
                        if self.setup_count_up[0] == 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[0])
                                or (valuewhen(setupCountUp == self.p.setup_bars, high, 0)
                                    >= self.setup_sell_perf_price[0])):
                                val = self.SETUP_IS_PERFECTED
                            else:
                                val = self.SETUP_IS_DEFERRED
                        elif not na(self.setup_sell_perf_mask[-1]):
                            if self.data_high[0] >= self.setup_sell_perf_price[0]:
                                val = self.SETUP_IS_PERFECTED
                            else:
                                val = self.setup_sell_perf_mask[-1]
                        if val is not None:
                            self.setup_sell_perf_mask[0] = val
            
                    if not (nz(self.setup_buy_perf_mask[-1])
                            >= self.SETUP_IS_PERFECTED or (not na(self.setup_sell[0]))):
                        val = None
                        if self.setup_count_down[0] == 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)
                                    <= self.setup_buy_perf_price[0]) or
                                (valuewhen(setupCountDown == self.p.setup_bars, low, 0)
                                    <= self.setup_buy_perf_price[0])):
                                val = self.SETUP_IS_PERFECTED
                            else:
                                val = self.SETUP_IS_DEFERRED
                        elif not na(self.setup_sell_perf_mask[-1]):
                            if self.data_low[0] >= self.setup_sell_perf_price[0]:
                                val = self.SETUP_IS_PERFECTED
                            else:
                                val = self.setup_buy_perf_mask[-1]
                        if val is not None:
                            self.setup_buy_perf_mask[0] = val
            
                    if self.setup_sell_perf_mask[0] == self.SETUP_IS_PERFECTED:
                        self.setup_sell_perf[0] = self.source[0]
                    if self.setup_buy_perf_mask[0] == self.SETUP_IS_PERFECTED:
                        self.setup_buy_perf[0] = self.source[0]
                    if self.setup_sell[0]:
                        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[0] = min(
                                self.data_low.get(size=extend))
                        else:
                            self.setup_trend_support[0] = min(
                                self.data_low.get(size=self.p.setup_bars))
                    else:
                        self.setup_trend_support[0] = nz(self.setup_trend_support[-1])
            
                    if self.setup_buy[0]:
                        if self.p.setup_trend_extend and self.setup_buy_count[-1] > 0:
                            extend = int((
                                ((self.setup_buy_count[-1]
                                  - (self.setup_buy_count[-1] % self.p.setup_bars))
                                 / self.p.setup_bars)
                                + 1) * self.p.setup_bars)
                            self.setup_trend_resistance[0] = max(
                                self.data_high.get(size=extend))
                        else:
                            self.setup_trend_resistance[0] = max(
                                self.data_high.get(size=self.p.setup_bars))
                    else:
                        self.setup_trend_resistance[0] = nz(self.setup_trend_resistance[-1])
            
                    if self.setup_count_up[0] == (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[0] = (
                            valuewhen(
                                (setupCountUp == (2 * self.p.setup_bars)),
                                priceSource,
                                0)
                        )
                    if self.setup_count_down[0] == (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[0] = (
                            valuewhen(
                                (setupCountDown == (2 * self.p.setup_bars)),
                                priceSource,
                                0)
                        )
            
                    if na(self.countdown_price_up[0]):
                        self.countdown_count_up[0] = self.countdown_count_up[-1]
                    else:
                        val = None
                        if not (
                                (not na(self.setup_buy[0])
                                    or (self.source[0] < self.setup_trend_support[0]))
                                or (not na(self.countdown_count_up_recycle[0]))):
                            if not na(self.setup_sell[0]):
                                if self.countdown_price_up[0]:
                                    val = 1
                                else:
                                    val = 0
                            elif not na(self.countdown_count_up[-1]):
                                if self.countdown_price_up[0]:
                                    val = self.countdown_count_up[-1] + 1
                                else:
                                    val = self.countdown_count_up[-1]
                        if val is not None:
                            self.countdown_count_up[0] = val
                    if na(self.countdown_price_down[0]):
                        self.countdown_count_down[0] = self.countdown_count_down[-1]
                    else:
                        val = None
                        if not (
                                (not na(self.setup_sell[0])
                                    or (self.source[0] > self.setup_trend_resistance[0]))
                                or (not na(self.countdown_count_down_recycle[0]))):
                            if not na(self.setup_buy[0]):
                                if self.countdown_price_down[0]:
                                    val = 1
                                else:
                                    val = 0
                            elif not na(self.countdown_count_down[-1]):
                                if self.countdown_price_down[0]:
                                    val = self.countdown_count_down[-1] + 1
                                else:
                                    val = self.countdown_count_down[-1]
                        if val is not None:
                            self.countdown_count_down[0] = val
            
                    if self.countdown_price_up[0]:
                        self.countdown_count_up_imp[0] = self.countdown_count_up[0]
                    if self.countdown_price_down[0]:
                        self.countdown_count_down_imp[0] = self.countdown_count_down[0]
                
                    if self.p.countdown_qual_bar < self.p.countdown_bars:
            
                        if self.countdown_count_up_imp[0] == 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[0] = valuewhen(
                                cntdwnCountUpImp == self.p.countdown_qual_bar,
                                priceSource,
                                0)
                        else:
                            self.countdown_sell_qual_price[0] = self.countdown_sell_qual_price[-1]
            
                        if self.countdown_count_down_imp[0] == self.p.countdown_qual_bar:
                            cntdwnCountDownImp = line2arr(
                                self.countdown_count_down_imp,
                                self.DATA_CANDLES)
                            priceSource = line2arr(
                                self.source,
                                self.DATA_CANDLES)
                            self.countdown_buy_qual_price[0] = valuewhen(
                                cntdwnCountDownImp == self.p.countdown_qual_bar,
                                priceSource,
                                0)
                        else:
                            self.countdown_buy_qual_price[0] = self.countdown_buy_qual_price[-1]
            
                        if not ((nz(self.countdown_sell_qual_mask[-1])
                                 >= 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[0] == self.p.countdown_bars:
                                if (valuewhen(
                                        cntdwnCountUpImp == self.p.countdown_bars,
                                        high,
                                        0)) >= self.countdown_sell_qual_price[0]:
                                    val = self.COUNTDOWN_IS_QUALIFIED
                                else:
                                    val = self.COUNTDOWN_IS_DEFERRED
                            elif not na(self.countdown_sell_qual_mask[-1]):
                                if self.countdown_count_up_imp[0] > self.p.countdown_bars:
                                    if (valuewhen(
                                            cntdwnCountUpImp > self.p.countdown_bars,
                                            high,
                                            0)) >= self.countdown_sell_qual_price[0]:
                                        val = self.COUNTDOWN_IS_QUALIFIED
                                    else:
                                        val = self.countdown_sell_qual_mask[-1]
                                else:
                                    val = self.countdown_sell_qual_mask[-1]
                            if val is not None:
                                self.countdown_sell_qual_mask[0] = val
            
                        if not ((nz(self.countdown_buy_qual_mask[-1])
                                 >= 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[0] == self.p.countdown_bars:
                                if (valuewhen(
                                        cntdwnCountDownImp == self.p.countdown_bars,
                                        low,
                                        0)) <= self.countdown_buy_qual_price[0]:
                                    val = self.COUNTDOWN_IS_QUALIFIED
                                else:
                                    val = self.COUNTDOWN_IS_DEFERRED
                            elif not na(self.countdown_buy_qual_mask[-1]):
                                if self.countdown_count_down_imp[0] > self.p.countdown_bars:
                                    if (valuewhen(
                                            cntdwnCountDownImp > self.p.countdown_bars,
                                            low,
                                            0)) <= self.countdown_buy_qual_price[0]:
                                        val = self.COUNTDOWN_IS_QUALIFIED
                                    else:
                                        val = self.countdown_buy_qual_mask[-1]
                                else:
                                    val = self.countdown_buy_qual_mask[-1]
                            if val is not None:
                                self.countdown_buy_qual_mask[0] = val
                    else:
                        if self.countdown_count_up[0] == self.p.countdown_bars:
                            self.countdown_sell_qual_mask[0] = self.COUNTDOWN_IS_QUALIFIED
                        if self.countdown_count_down[0] == self.p.countdown_bars:
                            self.countdown_buy_qual_mask[0] = self.COUNTDOWN_IS_QUALIFED
            
                    if self.countdown_count_up_imp[0]:
                        self.countdown_sell_qual_mask_imp[0] = self.countdown_sell_qual_mask[0]
                    if self.countdown_count_down_imp[0]:
                        self.countdown_buy_qual_mask_imp[0] = self.countdown_buy_qual_mask[0]
                    if self.countdown_sell_qual_mask_imp[0] == self.COUNTDOWN_IS_QUALIFIED:
                        cntdwnSellQualMaskImp = line2arr(
                            self.countdown_sell_qual_mask_imp, self.DATA_CANDLES)
                        priceSource = line2arr(
                            self.source, self.DATA_CANDLES)
                        self.countdown_sell[0] = valuewhen(
                            cntdwnSellQualMaskImp == self.COUNTDOWN_IS_QUALIFIED,
                            priceSource,
                            0)
                    if self.countdown_sell_qual_mask_imp[0] == self.COUNTDOWN_IS_DEFERRED:
                        cntdwnSellQualMaskImp = line2arr(
                            self.countdown_sell_qual_mask_imp, self.DATA_CANDLES)
                        priceSource = line2arr(
                            self.source, self.DATA_CANDLES)
                        self.countdown_sell_defer[0] = valuewhen(
                            cntdwnSellQualMaskImp == self.COUNTDOWN_IS_DEFERRED,
                            priceSource,
                            0)
                
                    if self.countdown_buy_qual_mask_imp[0] == self.COUNTDOWN_IS_QUALIFIED:
                        cntdwnBuyQualMaskImp = line2arr(
                            self.countdown_buy_qual_mask_imp, self.DATA_CANDLES)
                        priceSource = line2arr(
                            self.source, self.DATA_CANDLES)
                        self.countdown_buy[0] = valuewhen(
                            cntdwnBuyQualMaskImp == self.COUNTDOWN_IS_QUALIFIED,
                            priceSource,
                            0)
                    if self.countdown_buy_qual_mask_imp[0] == self.COUNTDOWN_IS_DEFERRED:
                        cntdwnBuyQualMaskImp = line2arr(
                            self.countdown_buy_qual_mask_imp, self.DATA_CANDLES)
                        priceSource = line2arr(
                            self.source, self.DATA_CANDLES)
                        self.countdown_buy_defer[0] = valuewhen(
                            cntdwnBuyQualMaskImp == self.COUNTDOWN_IS_DEFERRED,
                            priceSource,
                            0)
            
                    if self.setup_sell[0] or self.countdown_count_up_recycle[0]:
                        high = line2arr(self.data_high, self.p.setup_bars)
                        tr = line2arr(self.tr, self.p.setup_bars)
                        highest = max(high)
                        self.risk_level[0] = highest + valuewhen(high == highest, tr, 0)
                    elif self.setup_buy[0] or self.countdown_count_down_recycle[0]:
                        low = line2arr(self.data_low, self.p.setup_bars)
                        tr = line2arr(self.tr, self.p.setup_bars)
                        lowest = min(low)
                        self.risk_level[0] = lowest - valuewhen(low == lowest, tr, 0)
                    elif self.countdown_sell[0]:
                        high = line2arr(self.data_high, self.p.countdown_bars)
                        tr = line2arr(self.tr, self.p.countdown_bars)
                        highest = max(high)
                        self.risk_level[0] = highest + valuewhen(high == highest, tr, 0)
                    elif self.countdown_buy[0]:
                        low = line2arr(self.data_low, self.p.countdown_bars)
                        tr = line2arr(self.tr, self.p.countdown_bars)
                        lowest = min(low)
                        self.risk_level[0] = lowest - valuewhen(low == lowest, tr, 0)
                    else:
                        self.risk_level[0] = nz(self.risk_level[-1], self.data_low[0])
            
            
                    # setup_sell
                    self.l.setup_sell[0] = 0
                    if not na(self.setup_sell[0]):
                        self.l.setup_sell[0] = 1
                    # setup_buy
                    self.l.setup_buy[0] = 0
                    if not na(self.setup_buy[0]):
                        self.l.setup_buy[0] = 1
                    # setup_sell_perf
                    self.l.setup_sell_perf[0] = 0
                    if not na(self.setup_sell_perf[0]):
                        self.l.setup_sell_perf[0] = 1
                    # setup_buy_perf
                    self.l.setup_buy_perf[0] = 0
                    if not na(self.setup_buy_perf[0]):
                        self.l.setup_buy_perf[0] = 1
                    # countdown_sell
                    self.l.countdown_sell[0] = 0
                    if not na(self.countdown_sell[0]):
                        self.l.countdown_sell[0] = 1
                    # countdown_buy
                    self.l.countdown_buy[0] = 0
                    if not na(self.countdown_buy[0]):
                        self.l.countdown_buy[0] = 1
                    # countdown_sell_defer
                    self.l.countdown_sell_defer[0] = 0
                    if not na(self.countdown_sell_defer[0]):
                        self.l.countdown_sell_defer[0] = 1
                    # countdown_buy_defer
                    self.l.countdown_buy_defer[0] = 0
                    if not na(self.countdown_buy_defer[0]):
                        self.l.countdown_buy_defer[0] = 1
                    # countdown_count_up_recycle
                    self.l.countdown_count_up_recycle[0] = 0
                    if not na(self.countdown_count_up_recycle[0]):
                        self.l.countdown_count_up_recycle[0] = 1
                    # countdown_count_down_recycle
                    self.l.countdown_count_down_recycle[0] = 0
                    if not na(self.countdown_count_down_recycle[0]):
                        self.l.countdown_count_down_recycle[0] = 1
            
            1 Reply Last reply Reply Quote 0
            • D
              dasch last edited by

              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_buy',                # setup buy marker
                      's_sell',               # setup sell marker
                      's_buy_perf',           # setup buy perf markerr
                      's_sell_perf',          # setup sell perf marker
                      's_buy_perf_price',
                      's_sell_perf_price',
                      's_trend_support',
                      's_trend_resistance',
                      'cd_seq_up',            # countdown up marker
                      'cd_seq_down',          # countdown down marker
                      'cd_buy',               # countdown buy marker
                      'cd_sell',              # countdown sell marker
                      'cd_buy_defer',         # countdown buy defer 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='',),
                      s_buy=dict(
                          marker='2', markersize=5.0, color='green',),
                      s_sell=dict(
                          marker='1', markersize=5.0, color='maroon',),
                      s_buy_perf=dict(
                          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='',),
                      cd_buy=dict(
                          marker='^', markersize=10.0, color='green',),
                      cd_sell=dict(
                          marker='v', markersize=10.0, color='maroon',),
                      cd_buy_defer=dict(
                          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
                          if self.td_seq.l.setup_buy[0]:
                              self.l.s_buy[0] = self.data_low[0] - (3 * self.p.bardist)
                          if self.td_seq.l.setup_sell[0]:
                              self.l.s_sell[0] = self.data_high[0] + (3 * self.p.bardist)
                          # setup perfected buy / sell signal
                          if self.td_seq.l.setup_buy_perf[0]:
                              self.l.s_buy_perf[0] = self.data_low[0] - (4 * self.p.bardist)
                          if self.td_seq.l.setup_sell_perf[0]:
                              self.l.s_sell_perf[0] = self.data_high[0] + (4 * self.p.bardist)
              
                      # setup count
                      if self.p.plot_setup_count:
                          # setup count up sequence
                          if self.td_seq.setup_count_up[0] > 0:
                              self.l.seq_up[0] = self.data_low[0] + self.p.bardist
                          # setup count down sequence
                          if self.td_seq.setup_count_down[0] > 0:
                              self.l.seq_down[0] = self.data_high[0] - self.p.bardist
              
                      # setup trend support / resistance (TDST)
                      if self.p.plot_setup_tdst:
                          self.l.s_trend_support[0] = self.td_seq.setup_trend_support[0]
                          self.l.s_trend_resistance[0] = self.td_seq.setup_trend_resistance[0]
              
                      # countdown count
                      if self.p.plot_countdown_count:
                          # countdown count up sequence
                          if self.td_seq.countdown_count_up_imp[0] >= 0:
                              if self.td_seq.countdown_count_up_imp[0] != self.countdown_up_prev:
                                  self.l.cd_seq_up[0] = self.data_low[0] + (2 * self.p.bardist)
                              self.countdown_up_prev = self.td_seq.countdown_count_up_imp[0]
                          # countdown count down sequence
                          if self.td_seq.countdown_count_down_imp[0] >= 0:
                              if self.td_seq.countdown_count_down_imp[0] != self.countdown_down_prev:
                                  self.l.cd_seq_down[0] = self.data_high[0] - (2 * self.p.bardist)
                              self.countdown_down_prev = self.td_seq.countdown_count_down_imp[0]
              
                      # countdown signals
                      if self.p.plot_countdown_signals:
                          # countdown_buy/sell
                          if self.td_seq.l.countdown_buy[0]:
                              self.l.cd_buy[0] = self.data_low[0] - (4 * self.p.bardist)
                          if self.td_seq.l.countdown_sell[0]:
                              self.l.cd_sell[0] = self.data_high[0] + (4 * self.p.bardist)
              
                          # countdown_buy_defer/sell_defer
                          if self.td_seq.l.countdown_buy_defer[0]:
                              self.l.cd_buy_defer[0] = self.data_low[0] - (3 * self.p.bardist)
                          if self.td_seq.l.countdown_sell_defer[0]:
                              self.l.cd_sell_defer[0] = self.data_high[0] + (3 * self.p.bardist)
              
                          # countdown_up_recycle/down_recycle
                          if self.td_seq.l.countdown_count_up_recycle[0]:
                              self.l.cd_cnt_up_recycle[0] = self.data_low[0] + (3 * self.p.bardist)
                          if self.td_seq.l.countdown_count_down_recycle[0]:
                              self.l.cd_cnt_down_recycle[0] = self.data_high[0] - (3 * self.p.bardist)
              
                      # risk level
                      if self.p.plot_risk_level:
                          self.l.risk_level[0] = self.td_seq.risk_level[0]
              
              1 Reply Last reply Reply Quote 0
              • D
                dasch last edited by

                strategy.py

                class StrategyTDSequential(bt.Strategy):
                
                    def __init__(self):
                        '''Initialization '''
                        self.td_seq = TDSequentialInd()
                        self._addobserver(
                            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)
                    
                
                1 Reply Last reply Reply Quote 0
                • D
                  dasch last edited by

                  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.

                  Wayne Filkins 1 Reply Last reply Reply Quote 0
                  • Wayne Filkins
                    Wayne Filkins last edited by

                    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[0] 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[0] 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.

                    D 1 Reply Last reply Reply Quote 0
                    • D
                      dasch @Wayne Filkins last edited by

                      @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.

                      Wayne Filkins 3 Replies Last reply Reply Quote 1
                      • Wayne Filkins
                        Wayne Filkins last edited by

                        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!

                        1 Reply Last reply Reply Quote 0
                        • Wayne Filkins
                          Wayne Filkins @dasch last edited by

                          @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!

                          1 Reply Last reply Reply Quote 0
                          • Wayne Filkins
                            Wayne Filkins @dasch last edited by

                            @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?

                            1 Reply Last reply Reply Quote 0
                            • Wayne Filkins
                              Wayne Filkins @dasch last edited by

                              @dasch NM I got it figured out! base didn't have [0] 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?

                              1 Reply Last reply Reply Quote 0
                              • Wayne Filkins
                                Wayne Filkins @dasch last edited by

                                @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

                                1 Reply Last reply Reply Quote 0
                                • D
                                  dasch last edited by

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

                                  1 Reply Last reply Reply Quote 0
                                  • 1 / 1
                                  • First post
                                    Last post
                                  Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                                  $(document).ready(function () { app.coldLoad(); }); }