Пример #1
0
        private void ProcessLevel1Depth(Level1ChangeMessage message, decimal bestBidPrice, decimal bestBidVolume, decimal bestAskPrice, decimal bestAskVolume, List <ExecutionMessage> retVal)
        {
            if (message.LocalTime.Date == _lastDepthDate)
            {
                return;
            }

            QuoteChange ask = null;
            QuoteChange bid = null;

            if (bestAskPrice != 0 && bestAskVolume != 0)
            {
                ask = new QuoteChange(Sides.Sell, bestAskPrice, bestAskVolume);
            }

            if (bestBidPrice != 0 && bestBidVolume != 0)
            {
                bid = new QuoteChange(Sides.Buy, bestBidPrice, bestBidVolume);
            }

            if (ask == null && bid == null)
            {
                return;
            }

            retVal.AddRange(ProcessQuoteChange(message.LocalTime,
                                               bid != null ? new[] { bid } : ArrayHelper <QuoteChange> .EmptyArray,
                                               ask != null ? new[] { ask } : ArrayHelper <QuoteChange> .EmptyArray));
        }
Пример #2
0
        private void AddExecMsg(List <ExecutionMessage> diff, DateTime time, QuoteChange quote, decimal volume, bool isSpread)
        {
            if (volume > 0)
            {
                diff.Add(CreateMessage(time, quote.Side, quote.Price, volume));
            }
            else
            {
                volume = volume.Abs();

                // matching only top orders (spread)
                if (isSpread && volume > 1 && _isMatch.Next())
                {
                    var tradeVolume = (int)volume / 2;

                    diff.Add(new ExecutionMessage
                    {
                        Side          = quote.Side,
                        Volume        = tradeVolume,
                        ExecutionType = ExecutionTypes.Tick,
                        SecurityId    = SecurityId,
                        LocalTime     = time,
                        TradePrice    = quote.Price,
                    });

                    // that tick will not affect on order book
                    //volume -= tradeVolume;
                }

                diff.Add(CreateMessage(time, quote.Side, quote.Price, volume, true));
            }
        }
        private static QuoteChange[] DeserializeQuotes(BitArrayReader reader, QuoteMetaInfo metaInfo, Sides side, bool useLong, bool nonAdjustPrice)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (metaInfo == null)
            {
                throw new ArgumentNullException(nameof(metaInfo));
            }

            var deltaCount = reader.ReadInt();

            if (deltaCount == 0)
            {
                return(ArrayHelper.Empty <QuoteChange>());
            }

            var quotes = new QuoteChange[deltaCount];

            for (var i = 0; i < deltaCount; i++)
            {
                var prevPrice = metaInfo.FirstPrice;
                var price     = reader.ReadPrice(ref prevPrice, metaInfo, useLong, nonAdjustPrice);
                metaInfo.FirstPrice = prevPrice;

                var volume = reader.ReadVolume(metaInfo);

                quotes[i] = new QuoteChange(side, price, volume);
            }

            return(quotes);
        }
Пример #4
0
		/// <summary>
		/// Initializes a new instance of the <see cref="TimeQuoteChange"/>.
		/// </summary>
		/// <param name="quote">The quote, from which changes will be copied.</param>
		/// <param name="message">The message with quotes.</param>
		public TimeQuoteChange(QuoteChange quote, QuoteChangeMessage message)
		{
			if (quote == null)
				throw new ArgumentNullException("quote");

			SecurityId = message.SecurityId;
			ServerTime = message.ServerTime;
			LocalTime = message.LocalTime;
			Price = quote.Price;
			Volume = quote.Volume;
			Side = quote.Side;
		}
        private void OnProcessQuotes(string where, string[] quotes)
        {
            // paper_no нужно парсить из условия where, так как в поле paper_no передается всегда 0
            var paperNo = where.Split('=')[1].Trim().To <int>();
            var f       = Wrapper.FieldsDepth;

            var bids = new List <QuoteChange>();
            var asks = new List <QuoteChange>();

            foreach (var quoteStr in quotes)
            {
                var cols = quoteStr.ToColumns();

                var sellQty = f.SellQty.GetValue(cols);
                var price   = f.Price.GetValue(cols);
                var buyQty  = f.BuyQty.GetValue(cols);

                if (sellQty == 0 && buyQty == 0)
                {
                    // If the sellQty and buyQty are 0 - that is our limit order
                    // which is not a part of the market depth. Just skip it.
                    continue;
                }

                QuoteChange quote;

                if (sellQty == 0)
                {
                    quote = new QuoteChange(Sides.Buy, price, buyQty);
                    bids.Insert(0, quote);
                }
                else
                {
                    quote = new QuoteChange(Sides.Sell, price, sellQty);
                    asks.Add(quote);
                }
            }

            if (bids.Count > 0 || asks.Count > 0)
            {
                SendOutMessage(new QuoteChangeMessage
                {
                    Bids       = bids,
                    Asks       = asks,
                    SecurityId = new SecurityId {
                        Native = paperNo
                    },
                    IsSorted   = true,
                    ServerTime = CurrentTime.Convert(TimeHelper.Moscow),
                });
            }
        }
Пример #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TimeQuoteChange"/>.
        /// </summary>
        /// <param name="side">Direction (buy or sell).</param>
        /// <param name="quote">The quote, from which changes will be copied.</param>
        /// <param name="message">The message with quotes.</param>
        public TimeQuoteChange(Sides side, QuoteChange quote, QuoteChangeMessage message)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            SecurityId = message.SecurityId;
            ServerTime = message.ServerTime;
            LocalTime  = message.LocalTime;
            Quote      = quote;
            Side       = side;
        }
        public NullableTimeQuoteChange(QuoteChange quote, QuoteChangeMessage message)
        {
            if (quote == null)
            {
                throw new ArgumentNullException(nameof(quote));
            }

            ServerTime = message.ServerTime;
            LocalTime  = message.LocalTime;
            Price      = quote.Price;
            Volume     = quote.Volume;
            Side       = quote.Side;
        }
        private void SessionOnStil2Update(ref structSTIL2Update structL2Update)
        {
            var depth = _depths[structL2Update.bstrSymbol];

            var quote = new QuoteChange(structL2Update.bstrSide.ToSide(), (decimal)structL2Update.fPrice, structL2Update.nQty);

            var quotes = quote.Side == Sides.Sell ? depth.Item1 : depth.Item2;

            switch (structL2Update.bstrAction)
            {
            case "A":                     // add
            {
                quotes.Add(quote);
                break;
            }

            case "C":                     // change
            {
                quotes.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                quotes.Add(quote);
                break;
            }

            case "D":                     // delete
            {
                quotes.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                break;
            }
            }

            var board = structL2Update.bstrMaker;

            if (board.IsEmpty())
            {
                board = AssociatedBoardCode;
            }

            var message = new QuoteChangeMessage
            {
                SecurityId = new SecurityId
                {
                    SecurityCode = structL2Update.bstrSymbol,
                    BoardCode    = board,
                },
                Asks       = depth.Item1.ToArray(),
                Bids       = depth.Item2.ToArray(),
                ServerTime = structL2Update.bstrTime.StrToTime(),
            };

            SendOutMessage(message);
        }
