private void ProcessMyTradeMessage(Order order, Security security, ExecutionMessage message, long transactionId)
		{
			var tuple = _entityCache.ProcessMyTradeMessage(order, security, message, transactionId);

			if (tuple == null)
			{
				List<ExecutionMessage> nonOrderedMyTrades;

				if (message.OrderId != null)
					nonOrderedMyTrades = _nonAssociatedByIdMyTrades.SafeAdd(message.OrderId.Value);
				else if (message.OriginalTransactionId != 0)
					nonOrderedMyTrades = _nonAssociatedByTransactionIdMyTrades.SafeAdd(message.OriginalTransactionId);
				else
					nonOrderedMyTrades = _nonAssociatedByStringIdMyTrades.SafeAdd(message.OrderStringId);

				this.AddInfoLog("My trade delayed: {0}", message);

				nonOrderedMyTrades.Add((ExecutionMessage)message.Clone());

				return;
			}

			if (!tuple.Item2)
				return;

			RaiseNewMyTrade(tuple.Item1);
		}
		private void ProcessExecutionMessage(ExecutionMessage message)
		{
			if (message.ExecutionType == null)
				throw new ArgumentException(LocalizedStrings.Str688Params.Put(message));

			switch (message.ExecutionType)
			{
				case ExecutionTypes.Transaction:
				{
					if (_entityCache.IsMassCancelation(message.OriginalTransactionId))
					{
						if (message.Error == null)
							RaiseMassOrderCanceled(message.OriginalTransactionId);
						else
							RaiseMassOrderCancelFailed(message.OriginalTransactionId, message.Error);

						break;
					}

					long transactionId;
					var order = _entityCache.GetOrder(message, out transactionId);

					if (order == null)
					{
						var security = LookupSecurity(message.SecurityId);
						ProcessTransactionMessage(null, security, message, transactionId);
					}
					else
					{
						ProcessTransactionMessage(order, order.Security, message, transactionId);
					}

					break;
				}

				case ExecutionTypes.Tick:
				case ExecutionTypes.OrderLog:
				//case null:
				{
					var security = LookupSecurity(message.SecurityId);

					switch (message.ExecutionType)
					{
						case ExecutionTypes.Tick:
							ProcessTradeMessage(security, message);
							break;
						case ExecutionTypes.OrderLog:
							ProcessOrderLogMessage(security, message);
							break;
					}

					if (CreateAssociatedSecurity && !IsAssociated(message.SecurityId.BoardCode))
					{
						var clone = (ExecutionMessage)message.Clone();
						clone.SecurityId = CreateAssociatedId(clone.SecurityId);
						ProcessExecutionMessage(clone);
					}

					break;
				}
				
				default:
					throw new ArgumentOutOfRangeException(LocalizedStrings.Str1695Params.Put(message.ExecutionType));
			}
		}
Пример #3
0
        /// <summary>
        /// Обработать сообщение.
        /// </summary>
        /// <param name="message">Сообщение.</param>
        /// <returns>Результат обработки. Если будет возрвщено <see langword="null"/>,
        /// то генератору пока недостаточно данных для генерации нового сообщения.</returns>
        protected override Message OnProcess(Message message)
        {
            DateTimeOffset time;

            switch (message.Type)
            {
            case MessageTypes.Level1Change:
            {
                var l1Msg = (Level1ChangeMessage)message;

                var value = l1Msg.Changes.TryGetValue(Level1Fields.LastTradePrice);

                if (value != null)
                {
                    _lastOrderPrice = (decimal)value;
                }

                TradeGenerator.Process(message);

                time = l1Msg.ServerTime;
                break;
            }

            case MessageTypes.Execution:
            {
                var execMsg = (ExecutionMessage)message;

                switch (execMsg.ExecutionType)
                {
                case ExecutionTypes.Tick:
                    _lastOrderPrice = execMsg.GetTradePrice();
                    break;

                default:
                    return(null);
                }

                time = execMsg.ServerTime;
                break;
            }

            case MessageTypes.Time:
            {
                var timeMsg = (TimeMessage)message;

                time = timeMsg.ServerTime;

                break;
            }

            default:
                return(null);
            }

            if (!IsTimeToGenerate(time))
            {
                return(null);
            }

            // TODO более реалистичную генерацию, так как сейчас объемы, цены и сделки c потолка

            var action = RandomGen.GetInt(0, 5);

            var isNew = action < 3 || _activeOrders.IsEmpty();

            ExecutionMessage item;

            if (isNew)
            {
                var priceStep = SecurityDefinition.PriceStep ?? 0.01m;

                _lastOrderPrice += RandomGen.GetInt(-MaxPriceStepCount, MaxPriceStepCount) * priceStep;

                if (_lastOrderPrice <= 0)
                {
                    _lastOrderPrice = priceStep;
                }

                item = new ExecutionMessage
                {
                    OrderId       = IdGenerator.GetNextId(),
                    SecurityId    = SecurityId,
                    ServerTime    = time,
                    OrderState    = OrderStates.Active,
                    Volume        = Volumes.Next(),
                    Side          = RandomGen.GetEnum <Sides>(),
                    Price         = _lastOrderPrice,
                    ExecutionType = ExecutionTypes.OrderLog,
                };

                _activeOrders.Enqueue((ExecutionMessage)item.Clone());
            }
            else
            {
                var activeOrder = _activeOrders.Peek();

                item            = (ExecutionMessage)activeOrder.Clone();
                item.ServerTime = time;

                var isMatched = action == 5;

                ExecutionMessage trade = null;

                if (isMatched)
                {
                    trade = (ExecutionMessage)TradeGenerator.Process(message);
                }

                if (isMatched && trade != null)
                {
                    item.Volume = RandomGen.GetInt(1, (int)activeOrder.GetVolume());

                    item.TradeId     = trade.TradeId;
                    item.TradePrice  = trade.TradePrice;
                    item.TradeStatus = trade.TradeStatus;

                    // TODO
                    //quote.Trade = TradeGenerator.Generate(time);
                    //item.Volume = activeOrder.Volume;

                    //if (item.Side == Sides.Buy && quote.Trade.Price > quote.Order.Price)
                    //	item.TradePrice = item.Price;
                    //else if (item.Side == Sides.Sell && quote.Trade.Price < quote.Order.Price)
                    //	item.TradePrice = item.Price;

                    activeOrder.Volume -= item.Volume;

                    if (activeOrder.Volume == 0)
                    {
                        item.OrderState = OrderStates.Done;
                        _activeOrders.Dequeue();
                    }
                    else
                    {
                        item.OrderState = OrderStates.Active;
                    }
                }
                else
                {
                    item.OrderState  = OrderStates.Done;
                    item.IsCancelled = true;
                    _activeOrders.Dequeue();
                }
            }

            LastGenerationTime = time;

            return(item);
        }
