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

Mismatch between Analysers and Observers



  • Hi there,

    In my code I make use of the standard Observers and Analysers. Below is some simplified code:

            cerebro.addobserver(bt.observers.Broker)
            cerebro.addobserver(bt.observers.Trades)
    ...
    
            # Add a strategy,data,sizers etc.
    
            # Add the analyzers we are interested in
            cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="TradeAnalyzer")
    
            # Run over everything
            results = cerebro.run()
            strategy_results = results[0]
    
            TA=strategy_results.analyzers.TradeAnalyzer.get_analysis()
    
            # PNL
            print(round(TA.pnl.net.total,2))
            #Strike rate
            print(round((TA.won.total/TA.total.closed)*100,2))
    ...
    

    This will print a PNL that is correct, and a Strike rate that isn't correct.
    For example, in the attached sanity check plot, there isn't a single unprofitable trade, which is also in agreement with the output in the strategy (I can attach if needed).
    However, according to the Trades observer, strike rate from TA is reported as 62.5%. (won=5/lost=3).
    So then this makes me question my Sharpe and SQN analysers as well.

    Any help is appreciated!
    Josh

    Here's the full output of TA if that helps.

    AutoOrderedDict([('total', AutoOrderedDict([('total', 8), ('open', 0), ('closed', 8)])), ('streak', AutoOrderedDict([('won', AutoOrderedDict([('current', 1), ('longest', 4)])), ('lost', AutoOrderedDict([('current', 0), ('longest', 2)]))])), ('pnl', AutoOrderedDict([('gross', AutoOrderedDict([('total', 480.4899999999998), ('average', 60.06124999999997)])), ('net', AutoOrderedDict([('total', 320.5035299999998), ('average', 40.06294124999997)]))])), ('won', AutoOrderedDict([('total', 5), ('pnl', AutoOrderedDict([('total', 340.37375999999966), ('average', 68.07475199999993), ('max', 140.4145000000004)]))])), ('lost', AutoOrderedDict([('total', 3), ('pnl', AutoOrderedDict([('total', -19.870229999999886), ('average', -6.623409999999962), ('max', -13.243230000000224)]))])), ('long', AutoOrderedDict([('total', 8), ('pnl', AutoOrderedDict([('total', 320.5035299999998), ('average', 40.06294124999997), ('won', AutoOrderedDict([('total', 340.37375999999966), ('average', 68.07475199999993), ('max', 140.4145000000004)])), ('lost', AutoOrderedDict([('total', -19.870229999999886), ('average', -6.623409999999962), ('max', -13.243230000000224)]))])), ('won', 5), ('lost', 3)])), ('short', AutoOrderedDict([('total', 0), ('pnl', AutoOrderedDict([('total', 0.0), ('average', 0.0), ('won', AutoOrderedDict([('total', 0.0), ('average', 0.0), ('max', 0.0)])), ('lost', AutoOrderedDict([('total', 0.0), ('average', 0.0), ('max', 0.0)]))])), ('won', 0), ('lost', 0)])), ('len', AutoOrderedDict([('total', 23), ('average', 2.875), ('max', 6), ('min', 1), ('won', AutoOrderedDict([('total', 20), ('average', 4.0), ('max', 6), ('min', 1)])), ('lost', AutoOrderedDict([('total', 3), ('average', 1.0), ('max', 1), ('min', 1)])), ('long', AutoOrderedDict([('total', 23), ('average', 2.875), ('max', 6), ('min', 1), ('won', AutoOrderedDict([('total', 20), ('average', 4.0), ('max', 6), ('min', 1)])), ('lost', AutoOrderedDict([('total', 3), ('average', 1.0), ('max', 1), ('min', 1)]))])), ('short', AutoOrderedDict([('total', 0), ('average', 0.0), ('max', 0), ('min', 9223372036854775807), ('won', AutoOrderedDict([('total', 0), ('average', 0.0), ('max', 0), ('min', 9223372036854775807)])), ('lost', AutoOrderedDict([('total', 0), ('average', 0.0), ('max', 0), ('min', 9223372036854775807)]))]))]))])
     2019-04-24 12:48:13 INFO [backtest.py:119] | Final Portfolio Value: 10320.50
    

    0_1556103667308_AAPL_60min_60min_CheatStrategy.png



  • I should mention that I have self.set_coc(True) in the Strategy, which explains why the first trade is profitable: Buys at 3rd bar close, and sells at 4th close.
    I.e., shift red and green arrows 1 to the left.


  • administrators

    One of the major problems:

    • A user reports something he/she believes to be a problem (it may or may not be)
    • The user posts half-cooked code which cannot of course be used for anything
    • No sample data is provided to reproduce the problem
    • No logging information is provided
    • A chart is the proof of all sins

    Let's use the chart then ...

    0_1556138826884_a867a6d4-de38-49a4-b7eb-870d1750823d-image.png

    That should say everything, because unless David Copperfield, this mere mortal needs something reliable and reproducible to work with.



  • Fair point.
    Full minimal example below.
    It seems the issue(?) is with commission. The TA Analyser seems to take it into account, the Observers (specifically Trades) does not.
    You can test this by enabling and disabling commission. With commission=0%, the Observer and Analyser StrikeRate match, with commision!=0%, they do not.

    So then the question: Is there an easy way to get Observers (or is it just the Trades one where this happens?) to take into account commission?

    import backtrader as bt
    from datetime import datetime
    
    
    class firstStrategy(bt.Strategy):
    
    
        def __init__(self):
            self.rsi = bt.indicators.RSI_SMA(self.data.close, period=20)
            self.broker.set_coc(True)
    
        def next(self):
            if not self.position:
                # Some bogus values to get some trades
                if self.rsi[0] < 30:
                    self.buy(size=5)
            else:
                if self.rsi[0] > 20:
                    self.close()
    
    
    #Variable for our starting cash
    startcash = 10000
    
    cerebro = bt.Cerebro()
    cerebro.addstrategy(firstStrategy)
    # File is one of the txt files in datas dir, just with most of entries removed for simplicity.
    data = bt.feeds.YahooFinanceCSVData(dataname="yhoo.txt")
    cerebro.adddata(data)
    
    # Set our desired cash start
    cerebro.broker.setcash(startcash)
    cerebro.broker.setcommission(commission=0.01)
    
    # Add the analyzers we are interested in
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="TradeAnalyzer")
    # Run over everything
    results = cerebro.run()
    strategy_results = results[0]
    
    TA=strategy_results.analyzers.TradeAnalyzer.get_analysis()
    print(TA)
    final_portfolio_values=dict()
    final_portfolio_values["StrikeRate"]=round((TA.won.total/TA.total.closed)*100,2)
    #Get final portfolio Value
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - startcash
    print('Final Portfolio Value: ${}'.format(portvalue))
    print('P/L: ${}'.format(pnl))
    print("StrikeRate = ",final_portfolio_values["StrikeRate"])
    #... And plot
    cerebro.plot(style='candlestick')
    
    

    Cheers,
    Josh


  • administrators


Log in to reply
 

});