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?