/// <summary> /// Обработка неудачной транзакции /// </summary> /// <param name="message"></param> private void ProcessFailedTransactionReply(QLTransactionReply message) { var newOrderTransaction = container.GetNewOrderTransaction(message.trans_id); var killOrderTransaction = container.GetKillOrderTransactionByTransId(message.trans_id); var modifyOrderTransaction = container.GetModifyOrderTransactionByTransId(message.trans_id); switch (message.status) { case 13: // отвергнута как кросс сделка Logger.Error().Print("Transaction failed, due to potential cross trade", LogFields.Message(message)); break; default: Logger.Error().Print("Transaction failed", LogFields.Message(message)); break; } OnMessageReceived(message: new TransactionReply { Success = message.Successful, Message = message.result_msg, TransactionId = newOrderTransaction?.TransactionId ?? killOrderTransaction?.TransactionId ?? modifyOrderTransaction?.TransactionId ?? Guid.Empty }); container.RemoveProcessedPendingReply(message); }
/// <summary> /// Обработка отказа по заявке /// </summary> private void OrderRequestRejectReceived(AdapterEventArgs <OrderRequestReject> args) { args.MarkHandled(); var message = $"[{args.Message.reject_code}] {args.Message.text_message}"; Logger.Error().Print("CQG order rejected", LogFields.Message(message)); TrySendTransactionReplyRejected(args.Message.request_id, message); }
/// <summary> /// Обработчик события изменения статуса подписки /// </summary> private void TradeSubscriptionStatusReceived(AdapterEventArgs <TradeSubscriptionStatus> args) { args.MarkHandled(); Logger.Debug().Print( "Trade subscription status received", LogFields.Id(args.Message.id), LogFields.Status((TradeSubscriptionStatus.StatusCode)args.Message.status_code), LogFields.Message(args.Message.text_message) ); }
private static void MarketDataSubscriptionStatusReceived(AdapterEventArgs <MarketDataSubscriptionStatus> args) { args.MarkHandled(); _Log.Debug().Print( $"Subscription status for contract #{args.Message.contract_id}: Level={1}, Status={2}. {3}", LogFields.Level((MarketDataSubscription.Level)args.Message.level), LogFields.Status((MarketDataSubscriptionStatus.StatusCode)args.Message.status_code), LogFields.Message(args.Message.text_message) ); }
private async Task HandleAsync(QLFill message) { try { Logger.Debug().Print($"Handle(QLFill): {message}"); var instrument = await adapter.ResolveInstrumentAsync(message.sec_code); if (instrument == null) { Logger.Error().Print($"Unable to resolve instrument for {message.sec_code}"); return; } // если заявка отправлялась в текущей сессии работы программы, то нужно убедиться, что oscm по ней уже отправлялся if (container.IsCurrentSessionOrder(message.order_num)) { var lastOscm = container.GetLastOrderStateChangeForOrderId(message.order_num); if (lastOscm == null) { Logger.Debug() .Print("Handle(QLFill): Fill will be processed later, no OSCM received", LogFields.Message(message)); container.PutPendingFill(message); return; } } else if (container.HasUnrepliedTransactions()) { Logger.Debug() .Print($"Handle(QLFill): Fill will be processed later, there are unreplied transactions", LogFields.Message(message)); container.PutPendingFill(message); return; } OnMessageReceived(new FillMessage { Instrument = instrument, Account = message.account, Quantity = (uint)message.qty, ClientCode = message.account, ExchangeId = message.trade_num.ToString(), ExchangeOrderId = message.order_num.ToString(), Price = message.price, Operation = message.Operation, DateTime = message.Time }); } catch (Exception e) { Logger.Error().Print(e, $"Failed to handle {message}"); } }
/// <summary> /// Обработка результата логина /// </summary> private void Handle(LogonResult msg) { var resultCode = (LogonResult.ResultCode)msg.result_code; switch (resultCode) { case LogonResult.ResultCode.SUCCESS: Log.Info().Print($"Connected to CQG with login {settings?.Username}"); if (!DateTime.TryParseExact( msg.base_time, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out baseTime)) { baseTime = DateTime.UtcNow; Log.Warn().Print($"Unable to parse base_time = msg.base_time. Order and fill timestamps might be corrupted. Historical data won't work at all."); } //[ENABLE_CQGC_TRACE]WebApiTrace.SetBaseTime(baseTime); connectionStatus = ConnectionStatus.Connected; OnConnectionStatusChanged(connectionStatus); logoffEfent.Reset(); RequestInitializingData(); return; case LogonResult.ResultCode.FAILURE: break; case LogonResult.ResultCode.NO_ONETIME_PASSWORD: break; case LogonResult.ResultCode.PASSWORD_EXPIRED: return; case LogonResult.ResultCode.CONCURRENT_SESSION: break; case LogonResult.ResultCode.REDIRECTED: break; } Log.Error().Print("Unable to log in", LogFields.Result(resultCode), LogFields.Message(msg.text_message)); connectionStatus = ConnectionStatus.Terminated; OnConnectionStatusChanged(connectionStatus); using (socketLock.Lock()) { Log.Debug().Print("Closing CQG socket"); socket?.Close(); } }
/// <summary> /// Сохранить сделку, требующую отложенной обработки /// </summary> /// <param name="message"></param> public void PutPendingFill(QLFill message) { log.Debug().Print("Postpone fill processing", LogFields.Message(message)); using (locker.WriteLock()) { if (pendingFills.ContainsKey(message.trade_num)) { log.Warn().Print($"Fill duplicate received", LogFields.Id(message.trade_num)); return; } pendingFills[message.trade_num] = message; } }
/// <summary> /// Предварительная обработка ответа на транзакцию /// </summary> /// <param name="message"></param> private void PreprocessTransactionReply(QLTransactionReply message) { if (!string.IsNullOrEmpty(message.result_msg) && ( message.result_msg.Contains("Вы не можете снять данную заявку") || message.result_msg.Contains("Не найдена заявка для удаления") )) { Logger.Warn().Print( $"Failed kill transaction will be treated as successfull, status will be changed from {message.status} to 3", LogFields.Message(message) ); message.status = 3; } }
public void Send(Transaction transaction) { try { transaction.Accept(this); } 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); } }
private void MarketDataNotResolved(AdapterEventArgs <InformationReport> args) { ResolutionRequest request; using (resolutionRequestsLock.Lock()) { if (!resolutionRequestsById.TryGetValue(args.Message.id, out request)) { return; } resolutionRequestsById.Remove(request.Id); resolutionRequestsByInstrument.Remove(request.Instrument); } request.Reject(); args.MarkHandled(); _Log.Error().Print("Failed to resolve instrument", LogFields.RequestId(request.Id), LogFields.Message(args.Message.text_message)); }
/// <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); } }
/// <summary> /// Положить OSCM в отложенную обработку /// </summary> /// <param name="oscm"></param> public void PutPendingOrderStateChange(QLOrderStateChange oscm) { log.Info().Print("Postpone OSCM processing, there are transactions without replies.", LogFields.Message(oscm)); List <QLOrderStateChange> oscms; using (locker.WriteLock()) { if (!mapOrderIdOnPendingOrderStateChange.TryGetValue(oscm.order_num, out oscms)) { mapOrderIdOnPendingOrderStateChange[oscm.order_num] = oscms = new List <QLOrderStateChange>(); } oscms.Add(oscm); } }
/// <summary> /// Обработка успешной транзакции /// </summary> private void ProcessSuccessfulTransactionReply(QLTransactionReply message) { var newOrderTransaction = container.GetNewOrderTransaction(message.trans_id); var killOrderTransaction = container.GetKillOrderTransactionByTransId(message.trans_id); var modifyOrderTransaction = container.GetModifyOrderTransactionByTransId(message.trans_id); if (newOrderTransaction == null && killOrderTransaction == null && modifyOrderTransaction == null) { Logger.Warn().Print("TRANS_REPL received for transaction which wasn't sent from application", LogFields.Message(message)); container.RemoveProcessedPendingReply(message); return; } QLOrderStateChange lastState; if ((lastState = container.GetLastOrderStateChangeForTransactionId(message)) == null) { Logger.Debug().Print("Postpone TrRepl processing, no last OSCM found", LogFields.Message(message)); container.PutPendingTransactionReply(message); return; } // проверка соответствия последнего статуса типу транзакции // kill транзакция считатется завершённой, если заявка находится в одном из статусов ниже if (killOrderTransaction != null && lastState.State == OrderState.New) { Logger.Debug().Print($"Postpone KillTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message)); container.PutPendingTransactionReply(message); return; } // new транзакция считатется завершённой, если заявка не находится в одном из статусов ниже if (newOrderTransaction != null && (lastState.State == OrderState.New || lastState.State == OrderState.Undefined)) { Logger.Debug().Print($"Postpone NewTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message)); container.PutPendingTransactionReply(message); return; } if (modifyOrderTransaction != null && (lastState.State == OrderState.New || lastState.State == OrderState.Undefined || lastState.trans_id != message.trans_id)) { Logger.Debug().Print($"Postpone ModifyTrRepl processing. Last ord state is {lastState.State}", LogFields.Message(message)); Logger.Error().Print("Handle(TR): for modify transaction not implemented"); container.PutPendingTransactionReply(message); return; } if (killOrderTransaction != null) { var unfilledQuantity = ParseUnfilledQuantityFromTransactionReply(message); Logger.Debug().Print($"Handle(TR)[{message.trans_id}]: Create artifitial OSCM from successful kill transaction reply", LogFields.Message(message)); OnMessageReceived(new OrderStateChangeMessage { TransactionId = killOrderTransaction.TransactionId, ActiveQuantity = (uint)unfilledQuantity, OrderExchangeId = message.order_num.ToString(), //FilledQuantity = (uint)(message.filled), State = OrderState.Cancelled, Price = message.price, Quantity = (uint?)message.quantity }); } OnMessageReceived(new TransactionReply { Success = message.Successful, Message = message.result_msg, TransactionId = newOrderTransaction?.TransactionId ?? killOrderTransaction?.TransactionId ?? modifyOrderTransaction?.TransactionId ?? Guid.Empty }); container.RemoveProcessedPendingReply(message); }
private void Handle(QLTransactionReply message) { Logger.Debug().PrintFormat("Handle: {0}", message); var orderExchangeId = ParseOrderIdFromTransactionReply(message); if (orderExchangeId > 0) { container.PutOrderExchangeId(orderExchangeId); } var pendingOscmsToProcess = container.PutTransactionReply(message, orderExchangeId); if (pendingOscmsToProcess != null) { Logger.Debug().Print($"Handle(TR) fires {pendingOscmsToProcess.Count} pending OSCMs to process", LogFields.Message(message)); foreach (var oscm in pendingOscmsToProcess) { // если у нас был получен oscm по данному номеру заявки, но с нулевым trans_id (да да, такое бывает), то тут // у нас есть прекрасная возможность проставить oscm.trans_id и запроцессить oscm по нормальному алгоритму if (oscm.order_num == orderExchangeId && oscm.trans_id == 0) { oscm.trans_id = message.trans_id; } Handle(oscm); } } PreprocessTransactionReply(message); if (message.Successful) { ProcessSuccessfulTransactionReply(message); } else { ProcessFailedTransactionReply(message); } }