Oandatest no attribute 'contractdetails'



  • Hello Dro,
    I' m trying to use the code Oandatest.py but that doesn't work I have the following error:

      File "C:/samples/oandatest/oandatest.py", line 168, in start
        if self.data0.contractdetails is not None:
      File "C:\backtrader\lineseries.py", line 429, in __getattr__
        return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'contractdetails'
    

    I have install Oanda.py it works indepandently from BT and entered the parameters : acount , practice and token.
    Do you have an idea how to solve this issue ?


  • administrators

    Without some code it is really difficult to understand why things may go wrong.

    There are two random guesses that can be made by looking at the actual reported error:

    1. The actual error code seems to indicate that data0 is not an actual OandaData

    or

    1. The requested timeframe/compression pair is not supported and the data feed is bailing out from creating a contractdetails attribute. This is reported in as a NOTSUPPORTED_TF data notification.

    The sample is not coded with full robustness, because it is a sample.



  • re: Oandatest no attribute 'contractdetails'
    Hello thanks for the answer. Do you know how I can select the right dataframe supported ?
    I'm sorry for the code it is below. here this the paramaters I used

    --token mytoken --account myaccount --data0 EUR_USD

    Thank you very much for your help.

    # Copyright (C) 2015,2016 Daniel Rodriguez
    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.
    # You should have received a copy of the GNU General Public License
    # along with this program. If not, see http://www.gnu.org/licenses/.
    ###############################################################################
    from future import (absolute_import, division, print_function, unicode_literals)
    import oandapy
    import argparse
    import datetime
    # The above could be sent to an independent module
    
    import backtrader as bt
    from backtrader.utils import flushfile # win32 quick stdout flushing
    
    StoreCls = bt.stores.OandaStore
    DataCls = bt.feeds.OandaData
    BrokerCls = bt.brokers.OandaBroker
    
    class TestStrategy(bt.Strategy):
    params = dict(
    smaperiod=5,
    trade=False,
    stake=10,
    exectype=bt.Order.Market,
    stopafter=0,
    valid=None,
    cancel=0,
    donotcounter=False,
    sell=False,
    )
    
    def __init__(self):
        # To control operation entries
        self.orderid = list()
        self.order = None
    
        self.counttostop = 0
        self.datastatus = 0
    
        # Create SMA on 2nd data
        self.sma = bt.indicators.MovAv.SMA(self.data, period=self.p.smaperiod)
    
        print('--------------------------------------------------')
        print('Strategy Created')
        print('--------------------------------------------------')
    
    def notify_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            self.counttostop = self.p.stopafter
            self.datastatus = 1
    
    def notify_store(self, msg, *args, **kwargs):
        print('*' * 5, 'STORE NOTIF:', msg)
    
    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')
    
    def notify_trade(self, trade):
        print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
        print(trade)
        print('-' * 50, 'TRADE END')
    
    def prenext(self):
        self.next(frompre=True)
    
    def next(self, frompre=False):
        txt = list()
        txt.append('Data0')
        txt.append('%04d' % len(self.data0))
        dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        txt.append('{:f}'.format(self.data.datetime[0]))
        txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
        txt.append('{:f}'.format(self.data.open[0]))
        txt.append('{:f}'.format(self.data.high[0]))
        txt.append('{:f}'.format(self.data.low[0]))
        txt.append('{:f}'.format(self.data.close[0]))
        txt.append('{:6d}'.format(int(self.data.volume[0])))
        txt.append('{:d}'.format(int(self.data.openinterest[0])))
        txt.append('{:f}'.format(self.sma[0]))
        print(', '.join(txt))
    
        if len(self.datas) > 1 and len(self.data1):
            txt = list()
            txt.append('Data1')
            txt.append('%04d' % len(self.data1))
            dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
            txt.append('{}'.format(self.data1.datetime[0]))
            txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
            txt.append('{}'.format(self.data1.open[0]))
            txt.append('{}'.format(self.data1.high[0]))
            txt.append('{}'.format(self.data1.low[0]))
            txt.append('{}'.format(self.data1.close[0]))
            txt.append('{}'.format(self.data1.volume[0]))
            txt.append('{}'.format(self.data1.openinterest[0]))
            txt.append('{}'.format(float('NaN')))
            print(', '.join(txt))
    
        if self.counttostop:  # stop after x live lines
            self.counttostop -= 1
            if not self.counttostop:
                self.env.runstop()
                return
    
        if not self.p.trade:
            return
    
        if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.sell:
                # price = round(self.data0.close[0] * 0.90, 2)
                price = self.data0.close[0] - 0.005
                self.order = self.buy(size=self.p.stake,
                                      exectype=self.p.exectype,
                                      price=price,
                                      valid=self.p.valid)
            else:
                # price = round(self.data0.close[0] * 1.10, 4)
                price = self.data0.close[0] - 0.05
                self.order = self.sell(size=self.p.stake,
                                       exectype=self.p.exectype,
                                       price=price,
                                       valid=self.p.valid)
    
            self.orderid.append(self.order)
        elif self.position and not self.p.donotcounter:
            if self.order is None:
                if not self.p.sell:
                    self.order = self.sell(size=self.p.stake // 2,
                                           exectype=bt.Order.Market,
                                           price=self.data0.close[0])
                else:
                    self.order = self.buy(size=self.p.stake // 2,
                                          exectype=bt.Order.Market,
                                          price=self.data0.close[0])
    
            self.orderid.append(self.order)
    
        elif self.order is not None and self.p.cancel:
            if self.datastatus > self.p.cancel:
                self.cancel(self.order)
    
        if self.datastatus:
            self.datastatus += 1
    
    """
    def start(self):
    if self.data0.contractdetails is not None:
    print('-- Contract Details:')
    print(self.data0.contractdetails)
    
        header = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume',
                  'OpenInterest', 'SMA']
        print(', '.join(header))
    
        self.done = False
    
    """
    
    def runstrategy():
    args = parse_args()
    
    # Create a cerebro
    cerebro = bt.Cerebro()
    
    storekwargs = dict(
        token="mytoken",
        account="myaccount",
        practice="practice"
    )
    
    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)
    
    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)
    
    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
    
    data0 = DataFactory(dataname=args.data0, **datakwargs)
    
    data1 = None
    if args.data1 is not None:
        if args.data1 != args.data0:
            datakwargs['timeframe'] = datatf1
            datakwargs['compression'] = datacomp1
            data1 = DataFactory(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,
    )
    
    if args.replay:
        cerebro.replaydata(dataname=data0, **rekwargs)
    
        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.replaydata(dataname=data1, **rekwargs)
    
    elif args.resample:
        cerebro.resampledata(dataname=data0, **rekwargs)
    
        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.resampledata(dataname=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)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        smaperiod=args.smaperiod,
                        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)
    
    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)
    if args.exactbars < 1:  # plotting is possible
        if args.plot:
            pkwargs = dict(style='line')
            if args.plot is not True:  # evals to True but is not True
                npkwargs = eval('dict(' + args.plot + ')')  # args were passed
                pkwargs.update(npkwargs)
    
            cerebro.plot(**pkwargs)
    
    def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    description='Test Oanda integration')
    
    parser.add_argument('--exactbars', default=1, type=int,
                        required=False, action='store',
                        help='exactbars level, use 0/-1/-2 to enable plotting')
    
    parser.add_argument('--stopafter', default=0, type=int,
                        required=False, action='store',
                        help='Stop after x lines of LIVE data')
    
    parser.add_argument('--no-store',
                        required=False, action='store_true',
                        help='Do not use the store pattern')
    
    parser.add_argument('--debug',
                        required=False, action='store_true',
                        help='Display all info received from source')
    
    parser.add_argument('--token', default="mytoken",
                        required=True, action='store',
                        help='Access token to use')
    
    parser.add_argument('--account', default=None,
                        required=True, action='store',
                        help='Account identifier to use')
    
    parser.add_argument('--live', default=None,
                        required=False, action='store',
                        help='Go to live server rather than practice')
    
    parser.add_argument('--qcheck', default=0.5, type=float,
                        required=False, action='store',
                        help=('Timeout for periodic '
                              'notification/resampling/replaying check'))
    
    parser.add_argument('--data0', default='EUR_USD',
                        required=True, action='store',
                        help='data 0 into the system')
    
    parser.add_argument('--data1', default=None,
                        required=False, action='store',
                        help='data 1 into the system')
    
    parser.add_argument('--timezone', default=None,
                        required=False, action='store',
                        help='timezone to get time output into (pytz names)')
    
    parser.add_argument('--bidask', default=None,
                        required=False, action='store_true',
                        help='Use bidask ... if False use midpoint')
    
    parser.add_argument('--useask', default=None,
                        required=False, action='store_true',
                        help='Use the "ask" of bidask prices/streaming')
    
    parser.add_argument('--no-backfill_start',
                        required=False, action='store_true',
                        help='Disable backfilling at the start')
    
    parser.add_argument('--no-backfill',
                        required=False, action='store_true',
                        help='Disable backfilling after a disconnection')
    
    parser.add_argument('--historical',
                        required=False, action='store_true',
                        help='do only historical download')
    
    parser.add_argument('--fromdate',
                        required=False, action='store',
                        help=('Starting date for historical download '
                              'with format: YYYY-MM-DD[THH:MM:SS]'))
    
    parser.add_argument('--smaperiod', default=20, type=int,
                        required=False, action='store',
                        help='Period to apply to the Simple Moving Average')
    
    pgroup = parser.add_mutually_exclusive_group(required=False)
    
    pgroup.add_argument('--replay',
                        required=False, action='store_true',
                        help='replay to chosen timeframe')
    
    pgroup.add_argument('--resample',
                        required=False, action='store_true',
                        help='resample to chosen timeframe')
    
    parser.add_argument('--timeframe', default=bt.TimeFrame.Names[0],
                        choices=bt.TimeFrame.Names,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay')
    
    parser.add_argument('--compression', default=1, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay')
    
    parser.add_argument('--timeframe1', default=None,
                        choices=bt.TimeFrame.Names,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay - Data1')
    
    parser.add_argument('--compression1', default=None, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay - Data1')
    
    parser.add_argument('--no-takelate',
                        required=False, action='store_true',
                        help=('resample/replay, do not accept late samples'))
    
    parser.add_argument('--no-bar2edge',
                        required=False, action='store_true',
                        help='no bar2edge for resample/replay')
    
    parser.add_argument('--no-adjbartime',
                        required=False, action='store_true',
                        help='no adjbartime for resample/replay')
    
    parser.add_argument('--no-rightedge',
                        required=False, action='store_true',
                        help='no rightedge for resample/replay')
    
    parser.add_argument('--broker',
                        required=False, action='store_true',
                        help='Use Oanda as broker')
    
    parser.add_argument('--trade',
                        required=False, action='store_true',
                        help='Do Sample Buy/Sell operations')
    
    parser.add_argument('--sell',
                        required=False, action='store_true',
                        help='Start by selling')
    
    parser.add_argument('--donotcounter',
                        required=False, action='store_true',
                        help='Do not counter the 1st operation')
    
    parser.add_argument('--exectype', default=bt.Order.ExecTypes[0],
                        choices=bt.Order.ExecTypes,
                        required=False, action='store',
                        help='Execution to Use when opening position')
    
    parser.add_argument('--stake', default=10, type=int,
                        required=False, action='store',
                        help='Stake to use in buy operations')
    
    parser.add_argument('--valid', default=None, type=float,
                        required=False, action='store',
                        help='Seconds to keep the order alive (0 means DAY)')
    
    parser.add_argument('--cancel', default=0, type=int,
                        required=False, action='store',
                        help=('Cancel a buy order after n bars in operation,'
                              ' to be combined with orders like Limit'))
    
    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example (escape the quotes if needed):\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))
    
    if pargs is not None:
        return parser.parse_args(pargs)
    
    return parser.parse_args()
    
    if name == 'main':
    runstrategy()
    


  • from your clues I found out the time frame didn't work if I g below the Minutes.
    Ticks didn't works and seconds didn't work.
    with minutes I have the following error message:

    --------------------------------------------------
    Strategy Created
    --------------------------------------------------
    Datetime, Open, High, Low, Close, Volume, OpenInterest, SMA
    ***** DATA NOTIF: NOTSUBSCRIBED
    
    Process finished with exit code 0
    

    when I use Oandpy without BT It works and I don't need to specify the Account the token is enough.
    do you know why TIcks and seconds did not work ?

    I sorry can you please erase , the topic I created by error it was supposed to be the answer of this topic.

    Thank you very much

    oanda = oandapy.API(environment="practice", access_token="mytoken ")
    
    response = oanda.get_prices(instruments="EUR_USD")
    prices = response.get("prices")
    asking_price = prices[0].get("ask")
    
    
    
    marketdata= oanda.get_history(instrument="EUR_USD",granularity="M1",candleFormat="midpoint", start=startdt, end=enddt)
    
    candle=marketdata['candles']
    print (type(candle))
    

  • administrators

    The data feed documentation says:

        This data feed supports only this mapping of ``timeframe`` and
        ``compression``, which comply with the definitions in the OANDA API
        Developer's Guid::
            (TimeFrame.Seconds, 5): 'S5',
            (TimeFrame.Seconds, 10): 'S10',
            (TimeFrame.Seconds, 15): 'S15',
            (TimeFrame.Seconds, 30): 'S30',
            (TimeFrame.Minutes, 1): 'M1',
            (TimeFrame.Minutes, 2): 'M3',
            (TimeFrame.Minutes, 3): 'M3',
            (TimeFrame.Minutes, 4): 'M4',
            (TimeFrame.Minutes, 5): 'M5',
            (TimeFrame.Minutes, 10): 'M5',
            (TimeFrame.Minutes, 15): 'M5',
            (TimeFrame.Minutes, 30): 'M5',
            (TimeFrame.Minutes, 60): 'H1',
            (TimeFrame.Minutes, 120): 'H2',
            (TimeFrame.Minutes, 180): 'H3',
            (TimeFrame.Minutes, 240): 'H4',
            (TimeFrame.Minutes, 360): 'H6',
            (TimeFrame.Minutes, 480): 'H8',
            (TimeFrame.Days, 1): 'D',
            (TimeFrame.Weeks, 1): 'W',
            (TimeFrame.Months, 1): 'M',
    

    That means that (Minutes, 1) is supported if you only specified --timeframe Minutes. If you specified --timeframe Seconds, that implies a compression of 1 and it is not supported. Those limits are specified here:

    Yes, one can receive ticks with the Streaming API (which backtrader uses to build subsequent bars in real-time), but backfilling wouldn't then be possible.


  • administrators

    This is a run of the sample right now (with account and token changed)

    $ ./oandatest.py --account 0000000 --token A-B --data0 EUR_USD --timeframe Minutes
    --------------------------------------------------
    Strategy Created
    --------------------------------------------------
    -- Contract Details:
    {u'pip': u'0.0001', u'instrument': u'EUR_USD', u'maxTradeUnits': 10000000, u'displayName': u'EUR/USD'}
    Datetime, Open, High, Low, Close, Volume, OpenInterest, SMA
    ***** DATA NOTIF: DELAYED
    Data0, 0001, 736326.863194, 2016-12-28T20:43:00.000000, 1.041095, 1.041100, 1.041070, 1.041100,      7, 0, nan
    Data0, 0002, 736326.863889, 2016-12-28T20:44:00.000000, 1.041080, 1.041145, 1.041055, 1.041105,     17, 0, nan
    Data0, 0003, 736326.864583, 2016-12-28T20:45:00.000000, 1.041115, 1.041205, 1.041110, 1.041110,     11, 0, nan
    Data0, 0004, 736326.865278, 2016-12-28T20:46:00.000000, 1.041070, 1.041070, 1.040885, 1.040920,     13, 0, nan
    Data0, 0005, 736326.865972, 2016-12-28T20:47:00.000000, 1.040895, 1.040925, 1.040895, 1.040905,      5, 0, 1.041028
    ...
    ...
    ...
    Data0, 0500, 736327.236806, 2016-12-29T05:41:00.000000, 1.045880, 1.045880, 1.045880, 1.045880,      1, 0, 1.045891
    ***** DATA NOTIF: LIVE
    Data0, 0501, 736327.236821, 2016-12-29T05:41:01.375075, 1.045800, 1.045800, 1.045800, 1.045800,      0, 0, 1.045871
    ...
    
    This is with a standard `Oanda` account with no special permissions whatsoever. With the default settings the sample uses the `practice` server.


  • Hello thank you for your response:
    I took the sample and used the same parameters as you. except my account and my token

    I still have :

    --------------------------------------------------
    Strategy Created
    --------------------------------------------------
    Datetime, Open, High, Low, Close, Volume, OpenInterest, SMA
    ***** DATA NOTIF: NOTSUBSCRIBED
    

    Process finished with exit code 0

    However when I import data and use the rest API of oanda I didn't need to specify the account and it work anyway.
    I can receive historical data.
    I use a demo account.


  • administrators

    No contract is being returned for your request and hence the answer: NOT_SUBSCRIBED

    Unless it would be the obvious (pointed out in the README)

    Oanda (needs oandapy) (REST API Only - v20 does not support streaming)


  • administrators

    And of course the token is not needed to access the data. But the sample is also ready for trading and that's why it requires the token.

    You can remove that limitation in the sample.



  • @backtrader
    Hello Dro,
    I'm trying to use the sample oandatest.py but I have this error message.
    "
    traceback (most recent call last):
    File "C:/Users/demo/PycharmProjects/oandatest.py", line 31, in <module>
    StoreCls = bt.stores.OandaStore
    AttributeError: 'module' object has no attribute 'OandaStore'
    "

    I use the last version of backtrader and I have installed ondapyV20
    I use the token and account name not the account number .
    My account is a V20 type.

    I utilise the last version of the sample oandatest.py.

    do you have any idea how to make this sample works ? did anyone had the same problem ?



  • @backtrader
    please forget the my question : V20 is not supported with ondatest.py I need to ask the support for a oldversion account.



  • @backtrader
    I have asked for a legacy account . at the end of the year legacy account wont access the api service anymore.
    I try with the legacy account (2121212 )

    here the code :
    from future import (absolute_import, division, print_function,
    unicode_literals)

    import argparse
    import datetime

    The above could be sent to an independent module

    import backtrader as bt
    from backtrader.utils import flushfile # win32 quick stdout flushing

    StoreCls = bt.stores.OandaStore
    DataCls = bt.feeds.OandaData
    #BrokerCls = bt.brokers.OandaBroker

    class TestStrategy(bt.Strategy):
    params = dict(
    smaperiod=5,
    trade=False,
    stake=10,
    exectype=bt.Order.Market,
    stopafter=0,
    valid=None,
    cancel=0,
    donotcounter=False,
    sell=False,
    usebracket=False,
    )

    def __init__(self):
        # To control operation entries
        self.orderid = list()
        self.order = None
    
        self.counttostop = 0
        self.datastatus = 0
    
        # Create SMA on 2nd data
        self.sma = bt.indicators.MovAv.SMA(self.data, period=self.p.smaperiod)
    
        print('--------------------------------------------------')
        print('Strategy Created')
        print('--------------------------------------------------')
    
    def notify_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            self.counttostop = self.p.stopafter
            self.datastatus = 1
    
    def notify_store(self, msg, *args, **kwargs):
        print('*' * 5, 'STORE NOTIF:', msg)
    
    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')
    
    def notify_trade(self, trade):
        print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
        print(trade)
        print('-' * 50, 'TRADE END')
    
    def prenext(self):
        self.next(frompre=True)
    
    def next(self, frompre=False):
        txt = list()
        txt.append('Data0')
        txt.append('%04d' % len(self.data0))
        dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        txt.append('{:f}'.format(self.data.datetime[0]))
        txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
        txt.append('{:f}'.format(self.data.open[0]))
        txt.append('{:f}'.format(self.data.high[0]))
        txt.append('{:f}'.format(self.data.low[0]))
        txt.append('{:f}'.format(self.data.close[0]))
        txt.append('{:6d}'.format(int(self.data.volume[0])))
        txt.append('{:d}'.format(int(self.data.openinterest[0])))
        txt.append('{:f}'.format(self.sma[0]))
        print(', '.join(txt))
    
        if len(self.datas) > 1 and len(self.data1):
            txt = list()
            txt.append('Data1')
            txt.append('%04d' % len(self.data1))
            dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
            txt.append('{}'.format(self.data1.datetime[0]))
            txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
            txt.append('{}'.format(self.data1.open[0]))
            txt.append('{}'.format(self.data1.high[0]))
            txt.append('{}'.format(self.data1.low[0]))
            txt.append('{}'.format(self.data1.close[0]))
            txt.append('{}'.format(self.data1.volume[0]))
            txt.append('{}'.format(self.data1.openinterest[0]))
            txt.append('{}'.format(float('NaN')))
            print(', '.join(txt))
    
        if self.counttostop:  # stop after x live lines
            self.counttostop -= 1
            if not self.counttostop:
                self.env.runstop()
                return
    
        if not self.p.trade:
            return
    
        if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.usebracket:
                if not self.p.sell:
                    # price = round(self.data0.close[0] * 0.90, 2)
                    price = self.data0.close[0] - 0.005
                    self.order = self.buy(size=self.p.stake,
                                          exectype=self.p.exectype,
                                          price=price,
                                          valid=self.p.valid)
                else:
                    # price = round(self.data0.close[0] * 1.10, 4)
                    price = self.data0.close[0] - 0.05
                    self.order = self.sell(size=self.p.stake,
                                           exectype=self.p.exectype,
                                           price=price,
                                           valid=self.p.valid)
    
            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=self.p.stake,
                                                    exectype=bt.Order.Market,
                                                    price=price,
                                                    stopprice=price - 0.10,
                                                    limitprice=price + 0.10,
                                                    valid=self.p.valid)
    
            self.orderid.append(self.order)
        elif self.position and not self.p.donotcounter:
            if self.order is None:
                if not self.p.sell:
                    self.order = self.sell(size=self.p.stake // 2,
                                           exectype=bt.Order.Market,
                                           price=self.data0.close[0])
                else:
                    self.order = self.buy(size=self.p.stake // 2,
                                          exectype=bt.Order.Market,
                                          price=self.data0.close[0])
    
            self.orderid.append(self.order)
    
        elif self.order is not None and self.p.cancel:
            if self.datastatus > self.p.cancel:
                self.cancel(self.order)
    
        if self.datastatus:
            self.datastatus += 1
    
    def start(self):
        if self.data0.contractdetails is not None:
            print('-- Contract Details:')
            print(self.data0.contractdetails)
    
        header = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume',
                  'OpenInterest', 'SMA']
        print(', '.join(header))
    
        self.done = False
    

    def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()
    
    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)
    
    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)
    
    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
    
    data0 = DataFactory(dataname=args.data0, **datakwargs)
    
    data1 = None
    if args.data1 is not None:
        if args.data1 != args.data0:
            datakwargs['timeframe'] = datatf1
            datakwargs['compression'] = datacomp1
            data1 = DataFactory(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,
    )
    
    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)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        smaperiod=args.smaperiod,
                        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)
    
    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)
    if args.exactbars < 1:  # plotting is possible
        if args.plot:
            pkwargs = dict(style='line')
            if args.plot is not True:  # evals to True but is not True
                npkwargs = eval('dict(' + args.plot + ')')  # args were passed
                pkwargs.update(npkwargs)
    
            cerebro.plot(**pkwargs)
    

    def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    description='Test Oanda integration')

    parser.add_argument('--exactbars', default=1, type=int,
                        required=False, action='store',
                        help='exactbars level, use 0/-1/-2 to enable plotting')
    
    parser.add_argument('--stopafter', default=0, type=int,
                        required=False, action='store',
                        help='Stop after x lines of LIVE data')
    
    parser.add_argument('--no-store',
                        required=False, action='store_true',
                        help='Do not use the store pattern')
    
    parser.add_argument('--debug',
                        required=False, action='store_true',
                        help='Display all info received from source')
    
    parser.add_argument('--token', default=None,
                        required=True, action='store',
                        help='Access token to use')
    
    parser.add_argument('--account', default=None,
                        required=True, action='store',
                        help='Account identifier to use')
    
    parser.add_argument('--live', default=None,
                        required=False, action='store',
                        help='Go to live server rather than practice')
    
    parser.add_argument('--qcheck', default=0.5, type=float,
                        required=False, action='store',
                        help=('Timeout for periodic '
                              'notification/resampling/replaying check'))
    
    parser.add_argument('--data0', default=None,
                        required=True, action='store',
                        help='data 0 into the system')
    
    parser.add_argument('--data1', default=None,
                        required=False, action='store',
                        help='data 1 into the system')
    
    parser.add_argument('--timezone', default=None,
                        required=False, action='store',
                        help='timezone to get time output into (pytz names)')
    
    parser.add_argument('--bidask', default=None,
                        required=False, action='store_true',
                        help='Use bidask ... if False use midpoint')
    
    parser.add_argument('--useask', default=None,
                        required=False, action='store_true',
                        help='Use the "ask" of bidask prices/streaming')
    
    parser.add_argument('--no-backfill_start',
                        required=False, action='store_true',
                        help='Disable backfilling at the start')
    
    parser.add_argument('--no-backfill',
                        required=False, action='store_true',
                        help='Disable backfilling after a disconnection')
    
    parser.add_argument('--historical',
                        required=False, action='store_true',
                        help='do only historical download')
    
    parser.add_argument('--fromdate',
                        required=False, action='store',
                        help=('Starting date for historical download '
                              'with format: YYYY-MM-DD[THH:MM:SS]'))
    
    parser.add_argument('--smaperiod', default=5, type=int,
                        required=False, action='store',
                        help='Period to apply to the Simple Moving Average')
    
    pgroup = parser.add_mutually_exclusive_group(required=False)
    
    pgroup.add_argument('--replay',
                        required=False, action='store_true',
                        help='replay to chosen timeframe')
    
    pgroup.add_argument('--resample',
                        required=False, action='store_true',
                        help='resample to chosen timeframe')
    
    parser.add_argument('--timeframe', default=bt.TimeFrame.Names[1],
                        choices=bt.TimeFrame.Names,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay')
    
    parser.add_argument('--compression', default=1, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay')
    
    parser.add_argument('--timeframe1', default=None,
                        choices=bt.TimeFrame.Names,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay - Data1')
    
    parser.add_argument('--compression1', default=None, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay - Data1')
    
    parser.add_argument('--no-takelate',
                        required=False, action='store_true',
                        help=('resample/replay, do not accept late samples'))
    
    parser.add_argument('--no-bar2edge',
                        required=False, action='store_true',
                        help='no bar2edge for resample/replay')
    
    parser.add_argument('--no-adjbartime',
                        required=False, action='store_true',
                        help='no adjbartime for resample/replay')
    
    parser.add_argument('--no-rightedge',
                        required=False, action='store_true',
                        help='no rightedge for resample/replay')
    
    parser.add_argument('--broker',
                        required=False, action='store_true',
                        help='Use Oanda as broker')
    
    parser.add_argument('--trade',
                        required=False, action='store_true',
                        help='Do Sample Buy/Sell operations')
    
    parser.add_argument('--sell',
                        required=False, action='store_true',
                        help='Start by selling')
    
    parser.add_argument('--usebracket',
                        required=False, action='store_true',
                        help='Test buy_bracket')
    
    parser.add_argument('--donotcounter',
                        required=False, action='store_true',
                        help='Do not counter the 1st operation')
    
    parser.add_argument('--exectype', default=bt.Order.ExecTypes[0],
                        choices=bt.Order.ExecTypes,
                        required=False, action='store',
                        help='Execution to Use when opening position')
    
    parser.add_argument('--stake', default=10, type=int,
                        required=False, action='store',
                        help='Stake to use in buy operations')
    
    parser.add_argument('--valid', default=None, type=float,
                        required=False, action='store',
                        help='Seconds to keep the order alive (0 means DAY)')
    
    parser.add_argument('--cancel', default=0, type=int,
                        required=False, action='store',
                        help=('Cancel a buy order after n bars in operation,'
                              ' to be combined with orders like Limit'))
    
    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example (escape the quotes if needed):\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))
    
    if pargs is not None:
        return parser.parse_args(pargs)
    
    return parser.parse_args()
    

    if name == 'main':
    runstrategy()


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.