@vladisld thank you, now it works !
Best posts made by marketwizard
-
RE: Test a strategy over a list of stocks (50+)
Latest posts made by marketwizard
-
RE: Test a strategy over a list of stocks (50+)
@vladisld thank you, now it works !
-
RE: Test a strategy over a list of stocks (50+)
I tried with
cerebro.run(stdstats=False)
But I received following error:
Traceback (most recent call last): File "C:/Users/marketwizard/PycharmProjects/MW_Backtests/EMA_universeV2/main.py", line 262, in <module> strategies = cerebro.run(stdstats=False) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1127, in run runstrat = self.runstrategies(iterstrat) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1257, in runstrategies strat._addanalyzer(ancls, *anargs, **ankwargs) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\lineseries.py", line 461, in __getattr__ return getattr(self.lines, name) AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute '_addanalyzer'
-
RE: Test a strategy over a list of stocks (50+)
I modified the code in accordance to your recommandation:
- I moved the creation of the cerebro instance, the "run" and "addstrategy" method into the loop.
Then, when I run the code, the backtests results (and the plot) for the first stock in the loop are generated, then I receive the following error:
Traceback (most recent call last): File "C:/Users/marketwizard/PycharmProjects/MW_Backtests/EMA_universeV2/main.py", line 262, in <module> strategies = cerebro.run() File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1127, in run runstrat = self.runstrategies(iterstrat) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\cerebro.py", line 1238, in runstrategies strat._addobserver(False, observers.Broker) File "C:\Users\marketwizard\PycharmProjects\Algotrading_libraries1\lib\site-packages\backtrader\lineseries.py", line 461, in __getattr__ return getattr(self.lines, name) AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute '_addobserver' Process finished with exit code 1
Here the code with the modifications described above
import backtrader as bt import pandas as pd import os.path import sys from datetime import datetime, timedelta, date import math period = 3650 strategyResults = {} lastBuy = None icap = 10000 tickers =['SH', 'VXX', 'EEM', 'QQQ', 'PSQ', 'XLF', 'GDX', 'HYG', 'EFA', 'IAU', 'XOP', 'IWM', 'FXI', 'SLV', 'USO', 'XLE', 'IEMG', 'AMLP', 'EWZ', 'XLK', 'XLI', 'VWO', 'GLD', 'XLP', 'JNK', 'EWJ', 'XLU', 'VEA', 'IEFA', 'XLV', 'PFF', 'VIXY', 'TLT', 'GDXJ', 'LQD', 'XLB', 'BKLN', 'XLY', 'SMH', 'OIH', 'ASHR', 'RSX', 'MCHI', 'VTI', 'EWH', 'SPLV', 'KRE', 'IVV', 'DIA', 'IEF', 'EZU', 'EWT', 'SPDW', 'VOO', 'SCHF', 'EWY', 'MYY', 'DOG', 'EUM'] data_path = r"C:\XXX" # define the resolution(s) of the chart(s) & the strategy timeframes = { '1D': 1 } # New Class to define the content of the strategy class EMAStack(bt.Strategy): # Define the parameters of the strategy params = ( ('portfolio_expo', 0.10), # Max 15% of the Portfolio per trade ('trade_risk', 0.02), # Max 2% risk per trade (stop loss) ('atrdist', 3.0) # ATR based Stop loss distance ) def notify_order(self, order): if order.status == order.Completed: pass if not order.alive(): self.order = None # indicate no order is pending # Initialize the elements which are needed for the strategy (indicators, etc...) def __init__(self): # Define the indicators self.ema50 = bt.indicators.EMA(self.data.close, period=50) self.ema200 = bt.indicators.EMA(self.data.close, period=200) self.atr = bt.indicators.ATR(period=14) # self.log_pnl = [] # Define the crossover signals self.bull_cross = bt.indicators.CrossOver(self.ema50, self.ema200) self.bull_cross.plotinfo.subplot = False self.bear_cross = bt.indicators.CrossOver(self.ema200, self.ema50) self.bear_cross.plotinfo.subplot = False def start(self): self.order = None # sentinel to avoid operations on pending order self.curpos = None def prenext(self): self.next() def next(self): # Get the Amount of cash in the Portfolio cash = self.broker.get_cash() if self.order: return # pending order execution if not self.position: # check if we already have an open position on the stock if self.bull_cross > 0.0: # Calculation of the Stock Qty to buy depending on our risk strategy pdist = self.atr[0] * self.p.atrdist self.pstop = self.data.close[0] - pdist qty = math.floor((cash * self.p.trade_risk) / pdist) portfolio_exposure_calc = qty * self.data.close[0] portfolio_exposure_strategy = cash * self.p.portfolio_expo if portfolio_exposure_calc <= portfolio_exposure_strategy: self.order = self.buy(size=qty) else: qty = math.floor(portfolio_exposure_strategy / self.data.close[0]) self.order = self.buy(size=qty) ''' elif self.bear_cross > 0.0: # Calculation of the Stock Qty to buy depending on our risk strategy pdist = self.atr[0] * self.p.atrdist self.pstop = self.data.close[0] - pdist qty = math.floor((cash * self.p.trade_risk) / pdist) portfolio_exposure_calc = qty * self.data.close[0] portfolio_exposure_strategy = cash * self.p.portfolio_expo if portfolio_exposure_calc <= portfolio_exposure_strategy: self.order = self.sell(size=qty) else: qty = math.floor(portfolio_exposure_strategy / self.data.close[0]) self.order = self.sell(size=qty) ''' else: # in the market pclose = self.data.close[0] pstop = self.pstop # Add detection if we are already short or long if pclose < pstop or self.bear_cross or self.bull_cross: self.close() # stop met - get out else: pdist = self.atr[0] * self.p.atrdist # Update only if greater than self.pstop = max(pstop, pclose - pdist) ''' def notify_trade(self, trade): date = self.data.datetime.datetime() if trade.isclosed: print('-' * 32, ' NOTIFY TRADE ', '-' * 32) print('{}, Avg Price: {}, Profit, Gross {}, Net {}'.format( date, trade.price, round(trade.pnl, 2), round(trade.pnlcomm, 2))) ''' def printSQN(analyzer): sqn = round(analyzer.sqn, 2) print('SQN: {}'.format(sqn)) def pretty_print(format, *args): print(format.format(*args)) def exists(object, *properties): for property in properties: if not property in object: return False object = object.get(property) return True def printTradeAnalysis(cerebro, analyzers): format = " {:<24} : {:<24}" NA = '-' print('Backtesting Results') if hasattr(analyzers, 'ta'): ta = analyzers.ta.get_analysis() openTotal = ta.total.open if exists(ta, 'total', 'open' ) else None closedTotal = ta.total.closed if exists(ta, 'total', 'closed') else None wonTotal = ta.won.total if exists(ta, 'won', 'total' ) else None lostTotal = ta.lost.total if exists(ta, 'lost', 'total' ) else None streakWonLongest = ta.streak.won.longest if exists(ta, 'streak', 'won', 'longest') else None streakLostLongest = ta.streak.lost.longest if exists(ta, 'streak', 'lost', 'longest') else None pnlNetTotal = ta.pnl.net.total if exists(ta, 'pnl', 'net', 'total' ) else None pnlNetAverage = ta.pnl.net.average if exists(ta, 'pnl', 'net', 'average') else None pretty_print(format, 'Open Positions', openTotal or NA) pretty_print(format, 'Closed Trades', closedTotal or NA) pretty_print(format, 'Winning Trades', wonTotal or NA) pretty_print(format, 'Loosing Trades', lostTotal or NA) print('\n') pretty_print(format, 'Longest Winning Streak', streakWonLongest or NA) pretty_print(format, 'Longest Loosing Streak', streakLostLongest or NA) pretty_print(format, 'Strike Rate (Win/closed)', (wonTotal / closedTotal) * 100 if wonTotal and closedTotal else NA) print('\n') pretty_print(format, 'Inital Portfolio Value', '${}'.format(icap)) pretty_print(format, 'Final Portfolio Value', '${}'.format(cerebro.broker.getvalue())) pretty_print(format, 'Net P/L', '${}'.format(round(pnlNetTotal, 2)) if pnlNetTotal else NA) pretty_print(format, 'P/L Average per trade', '${}'.format(round(pnlNetAverage, 2)) if pnlNetAverage else NA) print('\n') if hasattr(analyzers, 'drawdown'): pretty_print(format, 'Drawdown', '${}'.format(analyzers.drawdown.get_analysis()['drawdown'])) if hasattr(analyzers, 'sharpe'): pretty_print(format, 'Sharpe Ratio:', analyzers.sharpe.get_analysis()['sharperatio']) if hasattr(analyzers, 'vwr'): pretty_print(format, 'VRW', analyzers.vwr.get_analysis()['vwr']) if hasattr(analyzers, 'sqn'): pretty_print(format, 'SQN', analyzers.sqn.get_analysis()['sqn']) print('\n') print('Transactions') format = " {:<24} {:<24} {:<16} {:<8} {:<8} {:<16}" pretty_print(format, 'Date', 'Amount', 'Price', 'SID', 'Symbol', 'Value') for key, value in analyzers.txn.get_analysis().items(): pretty_print(format, key.strftime("%Y/%m/%d %H:%M:%S"), value[0][0], value[0][1], value[0][2], value[0][3], value[0][4]) if __name__ == '__main__': for ticker in tickers: cerebro = bt.Cerebro() endDate = date(2021, 1, 10) fromDate = (endDate - timedelta(days=period)) startDate = fromDate.strftime('%Y-%m-%d') filename = '%s_%s_%s.txt' % (ticker, startDate, endDate) datapath = os.path.join(data_path, filename) print(os.path.abspath(datapath)) #''' if not os.path.isfile(datapath): print('file: %s not found' % filename) sys.exit() #''' data = bt.feeds.YahooFinanceCSVData( dataname=datapath, # Do not pass values before this date fromdate=datetime(fromDate.year, fromDate.month, fromDate.day), # Do not pass values before this date todate=datetime(datetime.today().year, datetime.today().month, datetime.today().day), # Do not pass values after this date reverse=False) cerebro.adddata(data) # Set the Cash for the Strategy cerebro.broker.setcash(icap) # Set the comissions cerebro.broker.setcommission(commission=0.005) # Add the analyzers we are interested in cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta') cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown') cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.0, annualize=True, timeframe=bt.TimeFrame.Days) cerebro.addanalyzer(bt.analyzers.VWR, _name='vwr') cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn') cerebro.addanalyzer(bt.analyzers.Transactions, _name='txn') cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio') cerebro.addstrategy(EMAStack) # Run over everything strategies = cerebro.run() EMAStack = strategies[0] # print the analyzers printTradeAnalysis(cerebro, EMAStack.analyzers) cerebro.plot(style='candlestick', barup='green', bardown='red')
-
RE: Test a strategy over a list of stocks (50+)
@vladisld thank you for your answer.
I went through the documentations and the article from backtest-rookies and I also tested their code.
It seems that it is the same portfolio which is investing on every FX pair
What I'm trying to do is a bit different:
I want to backtest my strategy on each stock in order to generate the KPI's for each stock individually (and plot them idivually).
And in the next steps, the goal would be to generate a table / file with datas like:'AAPL' | strike rate = 24% | sharpe ratio = 0.36 | final portfolio value = XXX
'JNJ' | strike rate = 35% | sharpe ratio = 0.52 | final portfolio value = XXX
'AMZN' | strike rate = 60% | sharpe ratio = 1.236 | final portfolio value = XXX
.....
... -
Test a strategy over a list of stocks (50+)
Hello,
I'm currently triying to test a strategy over a list of stocks (30+) and then generate the results of the strategy for each stock.
I tried to use a for loop to go through every stock and get the results, but it doesn't work.
The code only prints the results for the first stock in the list.Here ist the code:
import backtrader as bt import os.path import sys from datetime import datetime, timedelta import math import warnings period = 3650 strategyResults = {} lastBuy = None optimization = True tickers =['SH', 'VXX', 'EEM', 'QQQ', 'PSQ', 'XLF', 'GDX', 'HYG', 'EFA', 'IAU', 'XOP', 'IWM', 'FXI', 'SLV', 'USO', 'XLE', 'IEMG', 'AMLP', 'EWZ', 'XLK', 'XLI', 'VWO', 'GLD', 'XLP', 'JNK', 'EWJ', 'XLU', 'VEA', 'IEFA', 'XLV', 'PFF', 'VIXY', 'TLT', 'GDXJ', 'LQD', 'XLB', 'BKLN', 'XLY', 'SMH', 'OIH', 'ASHR', 'RSX', 'MCHI', 'VTI', 'EWH', 'SPLV', 'KRE', 'IVV', 'DIA', 'IEF', 'EZU', 'EWT', 'SPDW', 'VOO', 'SCHF', 'EWY', 'MYY', 'DOG', 'EUM'] data_path = r"C:\Users\XXXXX" # define the resolution(s) of the chart(s) & the strategy timeframes = { '1D': 1 } # New Class to define the content of the strategy class EMAStack(bt.Strategy): # Define the parameters of the strategy params = ( ('portfolio_expo', 0.10), # Max 15% of the Portfolio per trade ('trade_risk', 0.02), # Max 2% risk per trade (stop loss) ('atrdist', 3.0) # ATR based Stop loss distance ) def notify_order(self, order): if order.status == order.Completed: pass if not order.alive(): self.order = None # indicate no order is pending # Initialize the elements which are needed for the strategy (indicators, etc...) def __init__(self): # Define the indicators self.ema50 = bt.indicators.EMA(self.data.close, period=10) self.ema200 = bt.indicators.EMA(self.data.close, period=30) self.atr = bt.indicators.ATR(period=14) # Define the crossover signals self.bull_cross = bt.indicators.CrossOver(self.ema50, self.ema200) self.bull_cross.plotinfo.subplot = False self.bear_cross = bt.indicators.CrossOver(self.ema200, self.ema50) self.bear_cross.plotinfo.subplot = False def start(self): self.order = None # sentinel to avoid operations on pending order def prenext(self): self.next() def next(self): # Get the Amount of cash in the Portfolio cash = self.broker.get_cash() if self.order: return # pending order execution if not self.position: # check if we already have an open position on the stock if self.bull_cross > 0.0: # Calculation of the Stock Qty to buy depending on our risk strategy pdist = self.atr[0] * self.p.atrdist self.pstop = self.data.close[0] - pdist qty = math.floor((cash * self.p.trade_risk) / pdist) portfolio_exposure_calc = qty * self.data.close[0] portfolio_exposure_strategy = cash * self.p.portfolio_expo if portfolio_exposure_calc <= portfolio_exposure_strategy: self.order = self.buy(size=qty) else: qty = math.floor(portfolio_exposure_strategy / self.data.close[0]) self.order = self.buy(size=qty) elif self.bear_cross > 0.0: # Calculation of the Stock Qty to buy depending on our risk strategy pdist = self.atr[0] * self.p.atrdist self.pstop = self.data.close[0] - pdist qty = math.floor((cash * self.p.trade_risk) / pdist) portfolio_exposure_calc = qty * self.data.close[0] portfolio_exposure_strategy = cash * self.p.portfolio_expo if portfolio_exposure_calc <= portfolio_exposure_strategy: self.order = self.sell(size=qty) else: qty = math.floor(portfolio_exposure_strategy / self.data.close[0]) self.order = self.sell(size=qty) else: # in the market pclose = self.data.close[0] pstop = self.pstop # Add detection if we are already short or long if pclose < pstop or self.bear_cross or self.bull_cross: self.close() # stop met - get out else: pdist = self.atr[0] * self.p.atrdist # Update only if greater than self.pstop = max(pstop, pclose - pdist) ''' def notify_trade(self, trade): date = self.data.datetime.datetime() if trade.isclosed: print('-' * 32, ' NOTIFY TRADE ', '-' * 32) print('{}, Avg Price: {}, Profit, Gross {}, Net {}'.format( date, trade.price, round(trade.pnl, 2), round(trade.pnlcomm, 2))) ''' def printTradeAnalysis(analyzer): ''' Function to print the Technical Analysis results in a nice format. ''' # Get the results we are interested in total_open = analyzer.total.open total_closed = analyzer.total.closed total_won = analyzer.won.total total_lost = analyzer.lost.total win_streak = analyzer.streak.won.longest lose_streak = analyzer.streak.lost.longest pnl_net = round(analyzer.pnl.net.total, 2) strike_rate = round(((total_won / total_closed) * 100), 2) profit_factor = round((total_won / total_lost), 2) # Designate the rows h1 = ['Total Open', 'Total Closed', 'Total Won', 'Total Lost'] h2 = ['Strike Rate', 'Win Streak', 'Losing Streak', 'PnL Net'] r1 = [total_open, total_closed, total_won, total_lost] r2 = [strike_rate, win_streak, lose_streak, pnl_net] # Check which set of headers is the longest. if len(h1) > len(h2): header_length = len(h1) else: header_length = len(h2) # Print the rows print_list = [h1, r1, h2, r2] row_format = "{:<15}" * (header_length + 1) print("Trade Analysis Results:") for row in print_list: print(row_format.format('', *row)) print('Profit Factor: {}'.format(profit_factor)) def printSQN(analyzer): sqn = round(analyzer.sqn, 2) print('SQN: {}'.format(sqn)) if __name__ == '__main__': cerebro = bt.Cerebro() for ticker in tickers: endDate = datetime.today().strftime('%Y-%m-%d') fromDate = (datetime.today() - timedelta(days=period)) startDate = fromDate.strftime('%Y-%m-%d') filename = '%s_%s_%s.txt' % (ticker, startDate, endDate) datapath = os.path.join(data_path, filename) print(os.path.abspath(datapath)) #''' if not os.path.isfile(datapath): print('file: %s not found' % filename) sys.exit() #''' data = bt.feeds.YahooFinanceCSVData( dataname=datapath, # Do not pass values before this date fromdate=datetime(fromDate.year, fromDate.month, fromDate.day), # Do not pass values before this date todate=datetime(datetime.today().year, datetime.today().month, datetime.today().day), # Do not pass values after this date reverse=False) cerebro.adddata(data) # Set the Cash for the Strategy cerebro.broker.setcash(10000) # Set the comissions cerebro.broker.setcommission(commission=0.005) # Add the analyzers we are interested in cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta') cerebro.addanalyzer(bt.analyzers.SQN, _name="sqn") cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name="annualreturn") cerebro.addanalyzer(bt.analyzers.PyFolio, _name="pyfolio") cerebro.addstrategy(EMAStack) # Run over everything strategies = cerebro.run() EMAStack = strategies[0] #cerebro.run() # print the analyzers printTradeAnalysis(EMAStack.analyzers.ta.get_analysis()) printSQN(EMAStack.analyzers.sqn.get_analysis()) print("Final Portfolio Value: %.2f" % cerebro.broker.getvalue())
I hope that someone can help
-
RE: Alpha Vantage DataFeed
@rajanprabu I already test this code and its not working for me, that's why I opened this topic
-
RE: Alpha Vantage DataFeed
Hello ab_trader,
thanks for the quick reply !
did you have a sample code for using this datafeed ? -
Alpha Vantage DataFeed
Hello,
I'm pretty new to Python coding and Backtrader.
However I have done some backtests using the Alpaca API and it worked without to much problems.I would to use the Alpha-Vantage API because they provide not only stock data, but also FOREX and Crypto.
First I tried the Code I found here:
https://backtest-rookies.com/2019/01/04/backtrader-alpha-vantage-data-direct-ingest/from alpha_vantage.timeseries import TimeSeries import pandas as pd import numpy as np import backtrader as bt from datetime import datetime # IMPORTANT! # ---------- # Register for an API at: # https://www.alphavantage.co/support/#api-key # Then insert it here. Apikey = 'XXXXX' def alpha_vantage_eod(symbol_list, compact=False, debug=False, *args, **kwargs): ''' Helper function to download Alpha Vantage Data. This will return a nested list with each entry containing: [0] pandas dataframe [1] the name of the feed. ''' data_list = list() size = 'compact' if compact else 'full' for symbol in symbol_list: if debug: print('Downloading: {}, Size: {}'.format(symbol, size)) # Submit our API and create a session alpha_ts = TimeSeries(key=Apikey, output_format='pandas') data, meta_data = alpha_ts.get_daily(symbol=symbol, outputsize=size) #Convert the index to datetime. data.index = pd.to_datetime(data.index) data.columns = ['Open', 'High', 'Low', 'Close','Volume'] if debug: print(data) data_list.append((data, symbol)) return data_list class TestStrategy(bt.Strategy): def __init__(self): pass def next(self): for i, d in enumerate(self.datas): bar = len(d) dt = d.datetime.datetime() dn = d._name o = d.open[0] h = d.high[0] l = d.low[0] c = d.close[0] v = d.volume[0] print('{} Bar: {} | {} | O: {} H: {} L: {} C: {} V:{}'.format(dt, bar,dn,o,h,l,c,v)) # Create an instance of cerebro cerebro = bt.Cerebro() # Add our strategy cerebro.addstrategy(TestStrategy) # Download our data from Alpha Vantage. symbol_list = ['LGEN.L','LLOY.L'] data_list = alpha_vantage_eod( symbol_list, compact=False, debug=False) for i in range(len(data_list)): data = bt.feeds.PandasData( dataname=data_list[i][0], # This is the Pandas DataFrame name=data_list[i][1], # This is the symbol timeframe=bt.TimeFrame.Days, compression=1, fromdate=datetime(2018,1,1), todate=datetime(2019,1,1) ) #Add the data to Cerebro cerebro.adddata(data) print('Starting to run') # Run the strategy cerebro.run()
Unfortunately it didn't work. I wasn't able to display any data with it.
And yes, I used my API - key (which is working, i tested it in another code)I thought that the problem came from the Pandas dataframe from Alpha-Vantage (maybe not supported anymore)
I tried different things and I done quiet a lot of research in the internet and I can't find any suitable solution.
I also tried to request JSON datas and convert them to pandas or CSV, unfortunately this was also not working.
But maybe someone can help with the code above.
Thanks a lot in adavance
marketwizard