class 的 backtrader 循环函数

backtrader loop functions with class

有一个 pickle 文件有很多(比如 10 个)股票名称 还有一个包含所有股票数据的文件夹 我正在尝试运行代码做MACD分析,希望能写下结果买卖时间、价格、头寸、手头现金。 当我走到这一步并尝试 运行 所有股票的 MACD 时。 但是经过两次循环,数据加载是错误的。并重复结果。

'''
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)
'''

buy and sell price issue

buy order issue when 100% cash is used

updated buy & sell price issues

在代码开头的文档字符串中查看我的注释。我已经注释掉了你的一些数据加载,所以我可以加载我自己的数据,因为我没有你的数据。

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()

打印出来如下:

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