Пример #9
0
        /// <summary>
        /// Создать <see cref="TimeQuoteChange"/>.
        /// </summary>
        /// <param name="quote">Котировка, из которой будут скопированы изменения.</param>
        /// <param name="message">Сообщение с котировками.</param>
        public TimeQuoteChange(QuoteChange quote, QuoteChangeMessage message)
        {
            if (quote == null)
            {
                throw new ArgumentNullException("quote");
            }

            SecurityId = message.SecurityId;
            ServerTime = message.ServerTime;
            LocalTime  = message.LocalTime;
            Price      = quote.Price;
            Volume     = quote.Volume;
            Side       = quote.Side;
        }
Пример #10
0
        public static Quote ToQuote(this QuoteChange change, Sides side, Security security, Func <SecurityId, Security> getSecurity = null)
        {
            if (!change.BoardCode.IsEmpty() && getSecurity != null)
            {
                security = getSecurity(new SecurityId {
                    SecurityCode = security.Code, BoardCode = change.BoardCode
                });
            }

            var quote = new Quote(security, change.Price, change.Volume, side, change.OrdersCount, change.Condition);

            change.CopyExtensionInfo(quote);
            return(quote);
        }
        private NullableTimeQuoteChange ToNullQuote(Sides side, QuoteChange quote, QuoteChangeMessage message)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            return(new NullableTimeQuoteChange
            {
                ServerTime = message.ServerTime,
                LocalTime = message.LocalTime,
                Side = side,
                Quote = quote,
            });
        }
Пример #12
0
        public NullableTimeQuoteChange(Sides side, QuoteChange quote, QuoteChangeMessage message)
        {
            if (quote == null)
            {
                throw new ArgumentNullException(nameof(quote));
            }

            ServerTime  = message.ServerTime;
            LocalTime   = message.LocalTime;
            Price       = quote.Price;
            Volume      = quote.Volume;
            Side        = side;
            OrdersCount = quote.OrdersCount;
            Condition   = quote.Condition;
        }
Пример #13
0
        /// <inheritdoc />
        protected override NullableTimeQuoteChange Read(FastCsvReader reader, IMarketDataMetaInfo metaInfo)
        {
            var quote = new NullableTimeQuoteChange
            {
                ServerTime = reader.ReadTime(metaInfo.Date),
            };

            var price  = reader.ReadNullableDecimal();
            var volume = reader.ReadNullableDecimal();

            quote.Side = reader.ReadEnum <Sides>();

            int?ordersCount = null;

            if ((reader.ColumnCurr + 1) < reader.ColumnCount)
            {
                ordersCount = reader.ReadNullableInt();
            }

            QuoteConditions condition = default;

            if ((reader.ColumnCurr + 1) < reader.ColumnCount)
            {
                condition = reader.ReadNullableEnum <QuoteConditions>() ?? default;
            }

            if (price != null)
            {
                var qq = new QuoteChange
                {
                    Price       = price.Value,
                    Volume      = volume ?? 0,
                    OrdersCount = ordersCount,
                    Condition   = condition,
                };

                quote.Quote = qq;

                if ((reader.ColumnCurr + 1) < reader.ColumnCount)
                {
                    qq.StartPosition = reader.ReadNullableInt();
                    qq.EndPosition   = reader.ReadNullableInt();
                    qq.Action        = reader.ReadNullableEnum <QuoteChangeActions>();
                }
            }

            return(quote);
        }
Пример #14
0
        private NullableTimeQuoteChange ToNullQuote(Sides side, QuoteChange quote, QuoteChangeMessage message)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            return(new NullableTimeQuoteChange
            {
                ServerTime = message.ServerTime,
                LocalTime = message.LocalTime,
                Side = side,
                State = message.State,
                Quote = quote,
                BuildFrom = message.BuildFrom,
                SeqNum = message.SeqNum.DefaultAsNull(),
            });
        }
Пример #15
0
        private static QuoteChange[] DeserializeQuotes(BitArrayReader reader, QuoteMetaInfo metaInfo, bool useLong, bool nonAdjustPrice)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (metaInfo == null)
            {
                throw new ArgumentNullException(nameof(metaInfo));
            }

            var deltaCount = reader.ReadInt();

            if (deltaCount == 0)
            {
                return(ArrayHelper.Empty <QuoteChange>());
            }

            var quotes = new QuoteChange[deltaCount];

            for (var i = 0; i < deltaCount; i++)
            {
                var prevPrice = metaInfo.FirstPrice;
                var price     = reader.ReadPrice(ref prevPrice, metaInfo, useLong, nonAdjustPrice);
                metaInfo.FirstPrice = prevPrice;

                var volume = reader.ReadVolume(metaInfo);

                var ordersCount = metaInfo.Version >= MarketDataVersions.Version56
                                        ? reader.ReadNullableInt()
                                        : null;

                var condition = metaInfo.Version >= MarketDataVersions.Version57
                                        ? reader.Read() ? (QuoteConditions)reader.ReadInt() : default
                                        : default;

                quotes[i] = new QuoteChange(price, volume, ordersCount, condition);
            }

            return(quotes);
        }
