Skip to content

Data Resampling

When data is only available in a single timeframe and the analysis has to be done for a different timeframe, it’s time to do some resampling.

“Resampling” should actually be called “Upsampling” given that one goes from a source timeframe to a larger time frame (for example: days to weeks)

“Downsampling” is not yet possible.

backtrader has built-in support for resampling by passing the original data through a filter object which has intelligently been named: DataResampler.

The class has two functionalities:

  • Change the timeframe

  • Compress bars

To do so the DataResampler uses standard feed.DataBase parameters during construction:

  • timeframe (default: bt.TimeFrame.Days)

    Destination timeframe which to be useful has to be equal or larger than the source

  • compression (default: 1)

    Compress the selected value “n” to 1 bar

Let’s see an example from Daily to weekly with a handcrafted script:

$ ./data-resampling.py --timeframe weekly --compression 1

The output:

image

We can compare it to the original daily data:

$ ./data-resampling.py --timeframe daily --compression 1

The output:

image

The magic is done by executing the following steps:

  • Loading the data as usual

  • Feeding the data into a DataResampler with the desired

    • timeframe

    • compression

The code in the sample (the entire script at the bottom).

    # Load the Data
    datapath = args.dataname or '../datas/sample/2006-day-001.txt'
    data = btfeeds.BacktraderCSVData(
        dataname=datapath)

    # Handy dictionary for the argument timeframe conversion
    tframes = dict(
        daily=bt.TimeFrame.Days,
        weekly=bt.TimeFrame.Weeks,
        monthly=bt.TimeFrame.Months)

    # Resample the data
    data_resampled = bt.DataResampler(
        dataname=data,
        timeframe=tframes[args.timeframe],
        compression=args.compression)

    # Add the resample data instead of the original
    cerebro.adddata(data_resampled)

A last example in which we first change the time frame from daily to weekly and then apply a 3 to 1 compression:

$ ./data-resampling.py --timeframe weekly --compression 3

The output:

image

From the original 256 daily bars we end up with 18 3-week bars. The breakdown:

  • 52 weeks

  • 52 / 3 = 17.33 and therefore 18 bars

It doesn’t take much more. Of course intraday data can also be resampled.

The sample code for the resampling test script.

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse

import backtrader as bt
import backtrader.feeds as btfeeds


def runstrat():
    args = parse_args()

    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)

    # Add a strategy
    cerebro.addstrategy(bt.Strategy)

    # Load the Data
    datapath = args.dataname or '../datas/sample/2006-day-001.txt'
    data = btfeeds.BacktraderCSVData(
        dataname=datapath)

    # Handy dictionary for the argument timeframe conversion
    tframes = dict(
        daily=bt.TimeFrame.Days,
        weekly=bt.TimeFrame.Weeks,
        monthly=bt.TimeFrame.Months)

    # Resample the data
    data_resampled = bt.DataResampler(
        dataname=data,
        timeframe=tframes[args.timeframe],
        compression=args.compression)

    # Add the resample data instead of the original
    cerebro.adddata(data_resampled)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot(style='bar')


def parse_args():
    parser = argparse.ArgumentParser(
        description='Pandas test script')

    parser.add_argument('--dataname', default='', required=False,
                        help='File Data to Load')

    parser.add_argument('--timeframe', default='weekly', required=False,
                        choices=['daily', 'weekly', 'monhtly'],
                        help='Timeframe to resample to')

    parser.add_argument('--compression', default=1, required=False, type=int,
                        help='Compress n bars into 1')

    return parser.parse_args()


if __name__ == '__main__':
    runstrat()