Quantcast
Channel: Active questions tagged datafeed - Stack Overflow
Viewing all articles
Browse latest Browse all 51

Can't access data added inside a strategy

$
0
0

I am trying to backtest a rebalancing strategy using performance of SPX. The (simplified) idea is: if price is above 200day moving average, then buy top 10 tickers on a pre-loaded list. If price is below 200 day moving average, sell everything in the portfolio.

Below is the code. Daily SPX data was loaded to cerebro initially to calculate indicators. Then the weekly list comprised of dates and a pre-generated list of 10 tickers for each week (Friday) was loaded.

Only when the date on the SPX dataframe match the date of the list, the 10 tickers on the list will be picked and their respective price info on that day and the next day will be fetched from yfinance (the reason for this is because the list could change every week therefore preloading all data for all tickers for the whole period does not seem to be efficient). The price info of tickers with existing positions will also be loaded.

Then the strategy will continue to check if the condition (close > MA200) is satisfied, if yes, using cash to buy the 10 tickers on the list. If not, sell all existing positions.

I got the following error message:

==============================Traceback (most recent call last):  File "c:\Users\abc\Documents\BackTrader\testcode4.py", line 123, in <module>    cerebro.run()    ~~~~~~~~~~~^^  File "C:\Users\abc\Documents\BackTrader\.venv\Lib\site-packages\backtrader\cerebro.py", line 1132, in run    runstrat = self.runstrategies(iterstrat)  File "C:\Users\abc\Documents\BackTrader\.venv\Lib\site-packages\backtrader\cerebro.py", line 1298, in runstrategies    self._runonce(runstrats)    ~~~~~~~~~~~~~^^^^^^^^^^^  File "C:\Users\abc\Documents\BackTrader\.venv\Lib\site-packages\backtrader\cerebro.py", line 1700, in _runonce    strat._oncepost(dt0)    ~~~~~~~~~~~~~~~^^^^^  File "C:\Users\abc\Documents\BackTrader\.venv\Lib\site-packages\backtrader\strategy.py", line 309, in _oncepost    self.next()    ~~~~~~~~~^^  File "c:\Users\abc\Documents\BackTrader\testcode4.py", line 91, in next    size = self.broker.get_cash() / 10 / data.close[0]                                         ~~~~~~~~~~^^^  File "C:\Users\abc\Documents\BackTrader\.venv\Lib\site-packages\backtrader\linebuffer.py", line 163, in __getitem__    return self.array[self.idx + ago]           ~~~~~~~~~~^^^^^^^^^^^^^^^^IndexError: array index out of range==============================

I am new to Python and BackTrader, so any help will be appreciated. Below are the simplified codes.

import backtrader as btimport pandas as pdimport yfinance as yf# Define strategyclass WeeklyRotation(bt.Strategy):    params = (        ('printlog', False),    )    def __init__(self, cerebro):        self.cerebro = cerebro        self.dataclose = self.datas[0].close        # Add indicators        self.ma200 = bt.indicators.SimpleMovingAverage(self.dataclose, period=200)    def notify_order(self, order):        # Buy/Sell order submitted/accepted to/by broker - Nothing to do        if order.status in [order.Submitted, order.Accepted]:            return        # Check if an order has been completed        # Attention: broker could reject order if not enough cash        if order.status in [order.Completed]:            # Log long trade execution info            if order.isbuy():                self.log('BUY EXECUTED, Ticker: %s, Price: %.2f, Size: %d, Cost: %.2f, Comm: %.2f' %                         (order.data._name,                        order.executed.price,                         order.executed.size,                         order.executed.value,                         order.executed.comm,))            else:                self.log('SELL EXECUTED, Price: %.2f, Size: %d, Cost: %.2f, Comm: %.2f' %                         (order.executed.price,                           order.executed.size,                           order.executed.value,                           order.executed.comm))            self.log(f'Portfolio value: {self.broker.get_value()}')            for position in self.getpositions():                self.log(f'Ticker: {position.data._name}, Price: {position.price}, Size: {position.size}, Value: {position.size * position.price}')        elif order.status in [order.Canceled, order.Margin, order.Rejected]:            self.log('Order Canceled/Margin/Rejected')    def next(self):        # Get the current date        current_date = self.datas[0].datetime.date(0)        # Load the pregenerated weekly list for strategy.        portfolio_df = pd.read_csv('weekly_list.csv', index_col=0)        portfolio_df.index = pd.to_datetime(portfolio_df.index)        # Check if the current date is in the portfolio        if current_date.strftime('%Y-%m-%d') in portfolio_df.index.strftime('%Y-%m-%d'):            # Get the list of tickers for the current date            tickers = portfolio_df.loc[current_date.strftime('%Y-%m-%d')].dropna().tolist()            # Load data for tickers on the weekly ticker list for the current date and next trading day            for ticker in tickers:                new_ticker_dataname = yf.download(ticker, start=current_date, end=current_date + pd.Timedelta(days=4))))                new_ticker_data = bt.feeds.PandasData(dataname=new_ticker_dataname)                self.cerebro.adddata(new_ticker_data, name=ticker)            # Check existing positions            for position in self.getpositions():                ticker = position.data._name                if ticker not in tickers:                    existing_ticker_data = bt.feeds.PandasData(dataname=yf.download(ticker, start=current_date, end=current_date + pd.Timedelta(days=4)))                    self.cerebro.adddata(existing_ticker_data, name=ticker)                    # Create a sell order for tickers not in the portfolio list                    self.sell(data=position.data)                    self.log(f'Sell order created for {ticker}, size: {position.size}')            # Up to here everything seems to be fine #            # Buy top 10 stocks if price is over 200-day moving average            if (self.dataclose[0] >= self.ma200[0]):                for ticker in tickers:                    data = self.cerebro.datasbyname[ticker]                    size = self.broker.get_cash() / 10 / data.close[0]                    self.buy(data=data, size=size)            else: # Just sell everything                for position in self.getpositions():                    ticker = position.data._name                    self.sell(ticker)        else:            returnif __name__ == '__main__':    # Create a cerebro entity    cerebro = bt.Cerebro()    # Co-pilot suggested to add cerebro as a parameter below so that data feeds can be added     # inside the strategy, but I don't know whether it works or not.    # Add a strategy    cerebro.addstrategy(WeeklyRotation, cerebro=cerebro)    # Create a DataFrame from SPX historical data    df = pd.read_csv('SPX_data.csv', index_col=0)    # Convert the index to datetime format    df.index = pd.to_datetime(df.index)    # Create a Data Feed using the DataFrame    data = bt.feeds.PandasData(dataname=df)    # Add the Data Feed to Cerebro    cerebro.adddata(data)    # Set cash start and commission rate    cerebro.broker.set_cash(1000000)    cerebro.broker.setcommission(commission=0.001)    # Run over everything    cerebro.run()

Tried to print out the value of new_ticker_dataname, new_ticker_data and self.cerebro.datasbyname[ticker]. Here is what I get:

new_ticker_dataname: Price           Close       High        Low       Open    VolumeTicker           EBAY       EBAY       EBAY       EBAY      EBAYDate2020-06-26  47.061386  47.422045  46.312327  47.061386  177624002020-06-29  47.449795  47.477537  46.349320  47.227849   8933900, lenth: 2new_ticker_data: <backtrader.feeds.pandafeed.PandasData object at 0x00000186138B2350>, lenth: 0self.cerebro.datasbyname[ticker]: <backtrader.feeds.pandafeed.PandasData object at 0x000001861385E350>

What I plan to do is to get the closing price of the particular ticker (in above case it's EBAY), so that I can calculate number of shares to buy and place an order. Also need to have the open price for the next trading day in order to know what price the trade will be executed at (based on market order). Since I can't find a way to locate the closing price (and other prices), don't know how to proceed.

Many thanks in advance.


Viewing all articles
Browse latest Browse all 51

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>