Пример #16
0
        public void ShowWarningQuoteChanged(string username, decimal quote, OrderType orderType)
        {
            if (messagesTextBox.InvokeRequired) //randomly chosen component from ui thread
            {
                QuoteChange del = new QuoteChange(ShowWarningQuoteChanged);
                messagesTextBox.BeginInvoke(del, new object[] { username, quote, orderType });
            }
            else
            {
                if (username != this.username)
                {
                    int numOrders = 0;
                    if (orderType == OrderType.BUYING)
                    {
                        numOrders = coordinator.GetAmountBuyingOrders(this.username);
                    }
                    else
                    {
                        numOrders = coordinator.GetAmountSellingOrders(this.username);
                    }

                    if (numOrders > 0)
                    {
                        string       message = username + " changed the diginote quote to\n" + quote.ToString() + "\nDo you want to keep your pending orders? If you don't answer in 30s, your orders will be kept.";
                        DialogResult result  = MessageBoxEx.Show(message, "Quote Change", MessageBoxButtons.YesNo, MessageBoxIcon.Information, 30000);

                        if (result == DialogResult.No)
                        {
                            if (orderType == OrderType.BUYING)
                            {
                                coordinator.CancelPurchasingOrders(numOrders, this.username);
                            }
                            else
                            {
                                coordinator.CancelSellingOrders(numOrders, this.username);
                            }
                        }
                    }
                }
                updateInfo();
            }
        }
		private void OnSecurityChanged(string smartId, Tuple<decimal, decimal, DateTime> lastTrade, decimal open, decimal high, decimal low, decimal close, decimal volume, QuoteChange bid, QuoteChange ask,
			decimal openInt, Tuple<decimal, decimal> goBuySell, Tuple<decimal, decimal> goBase, Tuple<decimal, decimal> limits, int tradingStatus, Tuple<decimal, decimal> volatTheorPrice)
		{
			var secId = new SecurityId { Native = smartId };

			var message = new Level1ChangeMessage
			{
				SecurityId = secId,
				ExtensionInfo = new Dictionary<object, object>
				{
					{ SmartComExtensionInfoHelper.SecurityOptionsMargin, goBase.Item1 },
					{ SmartComExtensionInfoHelper.SecurityOptionsSyntheticMargin, goBase.Item2 }
				},
				ServerTime = CurrentTime.Convert(TimeHelper.Moscow),
			};

			message.TryAdd(Level1Fields.LastTradePrice, lastTrade.Item1);
			message.TryAdd(Level1Fields.LastTradeVolume, lastTrade.Item2);
			message.Add(Level1Fields.LastTradeTime, lastTrade.Item3.ApplyTimeZone(TimeHelper.Moscow));

			var prevQuotes = _bestQuotes.TryGetValue(secId);

			if (bid.Price != 0)
			{
				message.Add(Level1Fields.BestBidPrice, bid.Price);

				if (prevQuotes != null && prevQuotes.First != null && prevQuotes.First.Item1 == bid.Price)
					message.Add(Level1Fields.BestBidVolume, prevQuotes.First.Item2);
			}

			if (ask.Price != 0)
			{
				message.Add(Level1Fields.BestAskPrice, ask.Price);

				if (prevQuotes != null && prevQuotes.Second != null && prevQuotes.Second.Item1 == ask.Price)
					message.Add(Level1Fields.BestAskVolume, prevQuotes.Second.Item2);
			}

			message.TryAdd(Level1Fields.BidsVolume, bid.Volume);
			message.TryAdd(Level1Fields.AsksVolume, ask.Volume);

			message.TryAdd(Level1Fields.OpenPrice, open);
			message.TryAdd(Level1Fields.LowPrice, low);
			message.TryAdd(Level1Fields.HighPrice, high);
			message.TryAdd(Level1Fields.ClosePrice, close);

			message.TryAdd(Level1Fields.MinPrice, limits.Item1);
			message.TryAdd(Level1Fields.MaxPrice, limits.Item2);

			message.TryAdd(Level1Fields.MarginBuy, goBuySell.Item1);
			message.TryAdd(Level1Fields.MarginSell, goBuySell.Item2);
			message.TryAdd(Level1Fields.OpenInterest, openInt);

			message.TryAdd(Level1Fields.ImpliedVolatility, volatTheorPrice.Item1);
			message.TryAdd(Level1Fields.TheorPrice, volatTheorPrice.Item2);

			message.TryAdd(Level1Fields.Volume, volume);

			message.Add(Level1Fields.State, tradingStatus == 0 ? SecurityStates.Trading : SecurityStates.Stoped);

			SendOutMessage(message);
		}
Пример #18
0
		private IEnumerable<ExecutionMessage> ProcessQuoteChange(DateTime time, QuoteChange[] newBids, QuoteChange[] newAsks)
		{
			var retVal = _bids.Select(p => p.Value.Second)
				.GetDiff(newBids, Sides.Buy, true)
				.Concat(_asks.Select(p => p.Value.Second).GetDiff(newAsks, Sides.Sell, true))
				.Select(q => CreateMessage(time, q.Side, q.Price, q.Volume.Abs(), !(q.Volume > 0)));

			var bestBidPrice = 0m;

			foreach (var bid in newBids)
			{
				if (bid.Price > bestBidPrice)
					bestBidPrice = bid.Price;
			}

			var bestAskPrice = 0m;

			foreach (var ask in newAsks)
			{
				if (bestAskPrice == 0 || ask.Price < bestAskPrice)
					bestAskPrice = ask.Price;
			}

			var spreadPrice = bestAskPrice == 0
				? bestBidPrice
				: (bestBidPrice == 0
					? bestAskPrice
					: (bestAskPrice - bestBidPrice) / 2 + bestBidPrice);

			//при обновлении стакана необходимо учитывать направление сдвига, чтобы не было ложного исполнения при наложении бидов и асков.
			//т.е. если цена сдвинулась вниз, то обновление стакана необходимо начинать с минимального бида.
			retVal = (spreadPrice < _currSpreadPrice)
				? retVal.OrderBy(m => m.Price)
				: retVal.OrderByDescending(m => m.Price);

			_currSpreadPrice = spreadPrice;

			return retVal.ToArray();
		}
