happydasch / btoandav20

Support for Oanda-V20 API in backtrader
Apache License 2.0
130 stars 52 forks source link

Replay Feature is fetching higher timeframe instead of lower timeframe #29

Closed edesmars closed 5 years ago

edesmars commented 5 years ago

Hi,

Thanks for the help on the previous issue. Works perfectly ;) So going through my backtesting, I wanted to use the replay function to backtest a strategy based on 15Min candles using 1Min candles for the ticks.

Though when using the oandav20feed, the data retrieved is already the replay dataframe / compression. In my example, the data fetch was 15Min instead of 1Min. That means that the tick (calling next on strategy) are not using M1 data but M15 so the same as if i don't use replay.

I did a bit of debug and was able to find out that the replay filter will override self._timeframe and self._compression from the data feed. Though in my case a simple override of the replay function in the oandav20feed seems to have fixed the issue with the following code:

    def replay(self, **kwargs):
        # save original timeframe and compression to fetch data
        # they will be overriden when calling replay
        orig_timeframe = self._timeframe
        orig_compression = self._compression
        #setting up replay configuration
        super(DataBase, self).replay(**kwargs)
        #putting back original timeframe and compression to fetch correct data
        #the replay configuration will still use the correct dataframe and compression for strategy
        self._timeframe = orig_timeframe
        self._compression = orig_compression

You can reproduce the issue by using a modified replay sample files as below (fetch day data to use on a strategy for weeks):

#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# 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 argparse

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
import btoandav20
import datetime

class SMAStrategy(bt.Strategy):
    params = (
        ('period', 10),
        ('onlydaily', False),
    )

    def __init__(self):
        self.sma = btind.SMA(self.data, period=self.p.period)

    def start(self):
        self.counter = 0

    def prenext(self):
        self.counter += 1
        print('prenext len %d - counter %d' % (len(self), self.counter))

    def next(self):
        self.counter += 1
        print('---next len %d - counter %d' % (len(self), self.counter))

def runstrat():

    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
    StoreCls = btoandav20.stores.OandaV20Store

    cerebro.addstrategy(SMAStrategy)

    # Load the Data
    storekwargs = dict(
        token="xxxxxxxxxxx",
        account="yyyyyyyyyyy",
        practice=True
    )

    store = StoreCls(**storekwargs)
    DataFactory = store.getdata
    dtformat = '%Y-%m-%dT%H:%M:%S'

    fromdate = datetime.datetime.strptime("2010-01-01T00:00:00", dtformat)
    todate = datetime.datetime.strptime("2010-01-31T00:00:00", dtformat)
    datakwargs = dict(
        timeframe=bt.TimeFrame.Days,
        compression=1,
        qcheck=0.5,
        historical=True,
        fromdate=fromdate,
        todate=todate,
        bidask=True,

    )
    data = DataFactory(dataname="EUR_USD", **datakwargs)
    # Prepare the data for replay at higher timeframe
    data.replay(
        timeframe=bt.TimeFrame.Weeks,
        compression=1)

    # First add the original data - smaller timeframe
    cerebro.adddata(data)

    # Run over everything
    cerebro.run(preload=False)

if __name__ == '__main__':
    runstrat()

Warning: With the fix proposed, on my system, the values don't correspond between oanda data and replaying the data. I believe it must be some timezone issues between the data coming back from Oanda and the timing of replayed data. But it could simply be another issue on replaying.

happydasch commented 5 years ago

I have added your method to the repository. I did not have time to check the code, but will do so in a few days.

edesmars commented 5 years ago

Thank you, let me know if you need any help when you are looking into it. ;)

happydasch commented 5 years ago

seems to be working fine