def CreateOrder(self, owner, data, size, price=None, plimit=None, exectype=None, valid=None, oco=None, parent=None, transmit=True, IsBuy=True, **kwargs): """ Создание заявки Привязка параметров счета и тикера Обработка связанных и родительской/дочерних заявок """ order = BuyOrder(owner=owner, data=data, size=size, price=price, pricelimit=plimit, exectype=exectype, valid=valid, oco=oco, parent=parent, transmit=transmit) if IsBuy \ else SellOrder(owner=owner, data=data, size=size, price=price, pricelimit=plimit, exectype=exectype, valid=valid, oco=oco, parent=parent, transmit=transmit) # Заявка на покупку/продажу order.ref = self.newTransId # Ставим номер транзакции в заявку self.newTransId += 1 # Увеличиваем номер транзакции для будущих заявок order.addcomminfo( self.getcommissioninfo(data) ) # По тикеру выставляем комиссии в заявку. Нужно для исполнения заявки в BackTrader order.addinfo( **kwargs ) # Передаем в заявку все дополнительные свойства из брокера, в т.ч. ClientCode и TradeAccountId classCode, secCode = self.store.DataNameToClassSecCode( data._dataname ) # Из названия тикера получаем код площадки и тикера order.addinfo( ClassCode=classCode, SecCode=secCode) # Код площадки ClassCode и тикера SecCode si = self.store.GetSecurityInfo( classCode, secCode) # Получаем параметры тикера (min_price_step, scale) if si is None: # Если тикер не найден print( f'Постановка заявки {order.ref} по тикеру {classCode}.{secCode} отменена. Тикер не найден' ) order.reject() # то отменяем заявку return order # Возвращаем отмененную заявку order.addinfo(Slippage=float(si['min_price_step']) * self.store.p.StopSteps ) # Размер проскальзывания в деньгах Slippage order.addinfo(Scale=int( si['scale'])) # Кол-во значащих цифр после запятой Scale if oco is not None: # Если есть связанная заявка self.store.ocos[ order.ref] = oco.ref # то заносим в список связанных заявок if not transmit or parent is not None: # Для родительской/дочерних заявок parentRef = getattr( order.parent, 'ref', order.ref ) # Номер транзакции родительской заявки или номер заявки, если родительской заявки нет if order.ref != parentRef and parentRef not in self.store.pcs: # Если есть родительская заявка, но она не найдена в очереди родительских/дочерних заявок print( f'Постановка заявки {order.ref} по тикеру {classCode}.{secCode} отменена. Родительская заявка не найдена' ) order.reject() # то отменяем заявку return order # Возвращаем отмененную заявку pcs = self.store.pcs[parentRef] # В очередь к родительской заявке pcs.append(order) # добавляем заявку (родительскую или дочернюю) if transmit: # Если обычная заявка или последняя дочерняя заявка if parent is None: # Для обычных заявок return self.store.PlaceOrder( order) # Отправляем заявку на рынок else: # Если последняя заявка в цепочке родительской/дочерних заявок self.notifs.append(order.clone( )) # Удедомляем брокера о создании новой заявки return self.store.PlaceOrder( order.parent) # Отправляем родительскую заявку на рынок # Если не последняя заявка в цепочке родительской/дочерних заявок (transmit=False) return order # то возвращаем созданную заявку со статусом Created. На рынок ее пока не ставим
def PlaceOrder(self, ClientCode, TradeAccountId, owner, data, size, price=None, plimit=None, exectype=None, valid=None, oco=None, CommInfo=None, IsBuy=True, **kwargs): # TODO: Организовать работу группы заявок с 'parent' и 'transmit' order = BuyOrder(owner=owner, data=data, size=size, price=price, pricelimit=plimit, exectype=exectype, oco=oco) if IsBuy \ else SellOrder(owner=owner, data=data, size=size, price=price, pricelimit=plimit, exectype=exectype, oco=oco) # Заявка на покупку/продажу order.addinfo(**kwargs) # Передаем все дополнительные параметры order.addcomminfo( CommInfo ) # По тикеру выставляем комиссии в заявку. Нужно для исполнения заявки в BackTrader classCode, secCode = self.DataNameToClassSecCode( data._dataname ) # Из названия тикера получаем код площадки и тикера size = self.SizeToLots(classCode, secCode, size) # Размер позиции в лотах if order.exectype == Order.Market: # Для рыночных заявок if classCode == 'SPBFUT': # Для рынка фьючерсов lastPrice = float( self.qpProvider.GetParamEx(classCode, secCode, 'LAST') ['data']['param_value']) # Последняя цена сделки price = lastPrice * 1.001 if IsBuy else lastPrice * 0.999 # Наихудшая цена (на 0.1% хуже последней цены). Все равно, заявка исполнится по рыночной цене else: # Для остальных рынков price = 0 # Цена рыночной заявки должна быть нулевой else: # Для остальных заявок price = self.BTToQKPrice( classCode, secCode, price) # Переводим цену из BackTrader в QUIK scale = int(self.GetSecurityInfo( classCode, secCode)['scale']) # Кол-во значащих цифр после запятой price = round(price, scale) # Округляем цену до кол-ва значащих цифр if price.is_integer( ): # Целое значение цены мы должны отправлять без десятичных знаков price = int(price) # поэтому, приводим такую цену к целому числу transaction = { # Все значения должны передаваться в виде строк 'TRANS_ID': str(self.newTransId), # Номер транзакции задается клиентом 'CLIENT_CODE': ClientCode, # Код клиента. Для фьючерсов его нет 'ACCOUNT': TradeAccountId, # Счет 'CLASSCODE': classCode, # Код площадки 'SECCODE': secCode, # Код тикера 'OPERATION': 'B' if IsBuy else 'S', # B = покупка, S = продажа 'PRICE': str(price), # Цена исполнения 'QUANTITY': str(size) } # Кол-во в лотах if order.exectype in [Order.Stop, Order.StopLimit]: # Для стоп заявок transaction['ACTION'] = 'NEW_STOP_ORDER' # Новая стоп заявка transaction['STOPPRICE'] = str(price) # Стоп цена срабатывания slippage = float( self.GetSecurityInfo(classCode, secCode)['min_price_step'] ) * self.StopSteps # Размер проскальзывания в деньгах if slippage.is_integer( ): # Целое значение проскальзывания мы должны отправлять без десятичных знаков slippage = int( slippage ) # поэтому, приводим такое проскальзывание к целому числу if plimit is not None: # Если задана лимитная цена исполнения limitPrice = plimit # то ее и берем elif IsBuy: # Если цена не задана, и покупаем limitPrice = price + slippage # то будем покупать по большей цене в размер проскальзывания else: # Если цена не задана, и продаем limitPrice = price - slippage # то будем продавать по меньшей цене в размер проскальзывания transaction['PRICE'] = str(limitPrice) # Лимитная цена исполнения expiryDate = 'GTC' # По умолчанию будем держать заявку до отмены GTC = Good Till Cancelled if valid in [Order.DAY, 0]: # Если заявка поставлена на день expiryDate = 'TODAY' # то будем держать ее до окончания текущей торговой сессии elif isinstance(valid, date): # Если заявка поставлена до даты expiryDate = valid.strftime( '%Y%m%d') # то будем держать ее до указанной даты transaction[ 'EXPIRY_DATE'] = expiryDate # Срок действия стоп заявки else: # Для рыночных или лимитных заявок transaction[ 'ACTION'] = 'NEW_ORDER' # Новая рыночная или лимитная заявка transaction[ 'TYPE'] = 'L' if order.exectype == Order.Limit else 'M' # L = лимитная заявка (по умолчанию), M = рыночная заявка order.ref = self.newTransId # Ставим номер транзакции в заявку self.newTransId += 1 # Увеличиваем номер транзакции для будущих заявок if oco is not None: # Если есть связанная заявка self.ocos[ order.ref] = oco.ref # то заносим в список родительских заявок self.qpProvider.SendTransaction( transaction) # Отправляем транзакцию на рынок order.submit(self) # Переводим заявку в статус Order.Submitted self.orders[order.ref] = order # Сохраняем в списке заявок return order # Возвращаем заявку