Пример #19
0
		private void AddExecMsg(List<ExecutionMessage> diff, DateTime time, DateTimeOffset serverTime, QuoteChange quote, decimal volume, bool isSpread)
		{
			if (volume > 0)
				diff.Add(CreateMessage(time, serverTime, quote.Side, quote.Price, volume));
			else
			{
				volume = volume.Abs();

				// matching only top orders (spread)
				if (isSpread && volume > 1 && _isMatch.Next())
				{
					var tradeVolume = (int)volume / 2;

					diff.Add(new ExecutionMessage
					{
						Side = quote.Side,
						Volume = tradeVolume,
						ExecutionType = ExecutionTypes.Tick,
						SecurityId = SecurityId,
						LocalTime = time,
						ServerTime = serverTime,
						TradePrice = quote.Price,
					});

					// that tick will not affect on order book
					//volume -= tradeVolume;
				}

				diff.Add(CreateMessage(time, serverTime, quote.Side, quote.Price, volume, true));
			}
		}
		private bool TryApplyTrades(ExecutionMessage item)
		{
			if (_matchingOrder == null)
				return false;

			try
			{
				var volume = _matchingOrder.GetVolume();

				if (item != null && _matchingOrder.OrderId == item.OrderId)
					volume -= item.GetVolume();

				// если заявка была вся отменена. например, по причине http://forum.rts.ru/viewtopic.asp?t=24197
				if (volume == 0)
					return false;

				var removingQuotes = new List<Tuple<QuoteChange, decimal>>();

				foreach (var quote in (_matchingOrder.Side == Sides.Buy ? _depth.Asks : _depth.Bids))
				{
					if ((_matchingOrder.Side == Sides.Buy && _matchingOrder.Price < quote.Price) || (_matchingOrder.Side == Sides.Sell && _matchingOrder.Price > quote.Price))
						break;

					if (volume >= quote.Volume)
					{
						removingQuotes.Add(Tuple.Create(quote, quote.Volume));

						volume -= quote.Volume;

						if (volume == 0)
							break;
					}
					else
					{
						removingQuotes.Add(Tuple.Create(quote, volume));

						volume = 0;
						break;
					}
				}

				// в текущей момент Плаза не транслирует признак MatchOrCancel через ОЛ, поэтому сделано на будущее
				if (_matchingOrder.TimeInForce == TimeInForce.MatchOrCancel && volume > 0)
					return false;

				foreach (var removingQuote in removingQuotes)
				{
					var quotes = (_matchingOrder.Side.Invert() == Sides.Buy ? _bids : _asks);

					var quote = quotes.TryGetValue(removingQuote.Item1.Price);

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

						if (quote.Volume <= 0)
						{
							quotes.Remove(removingQuote.Item1.Price);
						}
					}
				}
				
				if (volume > 0)
				{
					if (_matchingOrder.TimeInForce == TimeInForce.PutInQueue)
					{
						var quote = new QuoteChange
						{
							Side = _matchingOrder.Side,
							Price = _matchingOrder.Price,
							Volume = volume,
						};

						(quote.Side == Sides.Buy ? _bids : _asks).Add(quote.Price, quote);
					}
				}

				_depth.Bids = GetArray(_bids);
				_depth.Asks = GetArray(_asks);

				return true;
			}
			finally
			{
				_matchingOrder = null;
			}
		}
        private void OnSecurityChanged(string smartId, Tuple <decimal?, decimal?, DateTime> lastTrade, decimal?open, decimal?high, decimal?low, decimal?close, decimal?volume, QuoteChange bid, QuoteChange ask,
                                       decimal?openInt, Tuple <decimal?, decimal?> goBuySell, Tuple <decimal?, decimal?> goBase, Tuple <decimal?, decimal?> limits, int tradingStatus, Tuple <decimal?, decimal?> volatTheorPrice)
        {
            var secId = new SecurityId {
                Native = smartId
            };

            var message = new Level1ChangeMessage
            {
                SecurityId    = secId,
                ExtensionInfo = new Dictionary <object, object>
                {
                    { SmartComExtensionInfoHelper.SecurityOptionsMargin, goBase.Item1 },
                    { SmartComExtensionInfoHelper.SecurityOptionsSyntheticMargin, goBase.Item2 }
                },
                ServerTime = CurrentTime.Convert(TimeHelper.Moscow),
            };

            message.TryAdd(Level1Fields.LastTradePrice, lastTrade.Item1);
            message.TryAdd(Level1Fields.LastTradeVolume, lastTrade.Item2);
            message.Add(Level1Fields.LastTradeTime, lastTrade.Item3.ApplyTimeZone(TimeHelper.Moscow));

            var prevQuotes = _bestQuotes.TryGetValue(secId);

            if (bid.Price != 0)
            {
                message.Add(Level1Fields.BestBidPrice, bid.Price);

                if (prevQuotes != null && prevQuotes.First != null && prevQuotes.First.Item1 == bid.Price)
                {
                    message.Add(Level1Fields.BestBidVolume, prevQuotes.First.Item2);
                }
            }

            if (ask.Price != 0)
            {
                message.Add(Level1Fields.BestAskPrice, ask.Price);

                if (prevQuotes != null && prevQuotes.Second != null && prevQuotes.Second.Item1 == ask.Price)
                {
                    message.Add(Level1Fields.BestAskVolume, prevQuotes.Second.Item2);
                }
            }

            message.TryAdd(Level1Fields.BidsVolume, bid.Volume);
            message.TryAdd(Level1Fields.AsksVolume, ask.Volume);

            message.TryAdd(Level1Fields.OpenPrice, open);
            message.TryAdd(Level1Fields.LowPrice, low);
            message.TryAdd(Level1Fields.HighPrice, high);
            message.TryAdd(Level1Fields.ClosePrice, close);

            message.TryAdd(Level1Fields.MinPrice, limits.Item1);
            message.TryAdd(Level1Fields.MaxPrice, limits.Item2);

            message.TryAdd(Level1Fields.MarginBuy, goBuySell.Item1);
            message.TryAdd(Level1Fields.MarginSell, goBuySell.Item2);
            message.TryAdd(Level1Fields.OpenInterest, openInt);

            message.TryAdd(Level1Fields.ImpliedVolatility, volatTheorPrice.Item1);
            message.TryAdd(Level1Fields.TheorPrice, volatTheorPrice.Item2);

            message.TryAdd(Level1Fields.Volume, volume);

            message.Add(Level1Fields.State, tradingStatus == 0 ? SecurityStates.Trading : SecurityStates.Stoped);

            SendOutMessage(message);
        }
Пример #22
0
        private void GetDiff(List <ExecutionMessage> diff, DateTimeOffset time, DateTimeOffset serverTime, SortedDictionary <decimal, RefPair <LevelQuotes, QuoteChange> > from, IEnumerable <QuoteChange> to, Sides side, out decimal newBestPrice)
        {
            newBestPrice = 0;

            var canProcessFrom = true;
            var canProcessTo   = true;

            QuoteChange currFrom = null;
            QuoteChange currTo   = null;

            // TODO
            //List<ExecutionMessage> currOrders = null;

            var  mult     = side == Sides.Buy ? -1 : 1;
            bool?isSpread = null;

            using (var fromEnum = from.GetEnumerator())
                using (var toEnum = to.GetEnumerator())
                {
                    while (true)
                    {
                        if (canProcessFrom && currFrom == null)
                        {
                            if (!fromEnum.MoveNext())
                            {
                                canProcessFrom = false;
                            }
                            else
                            {
                                currFrom = fromEnum.Current.Value.Second;
                                isSpread = isSpread == null;
                            }
                        }

                        if (canProcessTo && currTo == null)
                        {
                            if (!toEnum.MoveNext())
                            {
                                canProcessTo = false;
                            }
                            else
                            {
                                currTo = toEnum.Current;

                                if (newBestPrice == 0)
                                {
                                    newBestPrice = currTo.Price;
                                }
                            }
                        }

                        if (currFrom == null)
                        {
                            if (currTo == null)
                            {
                                break;
                            }
                            else
                            {
                                AddExecMsg(diff, time, serverTime, currTo, currTo.Volume, side, false);
                                currTo = null;
                            }
                        }
                        else
                        {
                            if (currTo == null)
                            {
                                AddExecMsg(diff, time, serverTime, currFrom, -currFrom.Volume, side, isSpread.Value);
                                currFrom = null;
                            }
                            else
                            {
                                if (currFrom.Price == currTo.Price)
                                {
                                    if (currFrom.Volume != currTo.Volume)
                                    {
                                        AddExecMsg(diff, time, serverTime, currTo, currTo.Volume - currFrom.Volume, side, isSpread.Value);
                                    }

                                    currFrom = currTo = null;
                                }
                                else if (currFrom.Price * mult > currTo.Price * mult)
                                {
                                    AddExecMsg(diff, time, serverTime, currTo, currTo.Volume, side, isSpread.Value);
                                    currTo = null;
                                }
                                else
                                {
                                    AddExecMsg(diff, time, serverTime, currFrom, -currFrom.Volume, side, isSpread.Value);
                                    currFrom = null;
                                }
                            }
                        }
                    }
                }
        }
		private void OnProcessQuotes(string where, string[] quotes)
		{
			// paper_no нужно парсить из условия where, так как в поле paper_no передается всегда 0
			var paperNo = where.Split('=')[1].Trim().To<int>();
			var f = Wrapper.FieldsDepth;

			var bids = new List<QuoteChange>();
			var asks = new List<QuoteChange>();

			foreach (var quoteStr in quotes)
			{
				var cols = quoteStr.ToColumns();

				var sellQty = f.SellQty.GetValue(cols);
				var price = f.Price.GetValue(cols);
				var buyQty = f.BuyQty.GetValue(cols);

				if (sellQty == 0 && buyQty == 0)
				{
					// If the sellQty and buyQty are 0 - that is our limit order 
					// which is not a part of the market depth. Just skip it.
					continue;
				}

				QuoteChange quote;

				if (sellQty == 0)
				{
					quote = new QuoteChange(Sides.Buy, price, buyQty);
					bids.Insert(0, quote);
				}
				else
				{
					quote = new QuoteChange(Sides.Sell, price, sellQty);
					asks.Add(quote);
				}
			}

			if (bids.Count > 0 || asks.Count > 0)
			{
				SendOutMessage(new QuoteChangeMessage
				{
					Bids = bids,
					Asks = asks,
					SecurityId = new SecurityId { Native = paperNo },
					IsSorted = true,
					ServerTime = CurrentTime.Convert(TimeHelper.Moscow),
				});	
			}
		}
		private void SessionOnStil2Update(ref structSTIL2Update structL2Update)
		{
			var asksUpdate = _depths[structL2Update.bstrSymbol].Item1;
			var bidsUpdate = _depths[structL2Update.bstrSymbol].Item2;

			var quote = new QuoteChange(structL2Update.bstrSide.ToSide(), (decimal) structL2Update.fPrice, structL2Update.nQty) {BoardCode = structL2Update.bstrMaker};

			switch (structL2Update.bstrSide.ToSide())
			{
				case Sides.Buy:
				{
					switch (structL2Update.bstrAction)
					{
						case "A": // add
						{
							bidsUpdate.Add(quote);
							break;
						}
						case "C": // change
						{
							bidsUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
							bidsUpdate.Add(quote);
							break;
						}
						case "D": // delete
						{
							bidsUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
							break;
						}
					}

					break;
				}

				case Sides.Sell:
				{
					switch (structL2Update.bstrAction)
					{
						case "A": // add
						{
							asksUpdate.Add(quote);
							break;
						}
						case "C": // change
						{
							asksUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
							asksUpdate.Add(quote);
							break;
						}
						case "D": // delete
						{
							asksUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
							break;
						}
					}

					break;
				}
			}

			var message = new QuoteChangeMessage
			{
				SecurityId = new SecurityId
				{
					SecurityCode = structL2Update.bstrSymbol,
					BoardCode = "All",
				},
				Asks = asksUpdate,
				Bids = bidsUpdate,
				ServerTime = structL2Update.bstrTime.StrToTime(),
			};

			SendOutMessage(message);			
		}
Пример #25
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);
        }
Пример #26
0
        private bool TryApplyTrades(ExecutionMessage item)
        {
            if (_matchingOrder == null)
            {
                return(false);
            }

            try
            {
                var volume = _matchingOrder.Volume;

                if (item != null && _matchingOrder.OrderId == item.OrderId)
                {
                    volume -= item.Volume;
                }

                // если заявка была вся отменена. например, по причине http://forum.rts.ru/viewtopic.asp?t=24197
                if (volume == 0)
                {
                    return(false);
                }

                var removingQuotes = new List <Tuple <QuoteChange, decimal> >();

                foreach (var quote in (_matchingOrder.Side == Sides.Buy ? _depth.Asks : _depth.Bids))
                {
                    if ((_matchingOrder.Side == Sides.Buy && _matchingOrder.Price < quote.Price) || (_matchingOrder.Side == Sides.Sell && _matchingOrder.Price > quote.Price))
                    {
                        break;
                    }

                    if (volume >= quote.Volume)
                    {
                        removingQuotes.Add(Tuple.Create(quote, quote.Volume));

                        volume -= quote.Volume;

                        if (volume == 0)
                        {
                            break;
                        }
                    }
                    else
                    {
                        removingQuotes.Add(Tuple.Create(quote, volume));

                        volume = 0;
                        break;
                    }
                }

                // в текущей момент Плаза не транслирует признак MatchOrCancel через ОЛ, поэтому сделано на будущее
                if (_matchingOrder.TimeInForce == TimeInForce.MatchOrCancel && volume > 0)
                {
                    return(false);
                }

                foreach (var removingQuote in removingQuotes)
                {
                    var quotes = (_matchingOrder.Side.Invert() == Sides.Buy ? _bids : _asks);

                    var quote = quotes.TryGetValue(removingQuote.Item1.Price);

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

                        if (quote.Volume <= 0)
                        {
                            quotes.Remove(removingQuote.Item1.Price);
                        }
                    }
                }

                if (volume > 0)
                {
                    if (_matchingOrder.TimeInForce == TimeInForce.PutInQueue)
                    {
                        var quote = new QuoteChange
                        {
                            Side   = _matchingOrder.Side,
                            Price  = _matchingOrder.Price,
                            Volume = volume,
                        };

                        (quote.Side == Sides.Buy ? _bids : _asks).Add(quote.Price, quote);
                    }
                }

                _depth.Bids = GetArray(_bids);
                _depth.Asks = GetArray(_asks);

                return(true);
            }
            finally
            {
                _matchingOrder = null;
            }
        }
