Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    How to backtest using portfolio compositions

    General Code/Help
    2
    2
    1134
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • A
      anarchy1989 last edited by

      I have a csv file / pandas dataframe which looks like this. It contains various portfolio compositions for different strategies. Mostly based on different optimisation methods, max sharpe, min VaR etc..

      date	        asset	percentage strategy
      4-Jan-21	AAPL	12.00%	strategy1
      4-Jan-21	TSM	1.00%	strategy1
      4-Jan-21	IBM	31.00%	strategy1
      4-Jan-21	KO	15.00%	strategy1
      4-Jan-21	AMD	41.00%	strategy1
      4-Jan-21	DELL	23.00%	strategy2
      4-Jan-21	TSM	12.20%	strategy2
      4-Jan-21	IBM	15.24%	strategy2
      4-Jan-21	KO	1.50%	strategy2
      4-Jan-21	NKE	7.50%	strategy2
      4-Jan-21	TSLA	9.50%	strategy2
      4-Jan-21	CSCO	3.30%	strategy2
      4-Jan-21	JPM	27.76%	strategy2
      4-Jan-21	AMD	45%	strategy3
      4-Jan-21	BA	0.50%	strategy3
      4-Jan-21	ORCL	54.50%	strategy3
      5-Jan-21	…	…	strategy1
      
      

      I want to test a bunch of strategies with a 30 asset portfolio.

      So lets say on 4Jan2021, strategy 1 asks me to hold 12% in apple, 1% in TSM.. etc. I want to be able to check the prices and know howmany I should be holding. I can easily create a function for that.

      The code I have seen in the repo mostly shows how to do stuff with indicators, like SMA cross over, RSI...

      My question is, is it possible to create and test portfolios based on these compositions I have so I can test which strategy is the most profitable one? So if I'm testing strategy1, it would check this frame, and know howmany stocks in apple to buy or sell on that particular day.

      Is there example of code anywhere that would let me test my strategy based on the frame I have?

      I am new to backtesting and backtrader, any help would be greatly appreciated.

      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @anarchy1989 last edited by

        @anarchy1989
        The first thing you will want to do it load your targets with your datas. I like personally to attach the target to the dataline I as I add it to backtrader.

        tickers = {"FB": 0.25, "MSFT": 0.4, "TSLA": 0.35}
        
        for ticker, target in tickers.items():
            data = bt.feeds.YahooFinanceData(
                dataname=ticker,
                timeframe=bt.TimeFrame.Days,
                fromdate=datetime.datetime(2019, 1, 1),
                todate=datetime.datetime(2020, 12, 31),
                reverse=False,
            )
            data.target = target
            cerebro.adddata(data, name=ticker)
        

        In next you will want to go through each data, and determine the current allocation. If the current allocation is too far from the desired allocation (threshold) you trade all datas.

        Notice there is a buffer variable. This will reduce the overall value of the account for calculating units to trade. This helps avoid margin.

        You will use a dictionary to track this information.

        def next(self):
            track_trades = dict()
            total_value = self.broker.get_value() * (1 - self.p.buffer)
        
            for d in self.datas:
                track_trades[d] = dict()
                value = self.broker.get_value(datas=[d])
                allocation = value / total_value
                units_to_trade = (d.target - allocation) * total_value / d.close[0]
                track_trades[d]["units"] = units_to_trade
        
                # Can check to make sure there is enough distance away from ideal to trade.
                track_trades[d]["threshold"] = abs(d.target - allocation) > self.p.threshold
        
        

        Check all the thresholds to determine if trading. If any of datas need trading, then all need trading.

        rebalance = False
        for values in track_trades.values():
            if values['threshold']:
                rebalance = True
        
        if not rebalance:
            return
        

        Finally, execute your trades. Always sell first to generate cash in the account and avoid margins.

        # Sell shares first
        for d, value in track_trades.items():
            if value["units"] < 0:
                self.sell(d, size=value["units"])
        
        # Buy shares second
        for d, value in track_trades.items():
            if value["units"] > 0:
                self.buy(d, size=value["units"])
        
        

        Here is the all of the code for your reference.

        import datetime
        import backtrader as bt
        
        class Strategy(bt.Strategy):
        
            params = (
                ("buffer", 0.05),
                ("threshold", 0.025),
            )
        
            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 print_signal(self):
                self.log(
                    f"o {self.datas[0].open[0]:7.2f} "
                    f"h {self.datas[0].high[0]:7.2f} "
                    f"l {self.datas[0].low[0]:7.2f} "
                    f"c {self.datas[0].close[0]:7.2f} "
                    f"v {self.datas[0].volume[0]:7.0f} "
                )
        
            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.
                type = "Buy" if order.isbuy() else "Sell"
                self.log(
                    f"{order.data._name:<6} Order: {order.ref:3d} "
                    f"Type: {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:5.2f}"
                )
                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):
                track_trades = dict()
                total_value = self.broker.get_value() * (1 - self.p.buffer)
        
                for d in self.datas:
                    track_trades[d] = dict()
                    value = self.broker.get_value(datas=[d])
                    allocation = value / total_value
                    units_to_trade = (d.target - allocation) * total_value / d.close[0]
                    track_trades[d]["units"] = units_to_trade
        
                    # Can check to make sure there is enough distance away from ideal to trade.
                    track_trades[d]["threshold"] = abs(d.target - allocation) > self.p.threshold
        
                rebalance = False
                for values in track_trades.values():
                    if values['threshold']:
                        rebalance = True
        
                if not rebalance:
                    return
        
                # Sell shares first
                for d, value in track_trades.items():
                    if value["units"] < 0:
                        self.sell(d, size=value["units"])
        
                # Buy shares second
                for d, value in track_trades.items():
                    if value["units"] > 0:
                        self.buy(d, size=value["units"])
        
        
        if __name__ == "__main__":
        
            cerebro = bt.Cerebro()
        
            tickers = {"FB": 0.25, "MSFT": 0.4, "TSLA": 0.35}
        
            for ticker, target in tickers.items():
                data = bt.feeds.YahooFinanceData(
                    dataname=ticker,
                    timeframe=bt.TimeFrame.Days,
                    fromdate=datetime.datetime(2019, 1, 1),
                    todate=datetime.datetime(2020, 12, 31),
                    reverse=False,
                )
                data.target = target
                cerebro.adddata(data, name=ticker)
        
            cerebro.addstrategy(Strategy)
        
            # Execute
            cerebro.run()
        

        RunBacktest.com

        1 Reply Last reply Reply Quote 1
        • 1 / 1
        • First post
          Last post
        Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors