There is a pickle file that has many (say 10) stocks names and also have a folder that has all stock data I am trying to run the code to do the MACD analysis and hope can write down the result buy and sell time, price, position, cash on hand. when I have gone this far and trying to run MACD on all stocks. but after two loops, the data load is wrong. and repeats the results.
'''
import backtrader as bt
import pandas as pd
import pickle
import math
def run_test_macd():
with open("sp500tickers.pickle", "rb") as f:
tickers = pickle.load(f)
cerebro.addstrategy(GoldenCross.gold_cross_class)
for ticker in tickers:
macd_stock_test2(ticker)
def macd_stock_test2(ticker):
# print("stock test")
# print("ticker")
cerebro.broker.set_cash(1000000)
ticker_prices = pd.read_csv('stock_dfs/{}.csv'.format(ticker), index_col='Date', parse_dates=True)
# print(ticker_prices)
# ticker prices
feed = bt.feeds.PandasData(dataname=ticker_prices)
print(feed)
print(ticker)
cerebro.adddata(feed)
# cerebro.addsizer(bt.sizers.FixedSize, stake=1000)
# cerebro.addanalyzer(btanalyzers.DrawDown, _name='drawdown')
# cerebro.addanalyzer(btanalyzers.DrawDown, _name='returns')
print('starting protfolio value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('final protfolio value: %.2f' % cerebro.broker.getvalue())
# cerebro.addanalyzer(SQN)
#
# cerebro.addwriter(bt.WriterFile, csv=args.writercsv, rounding=2)
# cerebro.plot(style='candle')
class gold_cross_class(bt.Strategy):
#set parameters to define fast and slow
params = (('fast',40),('slow',150),('order_percentage',0.99),('ticker', "stock"))
#define constractors
def __init__(self):
print("position size:",self.position.size)
self.fast_moving_average=bt.indicators.EMA(
self.data.close, period=self.params.fast, plotname='40 day moving average'
)
self.slow_moving_average = bt.indicators.EMA(
self.data.close, period=self.params.slow, plotname='150 day moving average'
)
self.crossover = bt.indicators.CrossOver(self.fast_moving_average, self.slow_moving_average)
def next(self):
if self.position.size == 0:
if self.crossover >0:
amount_to_invest = (self.params.order_percentage *self.broker.cash)
self.size=math.floor(amount_to_invest/self.data.close)
print("Buy {} shares of {} at {} on {}".format(self.size,self.params.ticker, self.data.close[0],self.data.close[0]))
self.buy(size=self.size)
if self.position.size > 0:
if self.crossover<0:
print("Sell {} shares of {} at {}".format(self.size,self.params.ticker, self.data.close[0]))
self.sell(size=self.size)
'''
See my comments in the docstring at the head of the code. I have commented out some of your data loading only so I can load my own data, since I don't have your data.
import datetime
import backtrader as bt
import pandas as pd
import pickle
import math
"""
You have a number of issues.
1. Establish cerebro in `macd_stock_test2`
2. Call the `run_test_macd` from if __name__ == "__main__":
3. Add in print log
4. Add in order and trade notify.
5. Don't pass in the strategy, create it in `macd_stock_test2`.
6. Change name of GoldCross class to python standard.
"""
def run_test_macd():
# with open("sp500tickers.pickle", "rb") as f:
# tickers = pickle.load(f)
tickers = ["TSLA", "FB"]
for ticker in tickers:
macd_stock_test2(ticker)
def macd_stock_test2(ticker):
# print("stock test")
# print("ticker")
cerebro = bt.Cerebro()
cerebro.addstrategy(GoldCross)
cerebro.broker.set_cash(1000000)
# ticker_prices = pd.read_csv(
# "stock_dfs/{}.csv".format(ticker), index_col="Date", parse_dates=True
# )
# print(ticker_prices)
# ticker prices
# feed = bt.feeds.PandasData(dataname=ticker_prices)
# print(feed)
# print(ticker)
feed = bt.feeds.YahooFinanceData(
dataname=ticker,
timeframe=bt.TimeFrame.Days,
fromdate=datetime.datetime(2019, 1, 1),
todate=datetime.datetime(2020, 12, 31),
reverse=False,
)
cerebro.adddata(feed)
# cerebro.addsizer(bt.sizers.FixedSize, stake=1000)
# cerebro.addanalyzer(btanalyzers.DrawDown, _name='drawdown')
# cerebro.addanalyzer(btanalyzers.DrawDown, _name='returns')
print("starting portfolio value: %.2f" % cerebro.broker.getvalue())
cerebro.run()
print("final portfolio value: %.2f" % cerebro.broker.getvalue())
# cerebro.addanalyzer(SQN)
#
# cerebro.addwriter(bt.WriterFile, csv=args.writercsv, rounding=2)
# cerebro.plot(style='candle')
class GoldCross(bt.Strategy):
# set parameters to define fast and slow
params = (
("fast", 40),
("slow", 150),
("order_percentage", 0.99),
("ticker", "stock"),
)
# define constractors
def __init__(self):
print("position size:", self.position.size)
self.fast_moving_average = bt.indicators.EMA(
self.data.close, period=self.params.fast, plotname="40 day moving average"
)
self.slow_moving_average = bt.indicators.EMA(
self.data.close, period=self.params.slow, plotname="150 day moving average"
)
self.crossover = bt.indicators.CrossOver(
self.fast_moving_average, self.slow_moving_average
)
def log(self, txt, dt=None):
""" Logging function fot this strategy"""
dt = dt or self.data.datetime[0]
if isinstance(dt, float):
dt = bt.num2date(dt)
print("%s, %s" % (dt.date(), txt))
def notify_order(self, order):
""" Triggered upon changes to orders. """
# Suppress notification if it is just a submitted order.
if order.status == order.Submitted:
return
# Print out the date, security name, order number and status.
dt, dn = self.datetime.date(), order.data._name
type = "Buy" if order.isbuy() else "Sell"
self.log(
f"{order.data._name:<6} Order: {order.ref:3d}\tType: {type:<5}\tStatus"
f" {order.getstatusname():<8} \t"
f"Size: {order.created.size:9.4f} Price: {order.created.price:9.4f} "
f"Position: {self.getposition(order.data).size}"
)
if order.status == order.Margin:
return
# Check if an order has been completed
if order.status in [order.Completed]:
self.log(
f"{order.data._name:<6} {('BUY' if order.isbuy() else 'SELL'):<5} "
# f"EXECUTED for: {dn} "
f"Price: {order.executed.price:6.2f} "
f"Cost: {order.executed.value:6.2f} "
f"Comm: {order.executed.comm:4.2f} "
f"Size: {order.created.size:9.4f} "
)
def notify_trade(self, trade):
"""Provides notification of closed trades."""
if trade.isclosed:
self.log(
"{} Closed: PnL Gross {}, Net {},".format(
trade.data._name,
round(trade.pnl, 2),
round(trade.pnlcomm, 1),
)
)
def next(self):
if self.position.size == 0:
if self.crossover > 0:
amount_to_invest = self.params.order_percentage * self.broker.cash
self.size = math.floor(amount_to_invest / self.data.close)
self.log(
"Buy {} shares of {} at {}".format(
self.size,
self.params.ticker,
self.data.close[0],
)
)
self.buy(size=self.size)
if self.position.size > 0:
if self.crossover < 0:
self.log(
"Sell {} shares of {} at {}".format(
self.size, self.params.ticker, self.data.close[0],
)
)
self.sell(size=self.size)
if __name__ == "__main__":
run_test_macd()
With a print out as follows:
starting protfolio value: 1000000.00
position size: 0
2019-10-28, Buy 15105 shares of stock at 65.54
2019-10-29, TSLA Order: 1 Type: Buy Status Accepted Size: 15105.0000 Price: 65.5400 Position: 15105
2019-10-29, TSLA Order: 1 Type: Buy Status Completed Size: 15105.0000 Price: 65.5400 Position: 15105
2019-10-29, TSLA BUY Price: 64.00 Cost: 966720.00 Comm: 0.00 Size: 15105.0000
final protfolio value: 10527931.90
starting protfolio value: 1000000.00
position size: 0
2020-05-12, Buy 4712 shares of stock at 210.1
2020-05-13, FB Order: 2 Type: Buy Status Accepted Size: 4712.0000 Price: 210.1000 Position: 4712
2020-05-13, FB Order: 2 Type: Buy Status Completed Size: 4712.0000 Price: 210.1000 Position: 4712
2020-05-13, FB BUY Price: 209.43 Cost: 986834.16 Comm: 0.00 Size: 4712.0000
final protfolio value: 1294217.28
Process finished with exit code 0