Пример #27
0
		private IEnumerable<ExecutionMessage> ProcessQuoteChange(DateTime time, QuoteChange[] newBids, QuoteChange[] newAsks)
		{
			decimal bestBidPrice;
			decimal bestAskPrice;

			var retVal =
				GetDiff(time, _bids, newBids, Sides.Buy, out bestBidPrice)
				.Concat(GetDiff(time, _asks, newAsks, Sides.Sell, out bestAskPrice));

			var spreadPrice = bestAskPrice == 0
				? bestBidPrice
				: (bestBidPrice == 0
					? bestAskPrice
					: (bestAskPrice - bestBidPrice) / 2 + bestBidPrice);

			//при обновлении стакана необходимо учитывать направление сдвига, чтобы не было ложного исполнения при наложении бидов и асков.
			//т.е. если цена сдвинулась вниз, то обновление стакана необходимо начинать с минимального бида.
			retVal = (spreadPrice < _currSpreadPrice)
				? retVal.OrderBy(m => m.Price)
				: retVal.OrderByDescending(m => m.Price);

			_currSpreadPrice = spreadPrice;

			return retVal.ToArray();
		}
		private void SessionOnStil2Update(ref structSTIL2Update structL2Update)
		{
			var depth = _depths[structL2Update.bstrSymbol];

			var quote = new QuoteChange(structL2Update.bstrSide.ToSide(), (decimal)structL2Update.fPrice, structL2Update.nQty);

			var quotes = quote.Side == Sides.Sell ? depth.Item1 : depth.Item2;

			switch (structL2Update.bstrAction)
			{
				case "A": // add
				{
					quotes.Add(quote);
					break;
				}
				case "C": // change
				{
					quotes.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
					quotes.Add(quote);
					break;
				}
				case "D": // delete
				{
					quotes.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
					break;
				}
			}

			var board = structL2Update.bstrMaker;

			if (board.IsEmpty())
				board = AssociatedBoardCode;

			var message = new QuoteChangeMessage
			{
				SecurityId = new SecurityId
				{
					SecurityCode = structL2Update.bstrSymbol,
					BoardCode = board,
				},
				Asks = depth.Item1.ToArray(),
				Bids = depth.Item2.ToArray(),
				ServerTime = structL2Update.bstrTime.StrToTime(),
			};

			SendOutMessage(message);			
		}
Пример #29
0
        /// <summary>
        /// Вычислить приращение между котировками.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="from">Первые котировки.</param>
        /// <param name="to">Вторые котировки.</param>
        /// <param name="side">Направление, показывающее тип котировок.</param>
        /// <param name="newBestPrice"></param>
        /// <returns>Изменения.</returns>
        private IEnumerable <ExecutionMessage> GetDiff(DateTime time, IEnumerable <QuoteChange> from, IEnumerable <QuoteChange> to, Sides side, out decimal newBestPrice)
        {
            //if (!isSorted)
            //{
            //	if (side == Sides.Sell)
            //	{
            //		from = from.OrderBy(q => q.Price);
            //		to = to.OrderBy(q => q.Price);
            //	}
            //	else
            //	{
            //		from = from.OrderByDescending(q => q.Price);
            //		to = to.OrderByDescending(q => q.Price);
            //	}
            //}

            newBestPrice = 0;

            var diff = new List <ExecutionMessage>();

            var canProcessFrom = true;
            var canProcessTo   = true;

            QuoteChange currFrom = null;
            QuoteChange currTo   = null;

            var mult = side == Sides.Buy ? -1 : 1;

            using (var fromEnum = from.GetEnumerator())
                using (var toEnum = to.GetEnumerator())
                {
                    while (true)
                    {
                        if (canProcessFrom && currFrom == null)
                        {
                            if (!fromEnum.MoveNext())
                            {
                                canProcessFrom = false;
                            }
                            else
                            {
                                currFrom = fromEnum.Current;
                            }
                        }

                        if (canProcessTo && currTo == null)
                        {
                            if (!toEnum.MoveNext())
                            {
                                canProcessTo = false;
                            }
                            else
                            {
                                currTo = toEnum.Current;

                                if (newBestPrice == 0)
                                {
                                    newBestPrice = currTo.Price;
                                }
                            }
                        }

                        if (currFrom == null)
                        {
                            if (currTo == null)
                            {
                                break;
                            }
                            else
                            {
                                //diff.Add(currTo.Clone());
                                AddExecMsg(diff, time, currTo, currTo.Volume);
                                currTo = null;
                            }
                        }
                        else
                        {
                            if (currTo == null)
                            {
                                //var clone = currFrom.Clone();
                                //clone.Volume = -clone.Volume;
                                //diff.Add(clone);
                                AddExecMsg(diff, time, currFrom, -currFrom.Volume);
                                currFrom = null;
                            }
                            else
                            {
                                if (currFrom.Price == currTo.Price)
                                {
                                    if (currFrom.Volume != currTo.Volume)
                                    {
                                        //var clone = currTo.Clone();
                                        //clone.Volume -= currFrom.Volume;
                                        //diff.Add(clone);
                                        AddExecMsg(diff, time, currTo, currTo.Volume - currFrom.Volume);
                                    }

                                    currFrom = currTo = null;
                                }
                                else if (currFrom.Price * mult > currTo.Price * mult)
                                {
                                    //diff.Add(currTo.Clone());
                                    AddExecMsg(diff, time, currTo, currTo.Volume);
                                    currTo = null;
                                }
                                else
                                {
                                    //var clone = currFrom.Clone();
                                    //clone.Volume = -clone.Volume;
                                    //diff.Add(clone);
                                    AddExecMsg(diff, time, currFrom, -currFrom.Volume);
                                    currFrom = null;
                                }
                            }
                        }
                    }
                }

            return(diff);
        }
Пример #30
0
        private static QuoteChange[] DeserializeQuotes(BitArrayReader reader, QuoteMetaInfo metaInfo, bool useLong, bool nonAdjustPrice)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (metaInfo == null)
            {
                throw new ArgumentNullException(nameof(metaInfo));
            }

            var count = reader.ReadInt();

            if (count == 0)
            {
                return(ArrayHelper.Empty <QuoteChange>());
            }

            var is56 = metaInfo.Version >= MarketDataVersions.Version56;
            var is57 = metaInfo.Version >= MarketDataVersions.Version57;
            var is58 = metaInfo.Version >= MarketDataVersions.Version58;

            var quotes = new QuoteChange[count];

            for (var i = 0; i < count; i++)
            {
                var prevPrice = metaInfo.FirstPrice;
                var price     = reader.ReadPrice(ref prevPrice, metaInfo, useLong, nonAdjustPrice);
                metaInfo.FirstPrice = prevPrice;

                var volume = reader.ReadVolume(metaInfo);

                var ordersCount = is56
                                        ? reader.ReadNullableInt()
                                        : null;

                var condition = is57
                                        ? (QuoteConditions)(reader.ReadNullableInt() ?? 0)
                                        : default;

                var quote = new QuoteChange(price, volume, ordersCount, condition);

                if (is58)
                {
                    if (reader.Read())
                    {
                        quote.Action = (QuoteChangeActions)reader.ReadInt();
                    }

                    if (reader.Read())
                    {
                        quote.StartPosition = reader.ReadInt();
                    }

                    if (reader.Read())
                    {
                        quote.EndPosition = reader.ReadInt();
                    }
                }

                quotes[i] = quote;
            }

            return(quotes);
        }
Пример #31
0
        /// <summary>
        /// Try create full book.
        /// </summary>
        /// <param name="change">Book change.</param>
        /// <returns>Full book.</returns>
        public QuoteChangeMessage TryApply(QuoteChangeMessage change)
        {
            if (change is null)
            {
                throw new ArgumentNullException(nameof(change));
            }

            if (change.State == null)
            {
                throw new ArgumentException(nameof(change));
            }

            var currState = _state;
            var newState  = change.State.Value;

            void CheckSwitch()
            {
                switch (currState)
                {
                case _none:
                case QuoteChangeStates.SnapshotStarted:
                {
                    if (newState != QuoteChangeStates.SnapshotBuilding && newState != QuoteChangeStates.SnapshotComplete)
                    {
                        _logs.AddDebugLog($"{currState}->{newState}");
                    }

                    break;
                }

                case QuoteChangeStates.SnapshotBuilding:
                {
                    if (newState != QuoteChangeStates.SnapshotBuilding && newState != QuoteChangeStates.SnapshotComplete)
                    {
                        _logs.AddDebugLog($"{currState}->{newState}");
                    }

                    break;
                }

                case QuoteChangeStates.SnapshotComplete:
                case QuoteChangeStates.Increment:
                {
                    if (newState == QuoteChangeStates.SnapshotBuilding)
                    {
                        _logs.AddDebugLog($"{currState}->{newState}");
                    }

                    break;
                }
                }
            }

            if (currState != newState || newState == QuoteChangeStates.SnapshotComplete)
            {
                CheckSwitch();

                if (newState == QuoteChangeStates.SnapshotStarted)
                {
                    _bids.Clear();
                    _asks.Clear();
                }

                switch (currState)
                {
                case _none:
                {
                    _bids.Clear();
                    _asks.Clear();

                    break;
                }

                case QuoteChangeStates.SnapshotStarted:
                    break;

                case QuoteChangeStates.SnapshotBuilding:
                    break;

                case QuoteChangeStates.SnapshotComplete:
                {
                    if (newState == QuoteChangeStates.SnapshotComplete)
                    {
                        _bids.Clear();
                        _asks.Clear();
                    }

                    break;
                }

                case QuoteChangeStates.Increment:
                    break;

                default:
                    throw new ArgumentOutOfRangeException(currState.ToString());
                }

                _state = currState = newState;
            }

            void Apply(IEnumerable <QuoteChange> from, SortedList <decimal, QuoteChange> to)
            {
                foreach (var quote in from)
                {
                    if (quote.Volume == 0)
                    {
                        to.Remove(quote.Price);
                    }
                    else
                    {
                        to[quote.Price] = quote;
                    }
                }
            }

            void ApplyByPos(IEnumerable <QuoteChange> from, List <QuoteChange> to)
            {
                foreach (var quote in from)
                {
                    var startPos = quote.StartPosition.Value;

                    switch (quote.Action)
                    {
                    case QuoteChangeActions.New:
                    {
                        var tuple = new QuoteChange(quote.Price, quote.Volume, quote.OrdersCount, quote.Condition);

                        if (startPos > to.Count)
                        {
                            throw new InvalidOperationException($"Pos={startPos}>Count={to.Count}");
                        }
                        else if (startPos == to.Count)
                        {
                            to.Add(tuple);
                        }
                        else
                        {
                            to.Insert(startPos, tuple);
                        }

                        break;
                    }

                    case QuoteChangeActions.Update:
                    {
                        to[startPos] = new QuoteChange(quote.Price, quote.Volume, quote.OrdersCount, quote.Condition);
                        break;
                    }

                    case QuoteChangeActions.Delete:
                    {
                        if (quote.EndPosition == null)
                        {
                            to.RemoveAt(startPos);
                        }
                        else
                        {
                            to.RemoveRange(startPos, (quote.EndPosition.Value - startPos) + 1);
                        }

                        break;
                    }

                    default:
                        throw new ArgumentOutOfRangeException(nameof(from), quote.Action, LocalizedStrings.Str1219);
                    }
                }
            }

            if (change.HasPositions)
            {
                ApplyByPos(change.Bids, _bidsByPos);
                ApplyByPos(change.Asks, _asksByPos);
            }
            else
            {
                Apply(change.Bids, _bids);
                Apply(change.Asks, _asks);
            }

            if (currState == QuoteChangeStates.SnapshotStarted || currState == QuoteChangeStates.SnapshotBuilding)
            {
                return(null);
            }

            QuoteChange[] bids;
            QuoteChange[] asks;

            if (change.HasPositions)
            {
                bids = _bidsByPos.ToArray();
                asks = _asksByPos.ToArray();
            }
            else
            {
                bids = _bids.Values.ToArray();
                asks = _asks.Values.ToArray();
            }

            return(new QuoteChangeMessage
            {
                SecurityId = SecurityId,
                Bids = bids,
                Asks = asks,
                ServerTime = change.ServerTime,
                OriginalTransactionId = change.OriginalTransactionId,
            });
        }
Пример #32
0
        private void SessionOnStil2Update(ref structSTIL2Update structL2Update)
        {
            var asksUpdate = _depths[structL2Update.bstrSymbol].Item1;
            var bidsUpdate = _depths[structL2Update.bstrSymbol].Item2;

            var quote = new QuoteChange(structL2Update.bstrSide.ToSide(), (decimal)structL2Update.fPrice, structL2Update.nQty)
            {
                BoardCode = structL2Update.bstrMaker
            };

            switch (structL2Update.bstrSide.ToSide())
            {
            case Sides.Buy:
            {
                switch (structL2Update.bstrAction)
                {
                case "A":                                 // add
                {
                    bidsUpdate.Add(quote);
                    break;
                }

                case "C":                                 // change
                {
                    bidsUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                    bidsUpdate.Add(quote);
                    break;
                }

                case "D":                                 // delete
                {
                    bidsUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                    break;
                }
                }

                break;
            }

            case Sides.Sell:
            {
                switch (structL2Update.bstrAction)
                {
                case "A":                                 // add
                {
                    asksUpdate.Add(quote);
                    break;
                }

                case "C":                                 // change
                {
                    asksUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                    asksUpdate.Add(quote);
                    break;
                }

                case "D":                                 // delete
                {
                    asksUpdate.RemoveWhere(q => q.Price == quote.Price && q.BoardCode == quote.BoardCode);
                    break;
                }
                }

                break;
            }
            }

            var message = new QuoteChangeMessage
            {
                SecurityId = new SecurityId
                {
                    SecurityCode = structL2Update.bstrSymbol,
                    BoardCode    = "All",
                },
                Asks       = asksUpdate,
                Bids       = bidsUpdate,
                ServerTime = structL2Update.bstrTime.StrToTime(),
            };

            SendOutMessage(message);
        }
Пример #33
0
		private IEnumerable<ExecutionMessage> ProcessQuoteChange(DateTime time, DateTimeOffset serverTime, QuoteChange[] newBids, QuoteChange[] newAsks)
		{
			decimal bestBidPrice;
			decimal bestAskPrice;

			var diff = new List<ExecutionMessage>();

			GetDiff(diff, time, serverTime, _bids, newBids, Sides.Buy, out bestBidPrice);
			GetDiff(diff, time, serverTime, _asks, newAsks, Sides.Sell, out bestAskPrice);

			var spreadPrice = bestAskPrice == 0
				? bestBidPrice
				: (bestBidPrice == 0
					? bestAskPrice
					: (bestAskPrice - bestBidPrice) / 2 + bestBidPrice);

			try
			{
				//при обновлении стакана необходимо учитывать направление сдвига, чтобы не было ложного исполнения при наложении бидов и асков.
				//т.е. если цена сдвинулась вниз, то обновление стакана необходимо начинать с минимального бида.
				return (spreadPrice < _currSpreadPrice)
					? diff.OrderBy(m => m.OrderPrice)
					: diff.OrderByDescending(m => m.OrderPrice);
			}
			finally
			{
				_currSpreadPrice = spreadPrice;
			}
		}
		private void ProcessLevel1Depth(Level1ChangeMessage message, decimal bestBidPrice, decimal bestBidVolume, decimal bestAskPrice, decimal bestAskVolume, List<ExecutionMessage> retVal)
		{
			if (message.LocalTime.Date == _lastDepthDate)
				return;

			QuoteChange ask = null;
			QuoteChange bid = null;

			if (bestAskPrice != 0 && bestAskVolume != 0)
				ask = new QuoteChange(Sides.Sell, bestAskPrice, bestAskVolume);

			if (bestBidPrice != 0 && bestBidVolume != 0)
				bid = new QuoteChange(Sides.Buy, bestBidPrice, bestBidVolume);

			if (ask == null && bid == null)
				return;

			retVal.AddRange(ProcessQuoteChange(message.LocalTime, message.ServerTime,
				bid != null ? new[] { bid } : ArrayHelper.Empty<QuoteChange>(),
				ask != null ? new[] { ask } : ArrayHelper.Empty<QuoteChange>()));
		}
		// mika
		// debug code for check build algo

		//private readonly Dictionary<long, decimal> _pendingMatch = new Dictionary<long, decimal>();
		//private readonly Dictionary<long, Tuple<Sides, decimal, decimal>> _activeOrders = new Dictionary<long, Tuple<Sides, decimal, decimal>>();

		//private QuoteChangeMessage BuildDepth()
		//{
		//	var bids = new SortedDictionary<decimal, QuoteChange>(new BackwardComparer<decimal>());
		//	var asks = new SortedDictionary<decimal, QuoteChange>();

		//	foreach (var pair in _activeOrders)
		//	{
		//		var quotes = pair.Value.Item1 == Sides.Buy ? bids : asks;

		//		var quote = quotes.TryGetValue(pair.Value.Item2);

		//		if (quote == null)
		//		{
		//			quote = new QuoteChange(pair.Value.Item1, pair.Value.Item2, pair.Value.Item3);
		//			quotes.Add(pair.Value.Item2, quote);
		//		}
		//		else
		//			quote.Volume += pair.Value.Item3;
		//	}

		//	return new QuoteChangeMessage
		//	{
		//		Bids = bids.Values.ToArray(),
		//		Asks = asks.Values.ToArray()
		//	};
		//}

		/// <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");

			// mika
			// debug code for check build algo

			//var orderId = item.OrderId.Value;
			//var orderVol = item.Volume.Value;

			//if (item.IsOrderLogRegistered())
			//{
			//	var vol = _pendingMatch.TryGetValue2(orderId);

			//	if (vol != null)
			//		vol -= orderVol;
			//	else
			//		vol = orderVol;

			//	if (vol > 0)
			//		_activeOrders.Add(orderId, Tuple.Create(item.Side, item.Price, vol.Value));
			//}
			//else if (item.IsOrderLogMatched())
			//{
			//	var t = _activeOrders.TryGetValue(orderId);

			//	if (t != null)
			//	{
			//		var vol = t.Item3;

			//		vol -= orderVol;

			//		if (vol < 0)
			//			throw new Exception();

			//		if (vol == 0)
			//			_activeOrders.Remove(orderId);
			//		else
			//			_activeOrders[orderId] = Tuple.Create(item.Side, item.Price, vol);
			//	}
			//	else
			//	{
			//		var vol = _pendingMatch.TryGetValue2(orderId);

			//		if (vol == null)
			//			vol = orderVol;
			//		else
			//			vol += orderVol;

			//		_pendingMatch[orderId] = vol.Value;
			//	}
			//}
			//else if (item.IsOrderLogCanceled())
			//	_activeOrders.Remove(orderId);

			var volume = item.GetVolume();

			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 == false || item.TradePrice != null || 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 = volume,
								};

								quotes.Add(item.Price, quote);

								if (item.Side == Sides.Buy)
									_depth.Bids = GetArray(quotes);
								else
									_depth.Asks = GetArray(quotes);
							}
							else
								quote.Volume += 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 -= 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;
		}