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/

    simple MACD strategy with Long, Short and close

    General Code/Help
    1
    2
    192
    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.
    • ABMAB
      ABMAB last edited by

      Hi all,

      I just switched from Matlab to python and even newer to Backtrader so my questions might seem obvious.

      My problem seems similar to this :
      https://community.backtrader.com/topic/2857/wanted-exit-long-and-open-short-on-the-same-bar-and-vice-versa
      and this :
      https://community.backtrader.com/topic/2797/self-close-does-not-clear-position

      The code below is a simple MACD strategy.
      The main part of the algo is under

      def next(self):
      
      # -*- coding: utf-8 -*-
      """
      
      """
      
      import backtrader as bt
      import argparse
      import backtrader.feeds as btFeeds
      import numpy as np
      import yfinance as yf
      import pandas as pd
      import talib
      
      
      
      class SimpleMACDStrat(bt.Strategy):
          
          def __init__(self):
              #Keep a reference to the "close" line in the data[0] dataseries
              self.dataclose = self.datas[0].close
              self.order = None
      
          def log(self, txt, dt=None):
              dt = dt or self.datas[0].datetime.date(0)
              print(f'{dt.isoformat()} {txt}')
              #Print date and close
              
          
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
      
              # Check if an order has been completed
              # Attention: broker could reject order if not enough cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log('LONG EXECUTED, %.2f' % order.executed.price)
                      
                  elif order.issell():
                      self.log('SELL EXECUTED, %.2f' % order.executed.price)
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('Order Canceled/Margin/Rejected')
      
              # Write down: no pending order
              self.order = None
              
      
          def next(self):
              self.log("Close: '{0}'" .format(self.data.adj_close[0]))
              print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0],
                                                       self.data.high[0],self.data.low[0],
                                     self.data.close[0],self.data.adj_close[0],
                                     self.data.volume[0],self.data.EMA_100[0], 
                                     self.data.RSI[0], self.data.CCI[0],
                                     self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0]))
      
              if self.order:
                  return
              
              if self.data.MACD_hist[0]>0:
                  if self.position.size<0 and self.data.MACD_hist[-1]<0  :
                           self.close()
                           self.log('CLOSE SHORT POSITION, %.2f' % self.dataclose[0])
             
                  elif self.position.size==0:
                          self.order=self.buy()
                          self.log('OPEN LONG POSITION, %.2f' % self.dataclose[0])
                          
                      
              elif self.data.MACD_hist[0]<0:
                  if self.position.size>0 and self.data.MACD_hist[-1]>0:
                           self.order=self.close()
                           self.log('CLOSE LONG POSITION, %.2f' % self.dataclose[0])
                                  
                  elif self.position.size==0:
                          self.order=self.sell()
                          self.log('OPEN SHORT POSITION, %.2f' % self.dataclose[0])
              print('')
              
      
      
      class BasicIndicatorsFeeded(btFeeds.PandasData):
          lines = ('Indexx', 'adj_close', 'EMA_100', 'RSI', 'CCI', 'MACD_macd', 'MACD_sign', 'MACD_hist',)
      
          params = ( ('Indexx', 0), ('adj_close', 5), ('volume', 6), 
                    ('EMA_100', 7), ('RSI', 8), ('CCI', 9),
                    ('MACD_macd', 10), ('MACD_sign', 11), ('MACD_hist', 12),)
      
      
      
      
      
      
      
      if __name__ == '__main__':
          
          cerebro = bt.Cerebro()
          
          #Add data feed to Cerebro
          data1 = yf.download("AAPL",start="2021-08-09", end="2021-12-21",group_by="ticker")
          data1.insert(0,'Indexx',' ')
          data1['Indexx']=range(len(data1))
          data1['EMA_100']=talib.EMA(data1['Adj Close'],100)
          data1['RSI']=talib.RSI(data1['Adj Close'],14)
          data1['CCI']=talib.CCI(data1['High'], data1['Low'], data1['Adj Close'], timeperiod=14)
          data1['MACD_macd']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[0]
          data1['MACD_sign']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[1]
          data1['MACD_hist']=talib.MACD(data1['Adj Close'], fastperiod=12, slowperiod=26, signalperiod=9)[2]
         # data1['Long_position']
          # Run Cerebro Engine
          cerebro.broker.setcash(8000000000)
          start_portfolio_value = cerebro.broker.getvalue()
          cerebro.addstrategy(SimpleMACDStrat)
          
          data = BasicIndicatorsFeeded(dataname=data1)
          cerebro.adddata(data)
         
          cerebro.run()
          cerebro.plot()
         # print(data1)
          print('-------------------')
          #print('%f' %data)
          # print(data)
          end_portfolio_value = cerebro.broker.getvalue()
          pnl = end_portfolio_value - start_portfolio_value
          print(f'Starting Portfolio Value: {start_portfolio_value:2f}')
          print(f'Final Portfolio Value: {end_portfolio_value:2f}')
          print(f'PnL: {pnl:.2f}')
      
      

      Here are the results :

      backtrader.png

      On 2021-11-10, macd_hist goes from postive to negative. We are expecting that the next day( 2021-11-11):

      a)the long position is closed and right after
      b)a short position is opened

      1)We see that a) is actually closed the same day. Isn't it supposed to happen the next ?
      2)Also a sell is executed the next day, which is not supposed to happen.

      Any suggestion for 1) and 2) would be more then welcome. Thanks.

      Abbe

      1 Reply Last reply Reply Quote 0
      • ABMAB
        ABMAB last edited by

        Btw, I'm aware the idea can be coded that way (only def next) :

            def next(self):
                #print('%f' % (self.datas[0].Indexxx[0])
                self.log("Close: '{0}'" .format(self.data.adj_close[0]))
                print('%f %f %f %f %f %f %f %f %f %f %f %f %f' % (self.data.Indexx[0],self.data.open[0],
                                                         self.data.high[0],self.data.low[0],
                                       self.data.close[0],self.data.adj_close[0],
                                       self.data.volume[0],self.data.EMA_100[0], 
                                       self.data.RSI[0], self.data.CCI[0],
                                       self.data.MACD_macd[0],self.data.MACD_sign[0],self.data.MACD_hist[0]))
                if self.order:
                    return
         
        
                print(self.position)
                if self.data.MACD_hist[0]>0 and self.data.MACD_hist[-1]<0:
                             self.order=self.buy()
                             self.log('CLOSE SHORT POSITION and open long, %.2f' % self.dataclose[0])
                        
                        
                if self.data.MACD_hist[0]<0 and self.data.MACD_hist[-1]>0:
        
                             self.order=self.sell()
                             self.log('CLOSE LONG POSITION and open short, %.2f' % self.dataclose[0])
                        
                print('')
        

        But I really want to separate the

        self.close()
        

        and for instance the

        self.buy()
        

        That would allow me later to use different conditions for closing a position and opening one.

        Thanks a lot for any inputs, ideas, remarks.

        Abbe

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