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/

    Plotting text over bars

    Indicators/Strategies/Analyzers
    4
    6
    1530
    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.
    • M
      Marek Ozana last edited by

      Hi @backtrader
      I discovered Backtrader only recently, and it is great! It already makes impact in my work! Thanks!
      Newbie question: how can one create indicator which plots indicator line as a text above bars?
      For illustration see Sequential indicator:
      https://www.mql5.com/en/market/product/25007
      Thank you for your time and help
      /Marek

      1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators last edited by

        No it cannot be done. backtrader was not conceived as a charting/plotting platform and such things would require metadata and metaactions to indicate that it is text what you want to plot (text which obviously varies)

        1 Reply Last reply Reply Quote 0
        • N
          nooby_mcnoob last edited by

          I am also missing this functionality from Trading View. Is there anything out there that allows you to do this? I suppose we would need to write a generic charter based on d3. The backtrader_plotting package shows how we could do this.

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

            You can do it this way:

            # -*- coding: utf-8 -*-
            
            from __future__ import (absolute_import, division, print_function,
                                    unicode_literals)
            
            # Import the backtrader platform
            import backtrader as bt
            import backtrader.indicators as btind
            from backtrader.observer import Observer as btobserver
            
            import datetime
            import time
            
            '''
            TD Sequential-Style indicator
            
            // S = Sell Signal
            // B = Buy Signal
            // A = Sell (above bar) or Buy (below bar) Signal that is 'not ideal' - meaning the signal bar's or the previous bar's high or low is not higer or lower than one of the previous 2 bars.
            // - This could indicate that waiting another bar will deliver a 'true' signal. (Useful at the beginning of the trend reversal signal, not so much at the end)
            //
            // Indicator and Rules based on indicator as seen on CoinSheet.org
            
            
                Ein TD Sequential Buy Setup ist erfüllt, wenn 9 aufeinanderfolgende Bars tiefer Schliessen als 4 Bars zuvor. Für den Sell ist es umgekehrt, dort müssen 9 aufeinanderfolgende Bars höher Schliessen als 4 Bars zuvor.
            
                Das Ziel der Zählung ist es, mögliche Wendepunkte anzuzeigen und somit Low Risk Entrys zu finden. Falls die Bedingungen nicht bis zum 9er Count erfüllt wird, ist das Setup gecancelt. Sobald ein TD Setup erfüllt ist, würde normalerweise die Countdown resp. Intersection-Phase beginnen. Beim Countdown werden danach 13 Bars die einen tiefern Close haben, als das Low des zwei Bars zurückliegenden Stabes. Die Countdown Bars müssen jedoch nicht aufeinanderfolgen, die Zählung läuft weiter bis zu Bar 13, sofern nicht das Hoch des vorangehenden TD Setups (bei einem Buy Setup...sonst natürlich das Tief bei einem Sell) überschritten wird.
            
                Das ist jetzt wirklich nur ein ganz grober Überblick der Standartparameter.???
            
                Beim angehängten Indi handelt es sich übrigens um den TD Combo und nicht den TD Sequential. Der Unterschied zwischen den beiden liegt einfach gesagt darin, dass die Countdown Zählung beim erreichen eines kompletten TD Setups, die Countdownzählung zürückrechnet und somit schon beim ersten Bar des Setups beginnen kann, anstatt erst nach der Beendigung des Setups.:P
            
                Grob gesagt kann beim erfüllen eines Setups mit einem kleinen Wendepunkt gerechnet werden und beim erfüllen eines Countdowns ein grosser Wendepunkt.
                
                Entry:
                There are three methods of entry:
                (a) Enter on the close of the day on which the countdown is completed;
                (b) Long Trades: Enter on the close if Close[i] > Close[i - 4];
                    Short Trades: Enter on the close if Close[i] < Close[i - 4];
                (c) Long Trades: Enter on the close if Close[i] > High[i - 2];
                    Short Trades: Enter on the close if Close[i] < Low[i - 2]. Index: i ~ Current Bar. In this test we apply the 2nd method.
                
                Exit:
                Pattern Exit: The current position is liquidated once the new setup is complete and price fails to exceed the furthest price recorded by the recent inactive setup.
                Stop Loss Exit: 
                Long Trades: The difference between the close at the entry bar and the low of the lowest day of the pattern is subtracted from the low of the lowest day. Short Trades: The difference between the high of the highest day of the pattern and the close at the entry bar is added to the high of the highest day.                
                Note: In order to activate a stop loss, the close must exceed the calculated levels.
            
            '''
            
            class strategy(bt.Strategy):
                params = dict(
                  candles_past_to_compare = 4,
                  stoploss_dist = 0.0,
                )
            
                ''' Logging function for strategy '''
                def log(self, txt, dt=None):
                    dt = dt or self.dataprimary.datetime.datetime(0)
                    print('%s, %s' % (dt.isoformat(), txt))
                
                
                '''Initialization '''    
                def __init__(self):
                    # Keep a reference to the primary data feed
                    self.dataprimary = self.datas[0]
                    # Keep a reference to the "close" line in the primary dataseries
                    self.dataclose = self.dataprimary.close
                    # Control vars
                    self.live = True
                    self.order = None
            
                    self.bearishFlip = False
                    self.bullishFlip = False
            
                    self.tdsl = 0 # TD sequence long
                    self.tdss = 0 # TD sequence short
                    self.buySetup = False # TD buy setup 
                    self.sellSetup = False # TD sell setup
                    self.buyCountdown = 0 # TD buy countdown
                    self.sellCountdown = 0 # TD sell countdown
                    self.buyVal = 0 # buy value at intersection
                    self.sellVal = 0 # sell value at intersection
                    
                    self.buySig = False
                    self.idealBuySig = False
                    self.sellSig = False
                    self.idealSellSig = False
            
                    print('-' * 80)
                    print('Strategy Created')
                    print('-' * 80)
            
            
                ''' Data notification '''
                def notify_data(self, data, status, *args, **kwargs):
                    print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
                    if status == data.LIVE:
                        self.live = True
                    elif status == data.DELAYED:
                        self.live = False
            
            
                ''' Store notification '''
                def notify_store(self, msg, *args, **kwargs):
                    print('*' * 5, 'STORE NOTIF:', msg)
                    
            
                ''' Order notification '''
                def notify_order(self, order):
                    if order.status in [order.Completed, order.Cancelled, order.Rejected]:
                        self.order = None
                    print('-' * 50, 'ORDER BEGIN', datetime.datetime.now())
                    print(order)
                    print('-' * 50, 'ORDER END')
            
            
                ''' Trade notification '''
                def notify_trade(self, trade):
                    print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
                    print(trade)
                    print('-' * 50, 'TRADE END')
                    
                    if not trade.isclosed:
                        return
            
                    self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                        (trade.pnl, trade.pnlcomm))
            
                ''' Next '''        
                def next(self):
                    self.buySig = False
                    self.sellSig = False
                    self.idealBuySig = False
                    self.idealSellSig = False
                    
                    # Calculate td sequential values if enough bars
                    if len(self.dataclose) > self.p.candles_past_to_compare:
                        
                        # begin of sequence, bullish or bearish flip
                        if self.dataclose[0] < self.dataclose[-self.p.candles_past_to_compare] and self.dataclose[-1] > self.dataclose[-(self.p.candles_past_to_compare + 1)]:
                            self.bearishFlip = True
                            self.bullishFlip = False
                            self.tdsl = 0
                            self.tdss = 0
                            self.sellSetup = False
                            self.sellCountdown = 0
                        elif self.dataclose[0] > self.dataclose[-self.p.candles_past_to_compare] and self.dataclose[-1] < self.dataclose[-(self.p.candles_past_to_compare + 1)]:
                            self.bullishFlip = True
                            self.bearishFlip = False
                            self.tdss = 0
                            self.tdsl = 0
                            self.buySetup = False
                            self.buyCountdown = 0
            
                        # sequence start
                        if self.dataclose[0] < self.dataclose[-self.p.candles_past_to_compare] and self.bearishFlip:
                            self.tdsl += 1
                        elif self.dataclose[0] > self.dataclose[-self.p.candles_past_to_compare] and self.bullishFlip:
                            self.tdss += 1
                        
                        # sequence intersection
                        if self.tdsl == 9:
                            self.buySig = (self.dataprimary.low[0] < self.dataprimary.low[-3] and self.dataprimary.low[0] < self.dataprimary.low[-2]) or (self.dataprimary.low[-1] < self.dataprimary.low[-2] and self.dataprimary.low[-1] < self.dataprimary.low[-3])
                            self.bearishFlip = False
                            self.tdsl = 0
                            self.buySetup = True
                            self.buyCountdown = 0
                        elif self.tdss == 9:
                            self.sellSig = (self.dataprimary.high[0] > self.dataprimary.high[-2] and self.dataprimary.high[0] > self.dataprimary.high[-3]) or (self.dataprimary.high[-1] > self.dataprimary.high[-3] and self.dataprimary.high[-1] > self.dataprimary.high[-2])
                            self.bullishFlip = False
                            self.tdss = 0
                            self.sellSetup = True
                            self.sellCountdown = 0
                            
                        # sequence countdown
                        if self.buySetup:
                            if self.dataprimary.close[0] <= self.dataprimary.low[-2]:
                                self.buyCountdown += 1
                            if self.buyCountdown == 8:
                                self.buyVal = self.dataclose[0]
                            elif self.buyCountdown == 13:
                                if self.dataprimary.low[0] <= self.buyVal:
                                    self.idealBuySig = True
                                self.buySetup = False
                                self.buyCountdown = 0
                                
                        elif self.sellSetup:
                            if self.dataprimary.close[0] >= self.dataprimary.high[-2]:
                                self.sellCountdown += 1
                            if self.sellCountdown == 8:
                                self.sellVal = self.dataclose[0]
                            elif self.sellCountdown == 13:
                                if self.dataprimary.high[0] >= self.sellVal:
                                    self.idealSellSig = True
                                self.sellSetup = False
                                self.sellCountdown = 0
                                
                    # Trade only if no pending order and live data
                    if not self.order and self.live:
                        if not self.position:
                            if self.buySig:
                                if self.p.stoploss_dist > 0:
                                    stoplossPrice = self.dataprimary.close[0] - self.p.stoploss_dist
                                    self.buy_bracket(stopprice=stoplossPrice, exectype=bt.Order.Market)
                                else:
                                    self.buy()
                            elif self.sellSig:
                                if self.p.stoploss_dist > 0:
                                    stoplossPrice = self.dataprimary.close[0] + self.p.stoploss_dist
                                    self.sell_bracket(stopprice=stoplossPrice, exectype=bt.Order.Market)
                                else:
                                    self.sell()
                        else:
                            if self.position.size > 0 and (self.sellSig or self.idealSellSig):
                                self.close()
                            elif self.position.size < 0 and (self.buySig or self.idealBuySig):
                                self.close()
                                
                    # Log the closing price of the series from the reference
                    self.log("Close {} TD SeqShort {} TD SeqLong {} TD CDBuy {} TD CDSell {}".format(self.dataclose[0], self.tdss, self.tdsl, self.buyCountdown, self.sellCountdown))
            
            class observer(btobserver):
                plotinfo = dict(plot=True, subplot=False, plotlegend=False, 
                                plotlinelegend=False, plotlinevalues=False, 
                                plotabove=True, plotname="TD Sequential",)
                lines = ('buy', 'buy_ideal', 'sell', 'sell_ideal', 'seq_up_1', 
                        'seq_up_2', 'seq_up_3', 'seq_up_4', 'seq_up_5', 
                        'seq_up_6', 'seq_up_7', 'seq_up_8', 'seq_up_9', 
                        'seq_up_1_b', 'seq_up_2_b', 'seq_up_3_b', 'seq_up_4_b', 
                        'seq_up_5_b', 'seq_up_6_b', 'seq_up_7_b', 'seq_up_8_b', 
                        'seq_up_9_b', 'seq_up_10_b', 'seq_up_11_b', 'seq_up_12_b', 
                        'seq_down_1', 'seq_down_2', 'seq_down_3', 'seq_down_4', 
                        'seq_down_5', 'seq_down_6', 'seq_down_7', 'seq_down_8', 
                        'seq_down_9',
                        'seq_down_1_s', 'seq_down_2_s', 'seq_down_3_s', 'seq_down_4_s', 
                        'seq_down_5_s', 'seq_down_6_s', 'seq_down_7_s', 'seq_down_8_s', 
                        'seq_down_9_s', 'seq_down_10_s','seq_down_11_s', 'seq_down_12_s',)
                        
                plotlines = dict(
                    buy_ideal = dict(marker='^', markersize=10.0, color='green',),
                    buy = dict(marker='2', markersize=10.0, color='green',),
                    sell_ideal = dict(marker='v', markersize=10.0, color='maroon',),
                    sell = dict(marker='1', markersize=10.0, color='maroon',),
                    seq_up_1 = dict(marker='$1$', markersize=6.0, color='black', ls='',),
                    seq_up_2 = dict(marker='$2$', markersize=6.0, color='black', ls='',),
                    seq_up_3 = dict(marker='$3$', markersize=6.0, color='black', ls='',),
                    seq_up_4 = dict(marker='$4$', markersize=6.0, color='black', ls='',),
                    seq_up_5 = dict(marker='$5$', markersize=6.0, color='black', ls='',),
                    seq_up_6 = dict(marker='$6$', markersize=6.0, color='black', ls='',),
                    seq_up_7 = dict(marker='$7$', markersize=6.0, color='black', ls='',),
                    seq_up_8 = dict(marker='$8$', markersize=6.0, color='black', ls='',),
                    seq_up_9 = dict(marker='$9$', markersize=6.0, color='black', ls='',),
                    seq_up_1_b = dict(marker='$1$', markersize=6.0, color='blue', ls='',),
                    seq_up_2_b = dict(marker='$2$', markersize=6.0, color='blue', ls='',),
                    seq_up_3_b = dict(marker='$3$', markersize=6.0, color='blue', ls='',),
                    seq_up_4_b = dict(marker='$4$', markersize=6.0, color='blue', ls='',),
                    seq_up_5_b = dict(marker='$5$', markersize=6.0, color='blue', ls='',),
                    seq_up_6_b = dict(marker='$6$', markersize=6.0, color='blue', ls='',),
                    seq_up_7_b = dict(marker='$7$', markersize=6.0, color='blue', ls='',),
                    seq_up_8_b = dict(marker='$8$', markersize=6.0, color='blue', ls='',),
                    seq_up_9_b = dict(marker='$9$', markersize=6.0, color='blue', ls='',),
                    seq_up_10_b = dict(marker='$10$', markersize=6.0, color='blue', ls='',),
                    seq_up_11_b = dict(marker='$11$', markersize=6.0, color='blue', ls='',),
                    seq_up_12_b = dict(marker='$12$', markersize=6.0, color='blue', ls='',),
                    seq_down_1 = dict(marker='$1$', markersize=6.0, color='black', ls='',),
                    seq_down_2 = dict(marker='$2$', markersize=6.0, color='black', ls='',),
                    seq_down_3 = dict(marker='$3$', markersize=6.0, color='black', ls='',),
                    seq_down_4 = dict(marker='$4$', markersize=6.0, color='black', ls='',),
                    seq_down_5 = dict(marker='$5$', markersize=6.0, color='black', ls='',),
                    seq_down_6 = dict(marker='$6$', markersize=6.0, color='black', ls='',),
                    seq_down_7 = dict(marker='$7$', markersize=6.0, color='black', ls='',),
                    seq_down_8 = dict(marker='$8$', markersize=6.0, color='black', ls='',),
                    seq_down_9 = dict(marker='$9$', markersize=6.0, color='black', ls='',),
                    seq_down_1_s = dict(marker='$1$', markersize=6.0, color='blue', ls='',),
                    seq_down_2_s = dict(marker='$2$', markersize=6.0, color='blue', ls='',),
                    seq_down_3_s = dict(marker='$3$', markersize=6.0, color='blue', ls='',),
                    seq_down_4_s = dict(marker='$4$', markersize=6.0, color='blue', ls='',),
                    seq_down_5_s = dict(marker='$5$', markersize=6.0, color='blue', ls='',),
                    seq_down_6_s = dict(marker='$6$', markersize=6.0, color='blue', ls='',),
                    seq_down_7_s = dict(marker='$7$', markersize=6.0, color='blue', ls='',),
                    seq_down_8_s = dict(marker='$8$', markersize=6.0, color='blue', ls='',),
                    seq_down_9_s = dict(marker='$9$', markersize=6.0, color='blue', ls='',),
                    seq_down_10_s = dict(marker='$10$', markersize=6.0, color='blue', ls='',),
                    seq_down_11_s = dict(marker='$11$', markersize=6.0, color='blue', ls='',),
                    seq_down_12_s = dict(marker='$12$', markersize=6.0, color='blue', ls='',),
                )
                
                
                params = (
                    ('bardist', 0.00005),  
                )
                
                def __init__(self):
                    pass
                    
                def next(self):
                    # begin of sequence for long position
                    if self._owner.tdsl > 0 and self._owner.tdsl < 10:
                        name = 'seq_up_%d' % self._owner.tdsl
                        attr = getattr(self.lines, name, False)
                        attr[0] = self.data_low[0] - self.p.bardist
                    
                    # begin of sequence of short position
                    elif self._owner.tdss > 0 and self._owner.tdss < 10:
                        name = 'seq_down_%d' % self._owner.tdss
                        attr = getattr(self.lines, name, False)
                        attr[0] = self.data_high[0] + self.p.bardist
                        
                    if self._owner.buySetup:
                        if self._owner.buyCountdown > 0 and self._owner.buyCountdown < 13:
                            name = 'seq_up_%d_b' % self._owner.buyCountdown
                            attr = getattr(self.lines, name, False)
                            attr[0] = self.data_low[0] - (2 * self.p.bardist)
                    elif self._owner.sellSetup:
                        if self._owner.sellCountdown > 0 and self._owner.sellCountdown < 13 and self._owner.sellSetup:
                            name = 'seq_down_%d_s' % self._owner.sellCountdown
                            attr = getattr(self.lines, name, False)
                            attr[0] = self.data_high[0] + (2 * self.p.bardist)
                    
                    if self._owner.buySig:
                        self.lines.buy[0] = self.data_low[0] - (2 * self.p.bardist)
                    elif self._owner.sellSig:
                        self.lines.sell[0] = self.data_high[0] + (2 * self.p.bardist)
                    elif self._owner.idealBuySig:
                        self.lines.buy_ideal[0] = self.data_low[0] - (3 * self.p.bardist)
                    elif self._owner.idealSellSig:
                        self.lines.sell_ideal[0] = self.data_high[0] + (3 * self.p.bardist)
                        
            
            

            To plot this, you need to add the observer

            M 1 Reply Last reply Reply Quote 1
            • B
              backtrader administrators last edited by

              Not automated, but it can for sure do the trick

              And good to see that things are getting more multilingual each day (at least I can read and understand this one)

              1 Reply Last reply Reply Quote 0
              • M
                Marek Ozana @dasch last edited by

                Thanks @dasch & @backtrader
                Impressive community!

                1 Reply Last reply Reply Quote 0
                • 1 / 1
                • First post
                  Last post
                Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors