I am able to make a strategy in backtrader which equally invest in stocks based on a yearly constituent list. But if I change the code to monthly constituent lists the portfolio value remains the same. I cannot find what the problem is and how there is no match between the dates of the data and the dates of the constituent lists.
Here you can find the code for a yearly strategy
`class EquallyWeighted(bt.Strategy):
params = (
('constituent_companies', {}), # a dictionary mapping year to list of consistent companies
)
def __init__(self):
self.current_year = self.datas[0].datetime.date().year
self.constituent_companies = self.params.constituent_companies.get(self.current_year, [])
self.weights = {} # Dictionary to store the weights for each company
def next(self):
if self.datas[0].datetime.date().year != self.current_year:
self.current_year = self.datas[0].datetime.date().year
self.close_all_positions() # close all positions
self.constituent_companies = self.params.constituent_companies.get(self.current_year, [])
self.calculate_weights() # calculate new weights for the constituent companies
for i, d in enumerate(self.datas):
if not self.getposition(d).size:
if d._name in self.constituent_companies:
size = self.broker.get_cash() * self.weights[d._name] # calculate position size based on weight
self.buy(data=d, size=size)
def close_all_positions(self):
for i, d in enumerate(self.datas):
position = self.getposition(d).size
if position > 0:
self.close(data=d)
def calculate_weights(self):
num_companies = len(self.constituent_companies)
if num_companies > 0:
weight = 1.0 / num_companies
for company in self.constituent_companies:
self.weights[company] = weight
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro()
# Define the path to the CSV file
base_path = '/Users/jonat/Documents/Documenten/data/Test/Constituents/'
# Read constituent companies for each year
constituent_companies = {}
years = [2000, 2001, 2002, 2003, 2004] # Update with your desired years
for year in years:
csv_path = base_path + f'constituent_companies_{year}.csv'
with open(csv_path, 'r', encoding='utf-8-sig') as csvfile:
reader = csv.reader(csvfile)
next(reader) # Skip the header row
companies = [row[0] for row in reader if row[0]] # Read company names from column A
constituent_companies[year] = companies
cerebro.addstrategy(EquallyWeighted, constituent_companies=constituent_companies)
datalist = ['AAPL.csv', 'DIS.csv', 'IBM.csv', 'BA.csv', 'F.csv', 'GE.csv', 'KO.csv', 'MMM.csv', 'PG.csv', 'XOM.csv']
datapath= "/Users/jonat/Documents/Documenten/data/Test/"
for i in range(len(datalist)):
data= bt.feeds.YahooFinanceCSVData(
dataname=datapath+datalist[i],
# Do not pass values before this date
fromdate=datetime(2000, 1, 1),
# Do not pass values after this date
todate=datetime(2004, 12, 31),
reverse=False)
cerebro.adddata(data, name=datalist[i])
# Set our desired cash start
cerebro.broker.setcash(1000.0)
# Add a FixedSize sizer according to the stake
cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
# Set the commission
cerebro.broker.setcommission(commission=0.0)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())`
Here you can find the code for a monthly strategy.
`class EquallyWeightedM(bt.Strategy):
params = (
('constituent_companies', {}), # a dictionary mapping year to list of consistent companies
)
def __init__(self):
self.constituent_companies = constituent_companies
self.weights = {} # Dictionary to store the weights for each company
def start(self):
self.calculate_weights()
def next(self):
for i, d in enumerate(self.datas):
if not self.getposition(d).size:
if d._name in self.weights:
size = self.broker.get_cash() * self.weights[d._name] # calculate position size based on weight
self.buy(data=d, size=size)
def calculate_weights(self):
num_companies = len(self.constituent_companies)
if num_companies > 0:
weight = 1.0 / num_companies
for company in self.constituent_companies:
self.weights[company] = weight
if __name__ == '__main__':
cerebro = bt.Cerebro()
cerebro.addstrategy(EquallyWeightedM, constituent_companies=constituent_companies)
base_path = '/Users/jonat/Documents/Documenten/data/Test/Constituents/'
dates = [datetime(2000, 1, 1), datetime(2000, 2, 1), datetime(2000, 3, 1), datetime(2000, 4, 1), datetime(2000, 5, 1)]
for date in dates:
csv_path = base_path + f'constituent_companies_{date.strftime("%Y-%m-%d")}.csv'
with open(csv_path, 'r', encoding='utf-8-sig') as csvfile:
reader = csv.reader(csvfile)
next(reader) # Skip the header row
companies = [row[0] for row in reader if row[0]] # Read company names from column A
datalist = ['AAPL.csv', 'DIS.csv', 'IBM.csv', 'BA.csv', 'F.csv', 'GE.csv', 'KO.csv', 'MMM.csv', 'PG.csv', 'XOM.csv']
datapath= "/Users/jonat/Documents/Documenten/data/Test/"
for i in range(len(datalist)):
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath+datalist[i],
fromdate=datetime(2000, 1, 1),
todate=datetime(2004, 12, 31),
reverse=False
)
cerebro.adddata(data, name=datalist[i])
cerebro.broker.setcash(1000.0)
cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
cerebro.broker.setcommission(commission=0.0)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())