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

Real-Time Plotting



  • Has anyone had success in creating real-time plotting. Cerebro does not appear to be the solution here, however, it appears others have found a way that might leave clues on how to accomplish this.

    A programmer (sendex) has previously created live stock plotting, however, this is back in the day when Yahoo finance was available. Here is his post:
    https://pythonprogramming.net/advanced-matplotlib-graphing-charting-tutorial/

    Does anyone have the skills to alter this code so that it draws data from stores like Interactive Brokers or OANDA, while still being able to leverage Backtrader for analysis?

    FYI - I would replace the def rsiFunc, def movingaverage, def ExpMovingAverage and def computeMACD with the Backtrader, but before commencing this it would be good to know how to leverage IB or OANDA as the data source.

    Can anyone help?

    As far as I can tell the following section needs editing...

    def graphData(stock,MA1,MA2):
    
            #Use this to dynamically pull a stock:
    
        try:
            print('Currently Pulling',stock)
            urlToVisit = 'http://chartapi.finance.yahoo.com/instrument/1.0/'+stock+'/chartdata;type=quote;range=10y/csv'
            stockFile =[]
            try:
                sourceCode = urllib.request.urlopen(urlToVisit).read().decode()
                splitSource = sourceCode.split('\n')
                for eachLine in splitSource:
                    splitLine = eachLine.split(',')
                    if len(splitLine)==6:
                        if 'values' not in eachLine:
                            stockFile.append(eachLine)
            except Exception as e:
                print(str(e), 'failed to organize pulled data.')
        except Exception as e:
            print(str(e), 'failed to pull pricing data')
    
        try:
            date, closep, highp, lowp, openp, volume = np.loadtxt(stockFile,delimiter=',', unpack=True,
                                                                  converters={ 0: bytespdate2num('%Y%m%d')})
        
    

    FULL CODE (BY SENDEX) HERE:

    # THIS VERSION IS FOR PYTHON 3 #
    import urllib.request, urllib.error, urllib.parse
    import time
    import datetime
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.ticker as mticker
    import matplotlib.dates as mdates
    from matplotlib.finance import candlestick_ohlc
    import matplotlib
    import pylab
    matplotlib.rcParams.update({'font.size': 9})
    
    def rsiFunc(prices, n=14):
        deltas = np.diff(prices)
        seed = deltas[:n+1]
        up = seed[seed>=0].sum()/n
        down = -seed[seed<0].sum()/n
        rs = up/down
        rsi = np.zeros_like(prices)
        rsi[:n] = 100. - 100./(1.+rs)
    
        for i in range(n, len(prices)):
            delta = deltas[i-1] # cause the diff is 1 shorter
    
            if delta>0:
                upval = delta
                downval = 0.
            else:
                upval = 0.
                downval = -delta
    
            up = (up*(n-1) + upval)/n
            down = (down*(n-1) + downval)/n
    
            rs = up/down
            rsi[i] = 100. - 100./(1.+rs)
    
        return rsi
    
    def movingaverage(values,window):
        weigths = np.repeat(1.0, window)/window
        smas = np.convolve(values, weigths, 'valid')
        return smas # as a numpy array
    
    
    def ExpMovingAverage(values, window):
        weights = np.exp(np.linspace(-1., 0., window))
        weights /= weights.sum()
        a =  np.convolve(values, weights, mode='full')[:len(values)]
        a[:window] = a[window]
        return a
    
    
    def computeMACD(x, slow=26, fast=12):
        """
        compute the MACD (Moving Average Convergence/Divergence) using a fast and slow exponential moving avg'
        return value is emaslow, emafast, macd which are len(x) arrays
        """
        emaslow = ExpMovingAverage(x, slow)
        emafast = ExpMovingAverage(x, fast)
        return emaslow, emafast, emafast - emaslow
    
    
    def bytespdate2num(fmt, encoding='utf-8'):
        strconverter = mdates.strpdate2num(fmt)
        def bytesconverter(b):
            s = b.decode(encoding)
            return strconverter(s)
        return bytesconverter
    
    def graphData(stock,MA1,MA2):
    
        '''
            Use this to dynamically pull a stock:
        '''
        try:
            print('Currently Pulling',stock)
            urlToVisit = 'http://chartapi.finance.yahoo.com/instrument/1.0/'+stock+'/chartdata;type=quote;range=10y/csv'
            stockFile =[]
            try:
                sourceCode = urllib.request.urlopen(urlToVisit).read().decode()
                splitSource = sourceCode.split('\n')
                for eachLine in splitSource:
                    splitLine = eachLine.split(',')
                    if len(splitLine)==6:
                        if 'values' not in eachLine:
                            stockFile.append(eachLine)
            except Exception as e:
                print(str(e), 'failed to organize pulled data.')
        except Exception as e:
            print(str(e), 'failed to pull pricing data')
    
        try:
            date, closep, highp, lowp, openp, volume = np.loadtxt(stockFile,delimiter=',', unpack=True,
                                                                  converters={ 0: bytespdate2num('%Y%m%d')})
            x = 0
            y = len(date)
            newAr = []
            while x < y:
                appendLine = date[x],openp[x],highp[x],lowp[x],closep[x],volume[x]
                newAr.append(appendLine)
                x+=1
                
            Av1 = movingaverage(closep, MA1)
            Av2 = movingaverage(closep, MA2)
    
            SP = len(date[MA2-1:])
                
            fig = plt.figure(facecolor='#07000d')
    
            ax1 = plt.subplot2grid((6,4), (1,0), rowspan=4, colspan=4, axisbg='#07000d')
            candlestick_ohlc(ax1, newAr[-SP:], width=.6, colorup='#53c156', colordown='#ff1717')
    
            Label1 = str(MA1)+' SMA'
            Label2 = str(MA2)+' SMA'
    
            ax1.plot(date[-SP:],Av1[-SP:],'#e1edf9',label=Label1, linewidth=1.5)
            ax1.plot(date[-SP:],Av2[-SP:],'#4ee6fd',label=Label2, linewidth=1.5)
            
            ax1.grid(True, color='w')
            ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
            ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
            ax1.yaxis.label.set_color("w")
            ax1.spines['bottom'].set_color("#5998ff")
            ax1.spines['top'].set_color("#5998ff")
            ax1.spines['left'].set_color("#5998ff")
            ax1.spines['right'].set_color("#5998ff")
            ax1.tick_params(axis='y', colors='w')
            plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))
            ax1.tick_params(axis='x', colors='w')
            plt.ylabel('Stock price and Volume')
    
            maLeg = plt.legend(loc=9, ncol=2, prop={'size':7},
                       fancybox=True, borderaxespad=0.)
            maLeg.get_frame().set_alpha(0.4)
            textEd = pylab.gca().get_legend().get_texts()
            pylab.setp(textEd[0:5], color = 'w')
    
            volumeMin = 0
            
            ax0 = plt.subplot2grid((6,4), (0,0), sharex=ax1, rowspan=1, colspan=4, axisbg='#07000d')
            rsi = rsiFunc(closep)
            rsiCol = '#c1f9f7'
            posCol = '#386d13'
            negCol = '#8f2020'
            
            ax0.plot(date[-SP:], rsi[-SP:], rsiCol, linewidth=1.5)
            ax0.axhline(70, color=negCol)
            ax0.axhline(30, color=posCol)
            ax0.fill_between(date[-SP:], rsi[-SP:], 70, where=(rsi[-SP:]>=70), facecolor=negCol, edgecolor=negCol, alpha=0.5)
            ax0.fill_between(date[-SP:], rsi[-SP:], 30, where=(rsi[-SP:]<=30), facecolor=posCol, edgecolor=posCol, alpha=0.5)
            ax0.set_yticks([30,70])
            ax0.yaxis.label.set_color("w")
            ax0.spines['bottom'].set_color("#5998ff")
            ax0.spines['top'].set_color("#5998ff")
            ax0.spines['left'].set_color("#5998ff")
            ax0.spines['right'].set_color("#5998ff")
            ax0.tick_params(axis='y', colors='w')
            ax0.tick_params(axis='x', colors='w')
            plt.ylabel('RSI')
    
            ax1v = ax1.twinx()
            ax1v.fill_between(date[-SP:],volumeMin, volume[-SP:], facecolor='#00ffe8', alpha=.4)
            ax1v.axes.yaxis.set_ticklabels([])
            ax1v.grid(False)
            ###Edit this to 3, so it's a bit larger
            ax1v.set_ylim(0, 3*volume.max())
            ax1v.spines['bottom'].set_color("#5998ff")
            ax1v.spines['top'].set_color("#5998ff")
            ax1v.spines['left'].set_color("#5998ff")
            ax1v.spines['right'].set_color("#5998ff")
            ax1v.tick_params(axis='x', colors='w')
            ax1v.tick_params(axis='y', colors='w')
            ax2 = plt.subplot2grid((6,4), (5,0), sharex=ax1, rowspan=1, colspan=4, axisbg='#07000d')
            fillcolor = '#00ffe8'
            nslow = 26
            nfast = 12
            nema = 9
            emaslow, emafast, macd = computeMACD(closep)
            ema9 = ExpMovingAverage(macd, nema)
            ax2.plot(date[-SP:], macd[-SP:], color='#4ee6fd', lw=2)
            ax2.plot(date[-SP:], ema9[-SP:], color='#e1edf9', lw=1)
            ax2.fill_between(date[-SP:], macd[-SP:]-ema9[-SP:], 0, alpha=0.5, facecolor=fillcolor, edgecolor=fillcolor)
    
            plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))
            ax2.spines['bottom'].set_color("#5998ff")
            ax2.spines['top'].set_color("#5998ff")
            ax2.spines['left'].set_color("#5998ff")
            ax2.spines['right'].set_color("#5998ff")
            ax2.tick_params(axis='x', colors='w')
            ax2.tick_params(axis='y', colors='w')
            plt.ylabel('MACD', color='w')
            ax2.yaxis.set_major_locator(mticker.MaxNLocator(nbins=5, prune='upper'))
            for label in ax2.xaxis.get_ticklabels():
                label.set_rotation(45)
    
            plt.suptitle(stock.upper(),color='w')
            plt.setp(ax0.get_xticklabels(), visible=False)
            plt.setp(ax1.get_xticklabels(), visible=False)
            
            ax1.annotate('Big news!',(date[510],Av1[510]),
                xytext=(0.8, 0.9), textcoords='axes fraction',
                arrowprops=dict(facecolor='white', shrink=0.05),
                fontsize=14, color = 'w',
                horizontalalignment='right', verticalalignment='bottom')
    
            plt.subplots_adjust(left=.09, bottom=.14, right=.94, top=.95, wspace=.20, hspace=0)
            plt.show()
            fig.savefig('example.png',facecolor=fig.get_facecolor())
               
        except Exception as e:
            print('main loop',str(e))
    
    while True:
        stock = input('Stock to plot: ')
        graphData(stock,10,50)


  • For reference, here is my runstrategy() module that I want to integrate with 'real-time' plotting. It's a modified/integrated version of ibtest.py and iboandav20.py to suit my needs. Works fine with live and historical.

    How could I place real-time plotting into this module, based on the sample code above, or by other means?

    Thank in advance!!

    #!/usr/local/bin/python
    # -*- coding: utf-8 -*-
    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    #----- IMPORT PACKAGES -----#
    import argparse
    import datetime
    import backtrader as bt
    import backtrader.indicators as btind
    import pytz
    from backtrader import Observer
    from backtrader.utils import flushfile  # win32 quick stdout flushing
    from twilio.rest import Client
    import logging
    from tabulate import tabulate
    import time
    #import numpy as np
    #import matplotlib.pyplot as pyplot
    #----- END -----#
    
    #----- FOLDER DIRECTORY FOR MODULES -----#
    from __modules__.coremodules import __directory__ as directory
    import sys
    sys.path.append(directory.parentfolder)
    #----- END -----#
    
    #----- IMPORT CUSTOM INDICATORS -----#
    from coremodules.start import startcode
    #from indicators.klingerosc import KlingerOSC
    #from indicators.swing import SwingInd
    from args.args import parse_args
    from coremodules.tradeanalysis import TradeAnalysis
    from coremodules.sqn import SQN
    from coremodules.printstrategy import printstrategy
    from observers.buysellplot import BuySellPlot
    from coremodules.plotscheme import PlotScheme
    import actions.sendsms
    
    #----- END -----#
    
    
    #----- OANDA STORE -----#
    args = parse_args()
    
    if args.oanda:
        import btoandav20
        from information.broker_oanda import oandakeys
        
        StoreCls = btoandav20.stores.OandaV20Store
        DataCls = btoandav20.feeds.OandaV20Data
        # BrokerCls = btoandav20.brokers.OandaV20Broker
    
        # available timeframes for oanda
        TIMEFRAMES = [bt.TimeFrame.Names[bt.TimeFrame.Seconds],
                 bt.TimeFrame.Names[bt.TimeFrame.Minutes],
                 bt.TimeFrame.Names[bt.TimeFrame.Days],
                 bt.TimeFrame.Names[bt.TimeFrame.Weeks],
                 bt.TimeFrame.Names[bt.TimeFrame.Months]]
                 
    elif args.ib is not None:
        from information.broker_ib import IBhost
    
        #----- END -----#
    
    def runstrategy():
        args = parse_args()
        
        if args.ib and args.oanda is None:
            print('ERROR: Please select broker: args.oanda or args.ib')
            
    #----- CEREBRO START -----#
        
        if args.optimise:
            cerebro = bt.Cerebro(optreturn=False)
        else:
            cerebro = bt.Cerebro()
        
    #----- PLOT INFO -----#
        
        plotinfo = dict(plot=True,
             subplot=True,
             plotname='',
             plotskip=False,
             plotabove=False,
             plotlinelabels=True,
             plotlinevalues=True,
             plotvaluetags=True,
             plotymargin=0.0,
             plotyhlines=[],
             plotyticks=[],
             plothlines=[],
             plotforce=False,
             plotmaster=None,
             plotylimited=True,
        )
    
    #----- SELECT STRATEGY -----#
    
        if args.strategy == 'reversal':
           from strategies.strategy_reversal import TheStrategy as TheStrategy
        elif args.strategy == '3_bar_stack':
           from strategies.strategy_3_bar_stack import TheStrategy as TheStrategy
        elif args.strategy == 'pivotpoint':
           from strategies.strategy_pivotpoint import TheStrategy as TheStrategy
        elif args.strategy == 'bom':
           from strategies.strategy_bom import TheStrategy as TheStrategy
        elif args.strategy == 'bband':
           from strategies.strategy_bband import TheStrategy as TheStrategy
        elif args.strategy == '2wave':
           from strategies.strategy_secondwave import TheStrategy as TheStrategy
    
    #----- LOAD MODULES -----#
    
        def printTradeAnalysis(analyzer):
            return TradeAnalysis(analyzer)
            
        def printSQN(analyzer):          
            return SQN(analyzer)
    
    #----- STORE & BROKER -----#
    
        if args.ib is not None:
            
            if args.usestore:
                ibstore = bt.stores.IBStore(**IBhost.storekwargs)
    
            if args.broker:
                if args.usestore:
                    broker = ibstore.getbroker()
                else:
                    broker = bt.brokers.IBBroker(**IBhost.storekwargs)
    
                cerebro.setbroker(broker)
                
            else:
                startcash = 1000000
                cerebro.broker.setcash(startcash)
                
        elif args.oanda:
    
            storekwargs = dict(
                token=args.token,
                account=args.account,
                practice=not args.live
            )
    
            if not args.no_store:
                store = StoreCls(**storekwargs)
    
            if args.broker:
                if args.no_store:
                    broker = BrokerCls(**storekwargs)
                else:
                    broker = store.getbroker()
    
                cerebro.setbroker(broker)
                
            else:
                startcash = 1000000
                cerebro.broker.setcash(startcash)
    
    #----- TIMEFRAME AND COMPRESSION -----#
        if args.ib is not None:
        
            timeframe = args.timeframe
            # Manage data1 parameters
            tf1 = args.timeframe1
            #tf1 = bt.TimeFrame.TFrame(tf1) if tf1 is not None else timeframe
            cp1 = args.compression1
            cp1 = cp1 if cp1 is not None else args.compression
            '''
            if args.resample or args.replay:
                datatf = datatf1 = bt.Timeframe.Minutes
                datacomp = datacomp1 = args.compression
            else
                datatf = timeframe
                datacomp = args.compression
                datatf1 = tf1
                datacomp1 = cp1'''
    
            if args.resample or args.replay:
                datatf1 = datatf = args.timeframe
                datacomp1 = datacomp = 1
            else:
                datatf = timeframe
                datacomp = args.compression
                datatf1 = tf1
                datacomp1 = cp1
    
            if not args.historical:
                fromdate = datetime.datetime.now() - datetime.timedelta(days=4)
                todate = None
            else:
                dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
                fromdate = datetime.datetime.strptime(args.fromdate, dtformat)
                if args.now:
                    todate = datetime.datetime.now()
                else:
                    dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.todate))
                    todate = datetime.datetime.strptime(args.todate, dtformat)
    
            if args.now:
                todate = datetime.datetime.now()
        
        elif args.oanda:
            timeframe = bt.TimeFrame.TFrame(args.timeframe)
            # Manage data1 parameters
            tf1 = args.timeframe1
            #tf1 = bt.TimeFrame.TFrame(tf1) if tf1 is not None else timeframe
            cp1 = args.compression1
            cp1 = cp1 if cp1 is not None else args.compression
            
            if args.resample or args.replay:
                datatf = datatf1 = bt.TimeFrame.Ticks
                datacomp = datacomp1 = 1
            else:
                datatf = timeframe
                datacomp = args.compression
                datatf1 = tf1
                datacomp1 = cp1
    
            fromdate = None
            if args.fromdate:
                dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
                fromdate = datetime.datetime.strptime(args.fromdate, dtformat)
    
    #----- STORE AND DATAS - INTERACTIVE BROKERS -----#
    
        if args.ib is not None:
        
            IBDataFactory = ibstore.getdata if args.usestore else bt.feeds.IBData
    
            datakwargs = dict(
                timeframe=datatf,
                compression=datacomp,
                historical=args.historical,
                fromdate=fromdate,
                todate=todate,
                rtbar=args.rtbar,
                qcheck=args.qcheck,
                what=args.what,
                backfill_start=True,
                backfill=True,
                #backfill_start=not args.no_backfill_start,
                #backfill=not args.no_backfill,
                latethrough=args.latethrough,
                tz=args.timezone
            )
    
            if not args.usestore and not args.broker:   # neither store nor broker
                datakwargs.update(IBhost.storekwargs)  # pass the store args over the data
    
    #----- STORE AND DATAS - OANDA -----#
    
        elif args.oanda:
            
            DataFactory = DataCls if args.no_store else store.getdata
    
            datakwargs = dict(
                timeframe=datatf, compression=datacomp,
                qcheck=args.qcheck,
                historical=args.historical,
                fromdate=fromdate,
                bidask=args.bidask,
                useask=args.useask,
                backfill_start=not args.no_backfill_start,
                backfill=not args.no_backfill,
                tz=args.timezone
            )
    
            if args.no_store and not args.broker:   # neither store nor broker
                datakwargs.update(storekwargs)  # pass the store args over the data
    
    
    #--- DATAS - CSV FILE ---#
        
        if args.csv:
            
            datapath = '../data/csv/commodities/XAUUSD/XAUUSD_m1_Ask_2012-2016.csv'
            data0 = bt.feeds.GenericCSVData(
                timeframe=bt.TimeFrame.Minutes,
                compression=1,
                dataname=datapath,
                nullvalue=0.0,
                dtformat=('%m/%d/%Y'),
                tmformat=('%H:%M:%S'),
                fromdate=fromdate,
                todate=todate,
                datetime=0,
                time=1,
                high=3,
                low=4,
                open=2,
                close=5,
                volume=6,
                openinterest=-1 #-1 means not used
                )
    
    #--- DATA 0 - INTERACTIVE BROKERS & OANDA  ---#
        else:
            if args.ib is not None:
                data0 = IBDataFactory(dataname=args.data0, **datakwargs)
            elif args.oanda:
                data0 = DataFactory(dataname=args.data0, **datakwargs)
    
    #--- DATA 1 - BOTH INTERACTIVE BROKERS & OANDA  ---#
    
            data1 = None
            
            if args.data1 is not None:
                if args.data1 != args.data0:
                    datakwargs['timeframe'] = datatf1
                    datakwargs['compression'] = datacomp1
                    data1 = IBDataFactory(dataname=args.data1, **datakwargs)
                else:
                    data1 = data0
    
            rekwargs = dict(
                timeframe=timeframe, compression=args.compression,
                bar2edge=not args.no_bar2edge,
                adjbartime=not args.no_adjbartime,
                rightedge=not args.no_rightedge,
                takelate=not args.no_takelate,
            )
        
    #----- LOAD DATA -----#
        
        if args.replay:
            cerebro.replaydata(data0, **rekwargs)
    
            if data1 is not None:
                rekwargs['timeframe'] = tf1
                rekwargs['compression'] = cp1
                cerebro.replaydata(data1, **rekwargs)
    
        elif args.resample:
            cerebro.resampledata(data0, **rekwargs)
    
            if data1 is not None:
                rekwargs['timeframe'] = tf1
                rekwargs['compression'] = cp1
                cerebro.resampledata(data1, **rekwargs)
    
        else:
            cerebro.adddata(data0)
            if data1 is not None:
                cerebro.adddata(data1)
    
        if args.valid is None:
            valid = None
        else:
            valid = datetime.timedelta(seconds=args.valid)
    
    
        #--- PROCESSING LOAD TIME ---#
        cerebro.dtcerebro = dt0 = datetime.datetime.now()
        print('Cerebro Start Time:          {}'.format(dt0))
        
    #----- CEREBRO RUN - OPTIMISE -----#
    # https://www.backtrader.com/blog/posts/2015-07-23-multicore-optimization/multicore-optimization/
    
        if args.optimise:
        
            #cerebro.addstrategy(TheStrategy, bomperiod_0=100)
            cerebro.optstrategy(TheStrategy, bomperiod_0=(50,100,300))
            #cerebro.adddata(data0)
            # Run over everything
            opt_runs = cerebro.run(maxcpus=1)
            
            # Generate results list
            final_results_list = []
            for run in opt_runs:
                for strategy in run:
                    value = round(strategy.broker.getvalue(),2)
                    pnl = round(value - startcash,2)
                    period = strategy.params.bomperiod_0
                    final_results_list.append([period,pnl])
                            
            #Sort Results List
            by_period = sorted(final_results_list, key=lambda x: x[0])
            by_PnL = sorted(final_results_list, key=lambda x: x[1], reverse=True)
    
            #Print results
            print('')
            print('Results: Ordered by period:')
            for result in by_period:
                print('Period: {}, PnL: {:5,.5f}'.format(result[0], result[1]))
            print('')
            print('Results: Ordered by Profit:')
            for result in by_PnL:
                print('Period: {}, PnL: {:5,.5f}'.format(result[0], result[1]))
            print('')
        
        
    #----- CEREBRO RUN - LIVE -----# ... avoid long data accumulation by switching to "exactbars"
        elif args.historical is None:
        
            if args.ib is not None:
                cerebro.addstrategy(TheStrategy,
                                    trade=args.trade,
                                    exectype=bt.Order.ExecType(args.exectype),
                                    stake=args.stake,
                                    stopafter=args.stopafter,
                                    valid=valid,
                                    cancel=args.cancel,
                                    donotsell=args.donotsell,
                                    stoptrail=args.stoptrail,
                                    stoptraillimit=args.traillimit,
                                    trailamount=args.trailamount,
                                    trailpercent=args.trailpercent,
                                    limitoffset=args.limitoffset,
                                    oca=args.oca,
                                    bracket=args.bracket)
            elif args.oanda:
                cerebro.addstrategy(TheStrategy,
                                    trade=args.trade,
                                    exectype=bt.Order.ExecType(args.exectype),
                                    stake=args.stake,
                                    stopafter=args.stopafter,
                                    valid=valid,
                                    cancel=args.cancel,
                                    donotcounter=args.donotcounter,
                                    sell=args.sell,
                                    usebracket=args.usebracket)
                                    
            cerebro.addobserver(bt.observers.Trades)
            cerebro.addobserver(bt.observers.BuySell)
            cerebro.run(exactbars=args.exactbars)
            
    #----- CEREBRO RUN - HISTORICAL -----#
        
        else:
    
            cerebro.addstrategy(TheStrategy)
            
            if args.writer:
                cerebro.addwriter(bt.WriterFile, csv=True, out=(args.data0 + "-from-" + args.fromdate + "-to-" + args.todate + ".csv"))
            cerebro.addobserver(bt.observers.Trades)
            cerebro.addobserver(bt.observers.BuySell)
            cerebro.addobserver(bt.observers.Value, barplot=True)
            cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')
            cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')
            
            if data1 is not None:
                strategies = cerebro.run(stdstats=False, runonce=False) #runonce=False (for coupling of mixed tiemframes to work).
                firstStrat = strategies[0]
            else:
                strategies = cerebro.run(stdstats=False, runonce=True)
                firstStrat = strategies[0]
            
            #printstrategy()
            printTradeAnalysis(firstStrat.analyzers.ta.get_analysis())
            printSQN(firstStrat.analyzers.sqn.get_analysis())
            print('1.6 – 1.9 Below average, 2.0 – 2.4 Average, 2.5 – 2.9 Good, 3.0 – 5.0 Excellent, 5.1 – 6.9 Superb, 7.0 – Holy Grail')
    
            #portvalue = round(cerebro.broker.getvalue(), 2)
            portvalue = cerebro.broker.getvalue()
            #pnl = round(portvalue - startcash, 2)
            pnl = (portvalue - startcash)
            print('Final Portfolio Value: ${0:,.5f}'.format(portvalue))
            print('P/L: ${0:.5f}'.format(pnl))
            print('{0:.2f}%'.format((pnl / startcash * 100)))
    
            #pyplot.plot(data0) # TESTING DIFFERENT TYPE OF PLOTTING
            
            cerebro.plot(
                #plotter = None,
                #plotname=args.data0,
                #subplot=True,
                #plotlinelabels=True,
                style='candlestick',
                barup='green',
                bardown='red',
                volume=args.volume,
                )
    
    if __name__ == '__main__':
        runstrategy()
    


  • Hello mics,

    a few days ago I started to look into this subject.

    This:

    from matplotlib.finance import candlestick_ohlc
    

    seems to be substituted through:

    import mplfinance as mpf
    

    mplfinance

    matplotlib utilities for the visualization, and visual analysis, of financial data
    https://github.com/matplotlib/mplfinance



  • backtrader_plotting is able to plot live data. I created a new project based on backtrader_plotting with live plotting in mind. You can find it here:

    https://github.com/happydasch/btplotting

    There are a few issues with generating/displaying data but it mostly works.



  • with btplotting you don't need a modified version of backtrader, you start it with this:

    from btplotting import BacktraderPlottingLive
    from btplotting.schemes import Tradimo
    
    # black skin
    cerebro.addanalyzer(BacktraderPlottingLive)
    
    or
    
    # white skin
    cerebro.addanalyzer(BacktraderPlottingLive, scheme=Tradimo())
    

    after this you can access the live plot at http://localhost:80



  • @dasch This is wonderful, thank you so much. I just tested and it works just out of the box. Just one small comment. Please increase the default port number, otherwise one need supervisor credentials to make it run.

    I have couple of questions. How does it handle resample data ? I just tried with ibstore and I can see the ticks but not resampled data.. any suggestions ? How does one disable the DEBUG aka logger messages ?

    Once again thanks for sharing.



  • @rajanprabu said in Real-Time Plotting:

    I have couple of questions. How does it handle resample data ? I just tried with ibstore and I can see the ticks but not resampled data.. any suggestions ? How does one disable the DEBUG aka logger messages ?

    it should handle resampled data, but with ticks, the gaps are wide, depending on the resampled timeframe. for example, if you resample to 1 min from ticks, then they will not be that visible. In live plotting, you can configure what is being shown.



  • you can configure tick data to not be plotted. for the debugging messages, i am not sure what you mean.



  • By DEBUG lines I meant these lines in the output

    DEBUG:bokeh.server.tornado:[pid 72891] 0 clients connected
    DEBUG:bokeh.server.tornado:[pid 72891]   / has 1 sessions with 1 unused
    13-Oct-20 19:24:18 DEBUG     Sending patch for figurepage: defaultdict(<class 'list'>, {'datetime': [(0, Timestamp('2020-10-13 17:24:17.222988'))]})
    13-Oct-20 19:24:18 DEBUG     Sending patch for figure: defaultdict(<class 'list'>, {'datetime': [(0, Timestamp('2020-10-13 17:24:17.222988'))]})
    13-Oct-20 19:24:18 DEBUG     Sending patch for figure: defaultdict(<class 'list'>, {'140360762382800': [(0, 'NaN')], '140360762382992': [(0, 'NaN')], '140360762383184': [(0, 'NaN')], 'datetime': [(0, Timestamp('2020-10-13 17:24:17.222988'))]})
    13-Oct-20 19:24:18 DEBUG     Sending patch for figure: defaultdict(<class 'list'>, {'140360474604560open': [(0, 'NaN')], '140360474604560high': [(0, 'NaN')], '140360474604560low': [(0, 'NaN')], '140360474604560close': [(0, 'NaN')], '140360474604560volume': [(0, 'NaN')], '140360762380368': [(0, 'NaN')], '140360762380560': [(0, 'NaN')], 'datetime': [(0, Timestamp('2020-10-13 17:24:17.222988'))]})
    DEBUG:bokeh.server.contexts:Scheduling 1 sessions to discard
    DEBUG:bokeh.server.contexts:Discarding session 'DsZV4Kgrj4zubFpXMK4MFTgQd3AyalCksvFAFmN5ItYq' last in use 26883.637406999987 milliseconds ago
    DEBUG:bokeh.document.document:Deleting 0 modules for <bokeh.document.document.Document object at 0x7fa7e8e04c10>
    

    @dasch said in Real-Time Plotting:

    @rajanprabu said in Real-Time Plotting:

    it should handle resampled data, but with ticks, the gaps are wide, depending on the resampled timeframe. for example, if you resample to 1 min from ticks, then they will not be that visible. In live plotting, you can configure what is being shown.

    Understood, Thanks for your time and effort. Very much appreciate it.



  • Hello,

    I'm exactly looking for something like this, i must say it looks surprising.
    I merge historical data with livedata from a websocket to another dataframe.
    This dataframe should then be displayed in a live chart.

    However i wonder how to get started on this.
    A little bit more information on how to use this would be very nice :D

    Thanks in advance and thanks for your work


Log in to reply
 

});