/// <summary> /// Выставить заявку /// </summary> /// <param name="transaction"> /// Транзакция /// </param> /// <param name="order"> /// IB-заявка /// </param> public async Task PlaceOrderAsync(NewOrderTransaction transaction, IBOrder order) { var instrument = transaction.Instrument; var contract = await connector.ContractContainer.GetContractAsync(instrument, null); if (contract == null) { throw new TransactionRejectedException($"Details of contract {instrument.Code} are not available"); } using (orderInfoContainerLock.Lock()) { var tickerId = NextTickerId(); order.OrderId = tickerId; // Сохраняем заявку по ее тикеру var orderInfo = new OrderInfo(order, transaction.Instrument) { State = OrderState.New, NewOrderTransactionId = transaction.TransactionId }; orderInfoContainer.StoreByTickerId(tickerId, orderInfo, true); Socket.placeOrder(tickerId, contract, order); } }
public void Visit(NewOrderTransaction transaction) { var symbol = adapter.ResolveSymbolAsync(transaction.Instrument).Result; if (symbol == null) { Logger.Error().Print($"Unable to resolve symbol for {transaction.Instrument}"); return; } var newTransactionId = IncTransId(); var qlTrans = new QLTransaction { ACTION = ACTION.NEW_ORDER, ACCOUNT = transaction.Account, CLIENT_CODE = transaction.Account + @"//" + transaction.Comment, COMMENT = transaction.Comment, SECCODE = symbol, CLASSCODE = symbol.Length >= 5 ? "SPBOPT" : "SPBFUT", EXECUTION_CONDITION = EXECUTION_CONDITION.PUT_IN_QUEUE, OPERATION = transaction.Operation == OrderOperation.Buy ? OPERATION.B : OPERATION.S, QUANTITY = transaction.Quantity.ToString(), PRICE = transaction.Price.ToString("0.######"), TRANS_ID = newTransactionId.ToString(), TYPE = TYPE.L, }; Logger.Debug().PrintFormat("Visit: {0}", qlTrans); adapter.SendMessage(qlTrans); container.PutTransaction(newTransactionId, transaction); }
/// <summary> /// Получить транзакцию на постоновку заявки по её идентификатору /// </summary> /// <param name="transId">Идентификатор транзакции</param> /// <returns></returns> public NewOrderTransaction GetNewOrderTransaction(long transId) { using (locker.ReadLock()) { NewOrderTransaction rValue = null; mapQuikTransIdOnNewOrderTransaction.TryGetValue(transId, out rValue); return(rValue); } }
/// <summary> /// Сохранить транзакцию на постановку заявки по её идентификатору /// </summary> /// <param name="id"></param> /// <param name="newOrderTransaction"></param> public void PutTransaction(long id, NewOrderTransaction newOrderTransaction) { using (locker.WriteLock()) { currentSeccionTransactionIds.Add(id); newOrderTransactionsWithoutReplies.Add(id); mapQuikTransIdOnNewOrderTransaction.Add(id, newOrderTransaction); } }
public static OrderParams GetOrderParams(this NewOrderTransaction transaction) { switch (transaction.ExecutionCondition) { case OrderExecutionCondition.FillOrKill: return(OrderParams.FOK); case OrderExecutionCondition.KillBalance: return(OrderParams.IOC); default: case OrderExecutionCondition.PutInQueue: return(OrderParams.PIQ); } }
/// <summary> /// Обработать транзакцию <see cref="NewOrderTransaction"/> /// </summary> /// <param name="transaction"> /// Транзакция для обработки /// </param> async void ITransactionVisitor.Visit(NewOrderTransaction transaction) { try { var order = new IBOrder { Account = transaction.Account, ClientId = connector.ClientId, Action = transaction.Operation == OrderOperation.Buy ? "BUY" : "SELL", TotalQuantity = (int)transaction.Quantity, OrderRef = !string.IsNullOrWhiteSpace(transaction.Comment) ? connector.IBOrderRouter.SessionUidInternal + transaction.Comment : connector.IBOrderRouter.SessionUidInternal, Transmit = true }; switch (transaction.Type) { case OrderType.Limit: order.OrderType = "LMT"; order.LmtPrice = (double)transaction.Price; break; case OrderType.Market: order.OrderType = "MKT"; break; default: throw new ArgumentOutOfRangeException("transaction.Type"); } switch (transaction.ExecutionCondition) { case OrderExecutionCondition.PutInQueue: order.Tif = "GTC"; /* Good till cancelled */ break; case OrderExecutionCondition.FillOrKill: order.Tif = "FOC"; /* Fill or cancel */ break; case OrderExecutionCondition.KillBalance: order.Tif = "IOC"; /* Immediate or cancel */ break; default: throw new ArgumentOutOfRangeException("transaction.ExecutionCondition"); } await connector.Adapter.PlaceOrderAsync(transaction, order); } catch (TransactionRejectedException exception) { Reject(transaction, exception.Message); } catch (Exception exception) { _Log.Error() .Print(exception, "Unable to send transaction", LogFields.Transaction(transaction), LogFields.Message(exception.Message)); Reject(transaction, "Transaction execution failed: {0}", exception.Message); } }
public async void Visit(NewOrderTransaction transaction) { try { //internal async Task<string> ResolveInstrumentAsync(Instrument instrument) //{ // var data = await instrumentConverter.ResolveInstrumentAsync(this, instrument); // return data?.Symbol; //} var data = await connector.ResolveInstrumentDataAsync(transaction.Instrument); if (data == null) { OnMessageReceived(TransactionReply.Rejected(transaction, $"Unable to get symbol of {transaction.Instrument}")); return; } if (!accounts.TryGetValue(transaction.Account, out var account)) { OnMessageReceived(TransactionReply.Rejected(transaction, $"Не найден счет {transaction.Account}")); return; } var order = new SpimexAdapter.FTE.Order { account = account.code, firm = account.firm, client = account.client, security = data.Symbol, board = data.Board, @type = transaction.GetOrderType(), @params = transaction.GetOrderParams(), buy_sell = transaction.GetOperation(), price = PriceHelper.FromPrice(transaction.Price), qty = transaction.Quantity, trn = transaction.TransactionId.ToString("N"), isMarketMaker = transaction.IsMarketMakerOrder, comment = transaction.Comment, }; var reply = await transClient.SendOrder(order); OnMessageReceived(TransactionReply.Accepted(transaction, reply.code)); OrderState?state = null; switch (reply.status) { case OrderStatus.CANCELED: state = OrderState.Cancelled; break; case OrderStatus.MATCHED: state = OrderState.Filled; break; case OrderStatus.FREEZED: case OrderStatus.QUEUED: state = reply.qty_executed > 0 ? OrderState.PartiallyFilled : OrderState.Active; break; } var oscm = new OrderStateChangeMessage { TransactionId = transaction.TransactionId, OrderExchangeId = reply.code, Quantity = order.qty, ActiveQuantity = reply.qtyLeft, FilledQuantity = reply.qty_executed, Price = PriceHelper.ToPrice(order.price), ChangeTime = DateTime.Now, State = state }; OnMessageReceived(oscm); } catch (FTEException e) { OnMessageReceived(TransactionReply.Rejected(transaction, e.Message)); } }
/// <summary> /// Постановка заявки /// </summary> private async void SendTransactionInternal(NewOrderTransaction transaction) { using (LogManager.Scope()) { try { // Получаем счет для транзакции int accountId; var hasAccountId = true; using (accountsLock.ReadLock()) { if (!accountIdsByCode.TryGetValue(transaction.Account, out accountId)) { hasAccountId = false; } } if (!hasAccountId) { OnMessageReceived(TransactionReply.Rejected( transaction, $"Account \"{transaction.Account}\" is unknown")); return; } // Получаем инструмент для транзации uint contractId; try { contractId = await instrumentResolver.GetContractIdAsync(transaction.Instrument); if (contractId == uint.MaxValue) { return; } } catch (OperationCanceledException) { OnMessageReceived(TransactionReply.Rejected( transaction, $"Instrument \"{transaction.Instrument.Code}\" is unknown")); return; } // Пребразовываем цену var price = instrumentResolver.ConvertPrice(contractId, transaction.Price); if (price == null) { OnMessageReceived(TransactionReply.Rejected(transaction, "Unable to convert price")); return; } // Формируем запрос var msg = new OrderRequest { new_order = new NewOrder { order = new WebAPI.Order { cl_order_id = transaction.TransactionId.ToString("N"), account_id = accountId, contract_id = contractId, side = (uint)ConvertionHelper.GetSide(transaction.Operation), order_type = (uint)ConvertionHelper.GetOrderType(transaction.Type), duration = (uint)ConvertionHelper.GetDuration(transaction.ExecutionCondition), limit_price = price.Value, qty = transaction.Quantity, is_manual = transaction.IsManual, user_attribute = { new UserAttribute { name = CommentAttributeName, value = transaction.Comment } } }, suspend = false }, request_id = adapter.GetNextRequestId() }; // Запоминаем заявку using (ordersLock.Lock()) { var order = new Order(transaction); ordersByTransactionId.Add(transaction.TransactionId, order); } // Запоминаем транзакцию StoreTransaction(msg.request_id, transaction); Logger.Debug().PrintFormat("Sending {0}", transaction); adapter.SendMessage(msg); } catch (Exception e) { Logger.Error().Print(e, $"Failed to send {transaction}"); OnMessageReceived(TransactionReply.Rejected(transaction, "Unable to send order")); } } }
void ITransactionVisitor.Visit(NewOrderTransaction transaction) { router.SendTransactionInternal(transaction); }
/// <summary> /// Обработать транзакцию <see cref="NewOrderTransaction"/> /// </summary> /// <param name="transaction"> /// Транзакция для обработки /// </param> async void ITransactionVisitor.Visit(NewOrderTransaction transaction) { InstrumentData instrumentData; try { instrumentData = await _settings.InstrumentConverter.ResolveInstrumentAsync(this, transaction.Instrument); } catch (Exception e) { _Log.Error().Print(e, $"Failed to resolve instrument {transaction.Instrument}"); SendMessage(TransactionReply.Rejected(transaction, "Failed to resolve instrument")); return; } if (instrumentData == null) { _Log.Error().Print($"Instrument {transaction.Instrument} hasn't been resolved"); SendMessage(TransactionReply.Rejected(transaction, "Instrument hasn't been resolved")); return; } var msg = new NewOrderSingle(); msg.TransactTime = new TransactTime(DateTime.UtcNow); msg.OrdType = new OrdType(OrdType.LIMIT); msg.Symbol = new Symbol(instrumentData.Symbol); msg.Account = new Account(transaction.Account); switch (transaction.ExecutionCondition) { case OrderExecutionCondition.PutInQueue: msg.TimeInForce = new TimeInForce(TimeInForce.DAY); break; case OrderExecutionCondition.FillOrKill: msg.TimeInForce = new TimeInForce(TimeInForce.FILL_OR_KILL); break; default: SendMessage(TransactionReply.Rejected(transaction, $"Unknown execution condition: {transaction.ExecutionCondition}")); return; } switch (transaction.Operation) { case OrderOperation.Buy: msg.Side = new Side(Side.BUY); break; case OrderOperation.Sell: msg.Side = new Side(Side.SELL); break; default: SendMessage(TransactionReply.Rejected(transaction, $"Unknown operation: {transaction.Operation}")); return; } switch (transaction.Type) { case OrderType.Limit: break; default: SendMessage(TransactionReply.Rejected(transaction, $"Unsupported order type: {transaction.Type}")); return; } msg.OrderQty = new OrderQty(transaction.Quantity); msg.Price = new Price(transaction.Price); var clOrderId = _newOrderTransactions.Add(transaction); msg.ClOrdID = new ClOrdID(clOrderId); if (!string.IsNullOrEmpty(transaction.Comment)) { var comment = transaction.Comment; comment = comment.Length > 20 ? comment.Substring(0, 20) : comment; msg.SecondaryClOrdID = new SecondaryClOrdID(comment); } _orders.SaveOrderParams(transaction.TransactionId, clOrderId, msg.Symbol.Obj, msg.OrderQty.Obj, msg.Side.Obj); SendFixMessage(msg); }
public static BuySell GetOperation(this NewOrderTransaction transaction) => transaction.Operation == OrderOperation.Buy ? BuySell.BUY : BuySell.SELL;
public static OrderType GetOrderType(this NewOrderTransaction transaction) => transaction.Type == Messages.OrderType.Market ? OrderType.MARKET : OrderType.LIMIT;