Пример #4
0
        /// <summary>
        /// Добавить новую строчку из лога заявок к стакану.
        /// </summary>
        /// <param name="item">Строчка лога заявок.</param>
        /// <returns>Был ли изменен стакан.</returns>
        public bool Update(ExecutionMessage item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (item.ExecutionType != ExecutionTypes.OrderLog)
            {
                throw new ArgumentException("item");
            }

            var changed = false;

            try
            {
                // Очистить стакан в вечерний клиринг
                if (item.ServerTime.TimeOfDay >= _clearingBeginTime)
                {
                    // Garic - переделал
                    // Очищаем только в рабочие дни поскольку в субботу/воскресенье допустима отмена заявок

                    if (_lastUpdateTime != null && _lastUpdateTime.Value.TimeOfDay < _clearingBeginTime && _exchange.WorkingTime.IsTradeDate(item.ServerTime.LocalDateTime, true))
                    {
                        _depth.ServerTime = item.ServerTime;
                        _depth.Bids       = Enumerable.Empty <QuoteChange>();
                        _depth.Asks       = Enumerable.Empty <QuoteChange>();

                        _matchingOrder = null;
                        changed        = true;
                    }
                }

                _lastUpdateTime = item.ServerTime.LocalDateTime;

                if (!item.IsSystem || item.TradePrice != 0 || item.Price == 0 /* нулевая цена может появится при поставке опционов */)
                {
                    return(changed);
                }

                if (item.IsOrderLogRegistered())
                {
                    changed = TryApplyTrades(null);

                    if (
                        (item.Side == Sides.Buy && (_depth.Asks.IsEmpty() || item.Price < _depth.Asks.First().Price)) ||
                        (item.Side == Sides.Sell && (_depth.Bids.IsEmpty() || item.Price > _depth.Bids.First().Price))
                        )
                    {
                        if (item.TimeInForce == TimeInForce.PutInQueue)
                        {
                            var quotes = (item.Side == Sides.Buy ? _bids : _asks);
                            var quote  = quotes.TryGetValue(item.Price);

                            if (quote == null)
                            {
                                quote = new QuoteChange
                                {
                                    Side   = item.Side,
                                    Price  = item.Price,
                                    Volume = item.Volume,
                                };

                                quotes.Add(item.Price, quote);

                                if (item.Side == Sides.Buy)
                                {
                                    _depth.Bids = GetArray(quotes);
                                }
                                else
                                {
                                    _depth.Asks = GetArray(quotes);
                                }
                            }
                            else
                            {
                                quote.Volume += item.Volume;
                            }

                            changed = true;
                        }
                    }
                    else
                    {
                        _matchingOrder = (ExecutionMessage)item.Clone();

                        // mika
                        // из-за того, что могут быть кросс-сделки, матчинг только по заявкам невозможен
                        // (сначала идет регистрация вглубь стакана, затем отмена по причине кросс-сделки)
                        // http://forum.rts.ru/viewtopic.asp?t=24197
                        //
                    }
                }
                else if (item.IsOrderLogCanceled())
                {
                    var isSame = _matchingOrder != null && _matchingOrder.OrderId == item.OrderId;

                    changed = TryApplyTrades(item);

                    if (!isSame && item.TimeInForce == TimeInForce.PutInQueue)
                    {
                        // http://forum.rts.ru/viewtopic.asp?t=24197
                        if (item.GetOrderLogCancelReason() != OrderLogCancelReasons.CrossTrade)
                        {
                            var quotes = (item.Side == Sides.Buy ? _bids : _asks);
                            var quote  = quotes.TryGetValue(item.Price);

                            if (quote != null)
                            {
                                quote.Volume -= item.Volume;

                                if (quote.Volume <= 0)
                                {
                                    quotes.Remove(item.Price);

                                    if (item.Side == Sides.Buy)
                                    {
                                        _depth.Bids = GetArray(quotes);
                                    }
                                    else
                                    {
                                        _depth.Asks = GetArray(quotes);
                                    }
                                }
                            }
                        }

                        changed = true;
                    }
                }
                else
                {
                    throw new ArgumentException(LocalizedStrings.Str943Params.Put(item), "item");

                    // для одной сделки соответствуют две строчки в ОЛ
                    //_trades[item.Trade.Id] = item;
                }
            }
            finally
            {
                if (changed)
                {
                    _depth.ServerTime = item.ServerTime;
                }
            }

            return(changed);
        }