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 theTrades
observer, strike rate fromTA
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!
JoshHere'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
-
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. -
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 ...
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 withcommission
. TheTA
Analyser
seems to take it into account, theObservers
(specificallyTrades
) does not.
You can test this by enabling and disablingcommission
. Withcommission=0%
, theObserver
andAnalyser
StrikeRate
match, withcommision!=0%
, they do not.So then the question: Is there an easy way to get
Observers
(or is it just theTrades
one where this happens?) to take into accountcommission
?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 -