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

Optomisation giving different results



  • Hi,

    first of all thank you all for all your hard work.

    I have gone from knowing very little python a few weeks ago to writing my first strategy in backtrader. I started with gekko but then said to myself that it was worth learning a platform with greater potential.

    I am however scratching my head at the moment. When I run my code and vary the optomisation parameters I get different results. E.g. when I find a set of optimal paramaters and then squeeze my ranges to near the optimal values something seems wrong.

    Here are some of my results:

    Period: 13, rsi_low: 32, rsi_high: 72, PnL: 2150.7
    cerebro.optstrategy(firstStrategy, period=range(10, 15), rsi_low=range(30, 35), rsi_high=range(55, 75))

    Period: 13, rsi_low: 32, rsi_high: 72, PnL: 2466.8
    cerebro.optstrategy(firstStrategy, period=range(13, 14), rsi_low=range(32, 33), rsi_high=range(72, 73))

    Period: 13, rsi_low: 32, rsi_high: 72, PnL: 2207.9
    cerebro.optstrategy(firstStrategy, period=range(12, 14), rsi_low=range(31, 33), rsi_high=range(71, 73))

    Here is my code:

    import backtrader as bt
    import ccxt
    import datetime
    import time
    import backtrader.feeds as btfeed
    import math
    import pandas as pd
    import os.path
    import sys
    
    class dataFeed(btfeed.GenericCSVData):
        params = (
            ('dtformat', '%Y-%m-%d %H:%M:%S'),
            ('datetime', 0),
            ('open', 1),
            ('high', 2),
            ('low', 3),
            ('close', 4),
            ('volume', 5),
            ('openinterest', -1)
        )
    
    class firstStrategy(bt.Strategy):
        params = (
            ("period", 21),
            ("rsi_low", 41),
            ("rsi_high", 66),
        )
    
        def __init__(self):
            self.startcash = self.broker.getvalue()
            self.rsi = bt.indicators.RSI_SMA(self.data.close, period=self.params.period)
    
        def next(self):
            if not self.position:
                if self.rsi < self.params.rsi_low:
                    self.buy(size=100)
            else:
                if self.rsi > self.params.rsi_high:
                    self.sell(size=100)
    
    # INPUT CONDITIONS TO FEED INTO CEREBRO IS ADDED HERE
    if __name__ == '__main__':
        # Variable for our starting cash
        startcash = 1000000
        # Create an instance of cerebro
        cerebro = bt.Cerebro(optreturn=False)
    
        # Add our strategy
        cerebro.optstrategy(firstStrategy, period=range(12, 14), rsi_low=range(31, 33), rsi_high=range(71, 73))
    
        # DATA FEED FROM EXCHANGE
    
        symbol = str('ETH/USDT')
        timeframe = str('15m')
        exchange = str('poloniex')
        exchange_out = str(exchange)
        start_date = str('2018-3-16 00:00:00')
        end_date = str('2018-3-17 19:00:00')
    
        # Get our Exchange
        exchange = getattr(ccxt, exchange)()
        exchange.load_markets()
    
    
        def to_unix_time(timestamp):
            epoch = datetime.datetime.utcfromtimestamp(0)  # start of epoch time
            my_time = datetime.datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")  # plugin your time object
            delta = my_time - epoch
            return delta.total_seconds() * 1000
    
        # Get data
        hist_start_date = int(to_unix_time(start_date))
        hist_end_date = int(to_unix_time(end_date))
        data = exchange.fetch_ohlcv(symbol, timeframe, since=hist_start_date, limit=hist_end_date)
        header = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Volume']
        df = pd.DataFrame(data, columns=header)
        df['Timestamp'] = pd.to_datetime(df['Timestamp'], unit='ms')
        #df = pd.DataFrame(data, columns=header).set_index('Timestamp')
    
        #Precision
        df = df.round(3)
    
    
        # Save it
        symbol_out = symbol.replace("/", "")
        filename = '{}-{}-{}.csv'.format(exchange_out, symbol_out, timeframe)
        df.to_csv(filename, index= False)
    
        #READ DATA FROM CSV FILE
        modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
        datapath = os.path.join(modpath,str(filename))
        data = dataFeed(dataname=datapath, timeframe=bt.TimeFrame.Minutes, compression=60,)
    
    
        # Add the data to Cerebro
        cerebro.adddata(data)
    
        # Set our desired cash start
        cerebro.broker.setcash(startcash)
    
        # RUN STRATEGY THROUGH CEREBRO USING INPUT DATA
        opt_runs = cerebro.run()
    
        # CREATE A LIST VARIABLE THAT CONTAINS RESULTS
        final_results_list = []
        for run in opt_runs:
            for strategy in run:
                value = round(strategy.broker.get_value(), 2)
                PnL = round(value - startcash, 2)
                period = strategy.params.period
                rsi_low = strategy.params.rsi_low
                rsi_high = strategy.params.rsi_high
                final_results_list.append([period, rsi_low, rsi_high, 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[3], reverse=True)
    
        # Print results
        print('Results: Ordered by Profit:')
        for result in by_PnL:
            print('Period: {}, rsi_low: {}, rsi_high: {}, PnL: {}'.format(result[0], result[1], result[2], result[3]))
    
    

    Any ideas? Also if anybody reading this can tell me how to use the data frame without going via csv then that would also be great!


  • administrators

    @j45p41 said in Optomisation giving different results:

    data = exchange.fetch_ohlcv(symbol, timeframe, since=hist_start_date, limit=hist_end_date)
    

    Save it once to a file and use it always, rather than pulling data, because data can be back-adjusted.

    @j45p41 said in Optomisation giving different results:

    Also if anybody reading this can tell me how to use the data frame without going via csv then that would also be great!

    Use PandasData



  • Thank you that seemed to have worked!