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

Issue with the optimizing



  • Hi there,
    While backtesting works well for me, I run into problems with optimizing with respect to sharpe ratio when using my below code, see the below output.
    It seems that the final_results_list does get only NoneType values for the Sharpe Rations. Any idea what went wrong? Thanks!

    Unbenannt.JPG

    Output:

    File "C:\Users\User\Desktop\Python for Finance\MACD_Strategy\BackTrader_MACD_Example\btmain.py", line 174, in <module>
      sort_by_sharpe = sorted(final_results_list, key=lambda x: x[3], reverse=True)
    
    TypeError: '<' not supported between instances of 'NoneType' and 'NoneType'
    
    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import datetime  # For datetime objects
    import os.path  # To manage paths
    import sys  # To find out the script name (in argv[0])
    
    # Import the backtrader platform
    import backtrader as bt
    import backtrader.indicators as btind
    
    import pandas as pd
    import numpy as np
    
    from strategies import *
    
    ###--------------------------------
    ###--------------------------------
    ###--------------------------------
    ### FOR OPTIMIZATION
    
    #Instantiate Cerebro engine
    cerebro = bt.Cerebro(optreturn=False, optdatas=True)
    
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, 'C:/Users/User/Desktop/Python for Finance/MACD_Strategy/BackTrader_MACD_Example/ohlc.csv')
    
    data = bt.feeds.GenericCSVData(
        dataname=datapath,
        #add or remove do in-sample and out-of-sample tests (i.e. optimize in one and backtest in next years)
        #settings for in-sample optimization
        #fromdate=datetime.datetime(2020, 5, 4, 0, 0, 0),
        #todate=datetime.datetime(2020, 6, 4, 0, 0, 0),
        #settings for out-of-sample backtest
        #fromdate=datetime.datetime(2020, 6, 4, 0, 0, 0),
        #todate=datetime.datetime(2020, 6, 23, 0, 0, 0),  
        nullvalue=0.0,
        dtformat=('%Y-%m-%d %H:%M:%S'),
        datetime=0,
        time=-1,
        high=2,
        low=3,
        open=1,
        close=4,
        volume=5,
        openinterest=-1,
        timeframe=bt.TimeFrame.Minutes)
    
    cerebro.adddata(data)
    
    #Add strategy to Cerebro
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe_ratio')
    cerebro.optstrategy(MAcrossover, pfast=range(5, 20), pslow=range(50, 100)) 
    
    #Default position size / 1 = 1 underlying share
    cerebro.addsizer(bt.sizers.SizerFix, stake=1)
    
    if __name__ == '__main__':
        optimized_runs = cerebro.run()
    
        final_results_list = []
        for run in optimized_runs:
            for strategy in run:
                PnL = round(strategy.broker.get_value() - 10000,2)
                sharpe = strategy.analyzers.sharpe_ratio.get_analysis()
                final_results_list.append([strategy.params.pfast, 
                    strategy.params.pslow, PnL, sharpe['sharperatio']])
    
        sort_by_sharpe = sorted(final_results_list, key=lambda x: x[3], reverse=True)
         
        for line in sort_by_sharpe[:5]:
             print(line)
    

    Strategy:

    class MAcrossover(bt.Strategy): 
        #Moving average parameters
        params = (('pfast',20),('pslow',50),)
        
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.datetime(0)
            #print('%s, %s' % (dt.isoformat(), txt))
            print("%s, %s" % (dt, txt))
            
        def __init__(self):     
            self.dataclose = self.datas[0].close     
            # Order variable will contain ongoing order details/status
            self.order = None     
            # Instantiate moving averages     
            self.slow_sma = bt.indicators.MovingAverageSimple(self.datas[0],                                       
                            period=self.params.pslow)     
            self.fast_sma = bt.indicators.MovingAverageSimple(self.datas[0], 
                            period=self.params.pfast)
        
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                #Active Buy/Sell order submitted/accepted - Nothing to do
                return
            #Check if an order has been completed
            #Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():             
                    self.log('BUY EXECUTED, %.2f' % order.executed.price)         
                elif order.issell():             
                    self.log('SELL EXECUTED, %.2f' % order.executed.price)
                    self.bar_executed = len(self)
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')     
                #Reset orders  
                self.order = None
    
    
        def next(self):
            if self.order:
                return
            #Check if we are in the market
            if not self.position:
            #We are not in the market, look for a signal to OPEN trades
                #If the 20 SMA is above the 50 SMA
                if self.fast_sma[0] > self.slow_sma[0] and self.fast_sma[-1] < self.slow_sma[-1]:
                     self.log('BUY CREATE, %.2f' % self.dataclose[0])
                     #Keep track of the created order to avoid a 2nd order
                     self.order = self.buy()
                #Otherwise if the 20 SMA is below the 50 SMA
                elif self.fast_sma[0] < self.slow_sma[0] and self.fast_sma[-1] > self.slow_sma[-1]:
                    self.log('SELL CREATE, %.2f' % self.dataclose[0])
                    #Keep track of the created order to avoid a 2nd order
                    self.order = self.sell()
            else:
                # We are already in the market, look for a signal to CLOSE trades
                if len(self) >= (self.bar_executed + 5):
                   self.log('CLOSE CREATE, %.2f' % self.dataclose[0])
                   self.order = self.close()
    


  • The default timeframe for the SharpeRatio analyzer is TimeFrame.Years. So this could be a reason if you don't have enough data.

    Also, please see the following post: "Why do I get 'None' as value for SharpeRatio?"


Log in to reply
 

});