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 strategy works?

    General Code/Help
    2
    8
    319
    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.
    • Z
      ZooM last edited by

      I Have a code

      from datetime import datetime
      import backtrader as bt
      from BackTraderQuik.QKStore import QKStore
      from BackTraderQuik.QKData import QKData
      import talib

      class Trend_Pull_Back_SBER(bt.Strategy):

      params = (
          ('period', 230),
          ('PullBack', 4),
          ('ticker_id', 'SPBFUT.SRZ1')
      )
      
      def log(self, txt, dt=None):
          """Вывод строки с датой на консоль"""
          dt = bt.num2date(self.datas[0].datetime[0]) if dt is None else dt # Заданная дата или дата текущего бара
          print(f'{dt.strftime("%d.%m.%Y %H:%M")}, {txt}')  # Выводим дату и время с заданным текстом на консоль
      
      
      def __init__(self):
          """Инициализация торговой системы"""
          self.DataClose = self.datas[0].close
          self.isLive = False  # Сначала будут приходить исторические данные
          self.order = None  # Заявка
      
      def next(self):
          """Получение следующего исторического/нового бара"""
          if not self.isLive:
              return
      
          if self.order and self.order.status == bt.Order.Submitted:  # Если заявка не исполнена (отправлена брокеру)
              return  # то выходим, дальше не продолжаем
      
          if self.params.ticker_id==self.datas[0]._dataname:
              
              self.log(f'Position = {self.getposition(data = self.datas[0])}')
              self.log(f'DataClose SBER = {self.DataClose[0]:.5f}')
      
      
              if not self.getposition(data = self.datas[0]):
                  self.log(f'NO Position')
      
              else:
                  self.log(f'Have Position')
                  
      
      def notify_data(self, data, status, *args, **kwargs):
          """Изменение статуса приходящих баров"""
          dataStatus = data._getstatusname(status)  # Получаем статус (только при LiveBars=True)
          print(dataStatus + ' 1')  # Не можем вывести в лог, т.к. первый статус DELAYED получаем до первого бара (и его даты)
          self.isLive = dataStatus == 'LIVE'
      
      def notify_order(self, order):
          """Изменение статуса заявки"""
          if order.status in [order.Submitted, order.Accepted]:  # Если заявка не исполнена (отправлена брокеру или принята брокером)
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Canceled]:  # Если заявка отменена
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Completed]:  # Если заявка исполнена
              if order.isbuy():  # Заявка на покупку
                  self.log(f'Bought @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
              elif order.issell():  # Заявка на продажу
                  self.log(f'Sold @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
          elif order.status in [order.Margin, order.Rejected]:  # Нет средств, или заявка отклонена брокером
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
          self.order = None  # Этой заявки больше нет
      
      def notify_trade(self, trade):
          """Изменение статуса позиции"""
          if not trade.isclosed:  # Если позиция не закрыта
              return  # то статус позиции не изменился, выходим, дальше не продолжаем
          self.log(f'Trade Profit, Gross={trade.pnl:.2f}, NET={trade.pnlcomm:.2f}')
      

      class Trend_Hilbert_GOLD(bt.Strategy):

      params = (  # Параметры торговой системы
          ('ticker_id', 'SPBFUT.GDZ1'),
      )
      
      def log(self, txt, dt=None):
          """Вывод строки с датой на консоль"""
          dt = bt.num2date(self.datas[2].datetime[0]) if dt is None else dt # Заданная дата или дата текущего бара
          print(f'{dt.strftime("%d.%m.%Y %H:%M")}, {txt}')  # Выводим дату и время с заданным текстом на консоль
      
      def __init__(self):
          """Инициализация торговой системы"""
          self.DataClose = self.datas[2].close
          self.isLive = False  # Сначала будут приходить исторические данные
          self.order = None  # Заявка
      
      def next(self):
          """Получение следующего исторического/нового бара"""
          if not self.isLive:
              return
      
          if self.order and self.order.status == bt.Order.Submitted:  # Если заявка не исполнена (отправлена брокеру)
              return  # то выходим, дальше не продолжаем
      
          if self.params.ticker_id==self.datas[2]._dataname:
      
              if not self.getposition(data = self.datas[2]):
          	pass
              else:
                  pass 
                
      
      def notify_data(self, data, status, *args, **kwargs):
          """Изменение статуса приходящих баров"""
          dataStatus = data._getstatusname(status)  # Получаем статус (только при LiveBars=True)
          print(dataStatus + ' 2')  # Не можем вывести в лог, т.к. первый статус DELAYED получаем до первого бара (и его даты)
          self.isLive = dataStatus == 'LIVE'
      
      def notify_order(self, order):
          """Изменение статуса заявки"""
          if order.status in [order.Submitted, order.Accepted]:  # Если заявка не исполнена (отправлена брокеру или принята брокером)
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Canceled]:  # Если заявка отменена
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Completed]:  # Если заявка исполнена
              if order.isbuy():  # Заявка на покупку
                  self.log(f'Bought @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
              elif order.issell():  # Заявка на продажу
                  self.log(f'Sold @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
          elif order.status in [order.Margin, order.Rejected]:  # Нет средств, или заявка отклонена брокером
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
          self.order = None  # Этой заявки больше нет
      
      def notify_trade(self, trade):
          """Изменение статуса позиции"""
          if not trade.isclosed:  # Если позиция не закрыта
              return  # то статус позиции не изменился, выходим, дальше не продолжаем
          self.log(f'Trade Profit, Gross={trade.pnl:.2f}, NET={trade.pnlcomm:.2f}')
      

      class Trend_Hilbert_GAZP(bt.Strategy):

      params = (  # Параметры торговой системы
          ('ticker_id', 'SPBFUT.GZZ1'),
      )
      
      def log(self, txt, dt=None):
          """Вывод строки с датой на консоль"""
          dt = bt.num2date(self.datas[3].datetime[0]) if dt is None else dt # Заданная дата или дата текущего бара
          print(f'{dt.strftime("%d.%m.%Y %H:%M")}, {txt}')  # Выводим дату и время с заданным текстом на консоль
      
      def __init__(self):
          """Инициализация торговой системы"""
          self.DataClose = self.datas[3].close
          self.isLive = False  # Сначала будут приходить исторические данные
          self.order = None  # Заявка
      
      
      def next(self):
          """Получение следующего исторического/нового бара"""
          if not self.isLive:
              return
      
          if self.order and self.order.status == bt.Order.Submitted:  # Если заявка не исполнена (отправлена брокеру)
              return  # то выходим, дальше не продолжаем
          
          if self.params.ticker_id==self.datas[3]._dataname:
      
          
              if not self.getposition(data = self.datas[3]):
                  self.log(f'NO Position GAZP')             
              else:
                  self.log(f'Position GAZP')
                
                      
      
      def notify_data(self, data, status, *args, **kwargs):
          """Изменение статуса приходящих баров"""
          dataStatus = data._getstatusname(status)  # Получаем статус (только при LiveBars=True)
          print(dataStatus + ' 3')  # Не можем вывести в лог, т.к. первый статус DELAYED получаем до первого бара (и его даты)
          self.isLive = dataStatus == 'LIVE'
      
      def notify_order(self, order):
          """Изменение статуса заявки"""
          if order.status in [order.Submitted, order.Accepted]:  # Если заявка не исполнена (отправлена брокеру или принята брокером)
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Canceled]:  # Если заявка отменена
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
              return  # то выходим, дальше не продолжаем
      
          if order.status in [order.Completed]:  # Если заявка исполнена
              if order.isbuy():  # Заявка на покупку
                  self.log(f'Bought @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
              elif order.issell():  # Заявка на продажу
                  self.log(f'Sold @{order.executed.price:.2f}, Cost={order.executed.value:.2f}, Comm={order.executed.comm:.2f}')
          elif order.status in [order.Margin, order.Rejected]:  # Нет средств, или заявка отклонена брокером
              self.log(f'Order Status: {order.getstatusname()}. TransId={order.ref}')
          self.order = None  # Этой заявки больше нет
      
      def notify_trade(self, trade):
          """Изменение статуса позиции"""
          if not trade.isclosed:  # Если позиция не закрыта
              return  # то статус позиции не изменился, выходим, дальше не продолжаем
          self.log(f'Trade Profit, Gross={trade.pnl:.2f}, NET={trade.pnlcomm:.2f}')
      

      if name == 'main': # Точка входа при запуске этого скрипта
      cerebro = bt.Cerebro() # Инициируем "движок" BackTrader

      symbol0 = 'SPBFUT.SRZ1' # 0 1-resampledata
      symbol2 = 'SPBFUT.GDZ1'
      symbol3 = 'SPBFUT.GZZ1'
      
      cerebro.addstrategy(Trend_Pull_Back_SBER, ticker_id ='SPBFUT.SRZ1')  # Добавляем торговую систему
      cerebro.addstrategy(Trend_Hilbert_GOLD, ticker_id ='SPBFUT.GDZ1')  # Добавляем торговую систему
      cerebro.addstrategy(Trend_Hilbert_GAZP, ticker_id ='SPBFUT.GZZ1')  # Добавляем торговую систему
      
      store = QKStore()  # Хранилище QUIK
      broker = store.getbroker(ClientCode = '', FirmId = ' ', TradeAccountId= ' ' )  # Брокер со счетом по умолчанию (срочный рынок РФ)
      
      cerebro.setbroker(broker)  # Устанавливаем брокера
      data0 = QKData(dataname=symbol0, timeframe=bt.TimeFrame.Minutes, compression=60, fromdate=datetime(2021, 5, 5, 19, 0), LiveBars=True)  # Исторические и новые минутные бары за все время
      cerebro.adddata(data0)  # Добавляем данные
      cerebro.resampledata(data0, timeframe = bt.TimeFrame.Days , compression = 1)
      data2 = QKData(dataname=symbol2, timeframe=bt.TimeFrame.Minutes, compression=60, fromdate=datetime(2021, 7, 5, 19, 0), LiveBars=True)  # Исторические и новые минутные бары за все время
      data3 = QKData(dataname=symbol3, timeframe=bt.TimeFrame.Minutes, compression=60, fromdate=datetime(2021, 7, 5, 19, 0), LiveBars=True)  # Исторические и новые минутные бары за все время
      cerebro.adddata(data2)  # Добавляем данные
      cerebro.adddata(data3)  # Добавляем данные
      
      cerebro.run()  # Запуск торговой системы
      

      And the result

      DELAYED 1
      DELAYED 2
      DELAYED 3
      CONNECTED 1
      CONNECTED 2
      CONNECTED 3
      DELAYED 1
      DELAYED 2
      DELAYED 3
      CONNECTED 1
      CONNECTED 2
      CONNECTED 3
      DELAYED 1
      DELAYED 2
      DELAYED 3
      CONNECTED 1
      CONNECTED 2
      CONNECTED 3
      DISCONNECTED 1
      DISCONNECTED 2
      DISCONNECTED 3
      DISCONNECTED 1
      DISCONNECTED 2
      DISCONNECTED 3
      DISCONNECTED 1
      DISCONNECTED 2
      DISCONNECTED 3
      LIVE 1
      LIVE 2
      LIVE 3
      24.09.2021 16:00, Position = --- Position Begin

      • Size: 200
      • Price: 330.38
      • Price orig: 330.38
      • Closed: 0
      • Opened: 0
      • Adjbase: None
        --- Position End
        24.09.2021 16:00, DataClose SBER = 330.38000
        24.09.2021 16:00, Have Position
        24.09.2021 17:00, Position GAZP
        LIVE 1
        LIVE 2
        LIVE 3
        LIVE 1
        LIVE 2
        LIVE 3
        24.09.2021 17:00, Position = --- Position Begin
      • Size: 200
      • Price: 330.38
      • Price orig: 330.38
      • Closed: 0
      • Opened: 0
      • Adjbase: None
        --- Position End
        24.09.2021 17:00, DataClose SBER = 331.09000
        24.09.2021 17:00, Have Position
        24.09.2021 17:00, Position GAZP
        24.09.2021 17:00, Position = --- Position Begin
      • Size: 200
      • Price: 330.38
      • Price orig: 330.38
      • Closed: 0
      • Opened: 0
      • Adjbase: None
        --- Position End
        24.09.2021 17:00, DataClose SBER = 331.09000
        24.09.2021 17:00, Have Position
        24.09.2021 17:00, Position GAZP
        24.09.2021 18:00, Position = --- Position Begin
      • Size: 200
      • Price: 330.38
      • Price orig: 330.38
      • Closed: 0
      • Opened: 0
      • Adjbase: None
        --- Position End
        24.09.2021 18:00, DataClose SBER = 330.35000
        24.09.2021 18:00, Have Position
        24.09.2021 18:00, Position GAZP

      I removed some of the executable code in the next () construct. This is for simple.
      My question is:
      Why is the strategy executed 3 times? (I have a DELAYED take 1 2 3 3 times)
      How to correctly implement a strategy with trading multiple strategies?

      vladisld 1 Reply Last reply Reply Quote 0
      • vladisld
        vladisld @ZooM last edited by

        @zoom Each added strategy has access to every data - so each strategy got notified about every change in every data.

        So in your case, you've added 3 datas and 3 strategies. For each data's state change all 3 strategies will be notified. That's explain the multiple 'DELAYED' events.

        Regarding the proper implementation of multiple strategies, each using its own data but all of them running against same broker. There are multiple ways to implement it - and your implementation is almost right.

        Each strategy may get the data name as a parameter. The strategy may then calculate the datas index using this parameter. (in your case the datas index is hardcoded inside the strategy - which is suboptimal and will not scale)

        Z 1 Reply Last reply Reply Quote 1
        • Z
          ZooM @vladisld last edited by

          @vladisld
          thanks for the answer.

          My program is printing Position, DataClose SBER, Have Position, Position GAZP. Why is data0 printed three times? Each strategy has a filter for input data. That is, if data0, data1, data 2 enters the Trend_Pull_Back_SBER strategy, only data with data0 should be printed, the same with the Trend_Hilbert_GAZP and Trend_Hilbert_GOLD strategies.
          I understand it should be like this, the Trend_Pull_Back_SBER strategy is called three times with data data0, data1, data 2, but the data should be printed once.

          You could show an example of the optimal implementation of the strategy you are talking about.

          vladisld 1 Reply Last reply Reply Quote 0
          • vladisld
            vladisld @ZooM last edited by vladisld

            @zoom said in How strategy works?:

            Why is data0 printed three times? Each strategy has a filter for input data

            Are you talking about this code?:

            if self.params.ticker_id==self.datas[0]._dataname
            

            It just checks that datas[0] indeed matches the parameter passed to the strategy. This has nothing to do with filtering which data is changed upon next call. So each time the next is called for different datas, the check above is always true.

            Z 1 Reply Last reply Reply Quote 0
            • Z
              ZooM @vladisld last edited by

              @vladisld
              thanks for the answer.

              How would you do filtering? How would you make your trading system more optimal?

              vladisld 1 Reply Last reply Reply Quote 0
              • vladisld
                vladisld @ZooM last edited by

                @zoom Please take a look at the following post, it seems the similar problem was discussed there:

                https://community.backtrader.com/topic/1703/repeated-days-in-multi-symbol-strategy/2

                Z 1 Reply Last reply Reply Quote 1
                • Z
                  ZooM @vladisld last edited by

                  @vladisld
                  if we use this, we solve the data sync problem and we don't have data duplication.

                  if self.datas[0].datetime[0] == self.datas[1].datetime[0]

                  vladisld 1 Reply Last reply Reply Quote 0
                  • vladisld
                    vladisld @ZooM last edited by

                    @zoom said in How strategy works?:

                    @vladisld
                    if we use this, we solve the data sync problem and we don't have data duplication.

                    if self.datas[0].datetime[0] == self.datas[1].datetime[0]

                    hmm - I'm not sure. For backtest scenario it may depend on exact timestamps used by each data feed. In case they are different or offset (even slightly) - the above statement may always be false. For live trading scenario this may be even more emphasized.

                    IMHO the solution provided in the referenced post (above) seems pretty solid. If the length of each data is tracked inside the strategy - one may definitely state which data was updated in each next call.

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