Beispiel #1
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <TCandleMessage> candles, CandleMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstCandle = candles.First();

                metaInfo.FirstPrice   = firstCandle.LowPrice;
                metaInfo.LastPrice    = firstCandle.LowPrice;
                metaInfo.ServerOffset = firstCandle.OpenTime.Offset;
            }

            writer.WriteInt(candles.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version49;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version53;

            foreach (var candle in candles)
            {
                writer.WriteVolume(candle.TotalVolume, metaInfo, SecurityId);

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    writer.WriteVolume(candle.RelativeVolume ?? 0, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(candle.RelativeVolume != null);

                    if (candle.RelativeVolume != null)
                    {
                        writer.WriteVolume(candle.RelativeVolume.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version56)
                {
                    writer.WritePrice(candle.LowPrice, metaInfo.LastPrice, metaInfo, SecurityId);
                    metaInfo.LastPrice = candle.LowPrice;

                    writer.WritePrice(candle.OpenPrice, metaInfo.LastPrice, metaInfo, SecurityId);
                    writer.WritePrice(candle.ClosePrice, metaInfo.LastPrice, metaInfo, SecurityId);
                    writer.WritePrice(candle.HighPrice, metaInfo.LastPrice, metaInfo, SecurityId);
                }
                else
                {
                    writer.WritePriceEx(candle.LowPrice, metaInfo, SecurityId);

                    if (candle.OpenPrice <= candle.ClosePrice)
                    {
                        writer.Write(true);

                        writer.WritePriceEx(candle.OpenPrice, metaInfo, SecurityId);
                        writer.WritePriceEx(candle.ClosePrice, metaInfo, SecurityId);
                    }
                    else
                    {
                        writer.Write(false);

                        writer.WritePriceEx(candle.ClosePrice, metaInfo, SecurityId);
                        writer.WritePriceEx(candle.OpenPrice, metaInfo, SecurityId);
                    }

                    writer.WritePriceEx(candle.HighPrice, metaInfo, SecurityId);
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(candle.OpenTime, metaInfo.LastTime, LocalizedStrings.Str998, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    var isAll = !candle.HighTime.IsDefault() && !candle.LowTime.IsDefault();

                    DateTimeOffset first;
                    DateTimeOffset second;

                    writer.Write(isAll);

                    if (isAll)
                    {
                        var isOrdered = candle.HighTime <= candle.LowTime;
                        writer.Write(isOrdered);

                        first  = isOrdered ? candle.HighTime : candle.LowTime;
                        second = isOrdered ? candle.LowTime : candle.HighTime;
                    }
                    else
                    {
                        writer.Write(!candle.HighTime.IsDefault());
                        writer.Write(!candle.LowTime.IsDefault());

                        if (candle.HighTime.IsDefault())
                        {
                            first  = candle.LowTime;
                            second = default(DateTimeOffset);
                        }
                        else
                        {
                            first  = candle.HighTime;
                            second = default(DateTimeOffset);
                        }
                    }

                    if (!first.IsDefault())
                    {
                        if (first.Offset != lastOffset)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(first, lastOffset));
                        }

                        metaInfo.LastTime = writer.WriteTime(first, metaInfo.LastTime, LocalizedStrings.Str999, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                    }

                    if (!second.IsDefault())
                    {
                        if (second.Offset != lastOffset)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(second, lastOffset));
                        }

                        metaInfo.LastTime = writer.WriteTime(second, metaInfo.LastTime, LocalizedStrings.Str1000, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version47)
                {
                    writer.Write(!candle.CloseTime.IsDefault());

                    if (!candle.CloseTime.IsDefault())
                    {
                        if (candle.CloseTime.Offset != lastOffset)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(candle.CloseTime, lastOffset));
                        }

                        metaInfo.LastTime = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                    }
                }
                else
                {
                    var time = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);

                    if (metaInfo.Version >= MarketDataVersions.Version41)
                    {
                        metaInfo.LastTime = time;
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    if (metaInfo.Version < MarketDataVersions.Version51)
                    {
                        writer.WriteVolume(candle.OpenVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.HighVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.LowVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.CloseVolume ?? 0m, metaInfo, SecurityId);
                    }
                    else
                    {
                        if (candle.OpenVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.OpenVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.HighVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.HighVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.LowVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.LowVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.CloseVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.CloseVolume.Value, metaInfo, SecurityId);
                        }
                    }
                }

                writer.WriteInt((int)candle.State);

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    continue;
                }

                var oi = candle.OpenInterest;

                if (metaInfo.Version < MarketDataVersions.Version48)
                {
                    writer.WriteVolume(oi ?? 0m, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(oi != null);

                    if (oi != null)
                    {
                        writer.WriteVolume(oi.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    continue;
                }

                writer.Write(candle.DownTicks != null);

                if (candle.DownTicks != null)
                {
                    writer.WriteInt(candle.DownTicks.Value);
                }

                writer.Write(candle.UpTicks != null);

                if (candle.UpTicks != null)
                {
                    writer.WriteInt(candle.UpTicks.Value);
                }

                writer.Write(candle.TotalTicks != null);

                if (candle.TotalTicks != null)
                {
                    writer.WriteInt(candle.TotalTicks.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version54)
                {
                    continue;
                }

                var priceLevels = candle.PriceLevels;

                writer.Write(priceLevels != null);

                if (priceLevels == null)
                {
                    continue;
                }

                priceLevels = priceLevels.ToArray();

                writer.WriteInt(priceLevels.Count());

                foreach (var level in priceLevels)
                {
                    if (metaInfo.Version < MarketDataVersions.Version56)
                    {
                        writer.WritePrice(level.Price, metaInfo.LastPrice, metaInfo, SecurityId);
                    }
                    else
                    {
                        writer.WritePriceEx(level.Price, metaInfo, SecurityId);
                    }

                    writer.WriteInt(level.BuyCount);
                    writer.WriteInt(level.SellCount);

                    writer.WriteVolume(level.BuyVolume, metaInfo, SecurityId);
                    writer.WriteVolume(level.SellVolume, metaInfo, SecurityId);

                    if (metaInfo.Version >= MarketDataVersions.Version55)
                    {
                        writer.WriteVolume(level.TotalVolume, metaInfo, SecurityId);
                    }

                    var volumes = level.BuyVolumes;

                    if (volumes == null)
                    {
                        writer.Write(false);
                    }
                    else
                    {
                        writer.Write(true);

                        volumes = volumes.ToArray();

                        writer.WriteInt(volumes.Count());

                        foreach (var volume in volumes)
                        {
                            writer.WriteVolume(volume, metaInfo, SecurityId);
                        }
                    }

                    volumes = level.SellVolumes;

                    if (volumes == null)
                    {
                        writer.Write(false);
                    }
                    else
                    {
                        writer.Write(true);

                        volumes = volumes.ToArray();

                        writer.WriteInt(volumes.Count());

                        foreach (var volume in volumes)
                        {
                            writer.WriteVolume(volume, metaInfo, SecurityId);
                        }
                    }
                }
            }
        }
Beispiel #2
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <QuoteChangeMessage> messages, QuoteMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstDepth = messages.FirstOrDefault(d => !d.Bids.IsEmpty() || !d.Asks.IsEmpty());

                //var price = firstDepth != null ? GetDepthPrice(firstDepth) : 0;

                //if (price != 0)
                //{
                //	if ((price % metaInfo.PriceStep) == 0)
                //		metaInfo.LastPrice = metaInfo.FirstPrice = price;
                //	else
                //		metaInfo.LastFractionalPrice = metaInfo.FirstFractionalPrice = price;
                //}

                metaInfo.ServerOffset = (firstDepth ?? messages.First()).ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            QuoteChangeMessage prevQuoteMsg = null;

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version47;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version52;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version53;
            var nonAdjustPrice   = metaInfo.Version >= MarketDataVersions.Version54;
            var useLong          = metaInfo.Version >= MarketDataVersions.Version55;

            foreach (var m in messages)
            {
                var quoteMsg = m;

                //if (m.IsFullEmpty())
                //	throw new ArgumentException(LocalizedStrings.Str1309, nameof(messages));

                if (!quoteMsg.IsSorted)
                {
                    quoteMsg = (QuoteChangeMessage)quoteMsg.Clone();

                    quoteMsg.Bids = quoteMsg.Bids.OrderByDescending(q => q.Price).ToArray();
                    quoteMsg.Asks = quoteMsg.Asks.OrderBy(q => q.Price).ToArray();
                }

                //var bid = quoteMsg.GetBestBid();
                //var ask = quoteMsg.GetBestAsk();

                // LMAX has equals best bid and ask
                //if (bid != null && ask != null && bid.Price > ask.Price)
                //	throw new ArgumentException(LocalizedStrings.Str932Params.Put(bid.Price, ask.Price, quoteMsg.ServerTime), nameof(messages));

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(quoteMsg.ServerTime, metaInfo.LastTime, LocalizedStrings.MarketDepth, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                var isFull = prevQuoteMsg == null;

                writer.Write(isFull);

                var delta = isFull ? quoteMsg : prevQuoteMsg.GetDelta(quoteMsg);

                prevQuoteMsg = quoteMsg;

                SerializeQuotes(writer, delta.Bids, metaInfo /*, isFull*/, useLong, nonAdjustPrice);
                SerializeQuotes(writer, delta.Asks, metaInfo /*, isFull*/, useLong, nonAdjustPrice);

                //metaInfo.LastPrice = GetDepthPrice(quoteMsg);

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteLong((quoteMsg.LocalTime - quoteMsg.ServerTime).Ticks);
                }
                else
                {
                    var hasLocalTime = true;

                    if (metaInfo.Version >= MarketDataVersions.Version49)
                    {
                        hasLocalTime = quoteMsg.HasLocalTime(quoteMsg.ServerTime);
                        writer.Write(hasLocalTime);
                    }

                    if (hasLocalTime)
                    {
                        lastOffset               = metaInfo.LastLocalOffset;
                        metaInfo.LastLocalTime   = writer.WriteTime(quoteMsg.LocalTime, metaInfo.LastLocalTime, LocalizedStrings.Str934, allowNonOrdered, isUtc, metaInfo.LocalOffset, allowDiffOffsets, isTickPrecision, ref lastOffset, true);
                        metaInfo.LastLocalOffset = lastOffset;
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version51)
                {
                    continue;
                }

                writer.Write(quoteMsg.Currency != null);

                if (quoteMsg.Currency != null)
                {
                    writer.WriteInt((int)quoteMsg.Currency.Value);
                }
            }
        }
Beispiel #3
0
        private void SerializeQuotes(BitArrayWriter writer, QuoteChange[] quotes, QuoteMetaInfo metaInfo /*, bool isFull*/, bool useLong, bool nonAdjustPrice)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

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

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

            writer.WriteInt(quotes.Length);

            foreach (var quote in quotes)
            {
                // quotes for indices may have zero prices
                //if (quote.Price <= 0)
                //	throw new ArgumentOutOfRangeException(nameof(quotes), quote.Price, LocalizedStrings.Str935);

                // some forex connectors do not translate volume
                //
                if (quote.Volume < 0 /* || (isFull && quote.Volume == 0)*/)
                {
                    throw new ArgumentOutOfRangeException(nameof(quotes), quote.Volume, LocalizedStrings.Str936);
                }

                var pricePrice = metaInfo.LastPrice;
                writer.WritePrice(quote.Price, ref pricePrice, metaInfo, SecurityId, useLong, nonAdjustPrice);
                metaInfo.LastPrice = pricePrice;

                writer.WriteVolume(quote.Volume, metaInfo, SecurityId);

                if (metaInfo.Version < MarketDataVersions.Version56)
                {
                    continue;
                }

                writer.WriteNullableInt(quote.OrdersCount);

                if (metaInfo.Version < MarketDataVersions.Version57)
                {
                    continue;
                }

                if (quote.Condition != default)
                {
                    writer.Write(true);
                    writer.WriteInt((int)quote.Condition);
                }
                else
                {
                    writer.Write(false);
                }
            }
        }
Beispiel #4
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <ExecutionMessage> items, OrderLogMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty() && !items.IsEmpty())
            {
                var item = items.First();

                metaInfo.FirstOrderId       = metaInfo.LastOrderId = item.SafeGetOrderId();
                metaInfo.FirstTransactionId = metaInfo.LastTransactionId = item.TransactionId;
                metaInfo.ServerOffset       = item.ServerTime.Offset;
            }

            writer.WriteInt(items.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version47;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version48;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version52;

            foreach (var item in items)
            {
                var hasTrade = item.TradeId != null || item.TradePrice != null;

                var orderId = item.SafeGetOrderId();
                if (orderId < 0)
                {
                    throw new ArgumentOutOfRangeException("items", orderId, LocalizedStrings.Str925);
                }

                // sell market orders has zero price (if security do not have min allowed price)
                // execution ticks (like option execution) may be a zero cost
                // ticks for spreads may be a zero cost or less than zero
                //if (item.Price < 0)
                //	throw new ArgumentOutOfRangeException("items", item.Price, LocalizedStrings.Str926Params.Put(item.OrderId));

                var volume = item.SafeGetVolume();
                if (volume <= 0)
                {
                    throw new ArgumentOutOfRangeException("items", volume, LocalizedStrings.Str927Params.Put(item.OrderId));
                }

                long?tradeId = null;

                if (hasTrade)
                {
                    tradeId = item.GetTradeId();

                    if (tradeId <= 0)
                    {
                        throw new ArgumentOutOfRangeException("items", tradeId, LocalizedStrings.Str1012Params.Put(item.OrderId));
                    }

                    // execution ticks (like option execution) may be a zero cost
                    // ticks for spreads may be a zero cost or less than zero
                    //if (item.TradePrice <= 0)
                    //	throw new ArgumentOutOfRangeException("items", item.TradePrice, LocalizedStrings.Str929Params.Put(item.TradeId, item.OrderId));
                }

                metaInfo.LastOrderId = writer.SerializeId(orderId, metaInfo.LastOrderId);

                var orderPrice = item.Price;

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    writer.WritePriceEx(orderPrice, metaInfo, SecurityId);
                }
                else
                {
                    var isAligned = (orderPrice % metaInfo.PriceStep) == 0;
                    writer.Write(isAligned);

                    if (isAligned)
                    {
                        if (metaInfo.FirstOrderPrice == 0)
                        {
                            metaInfo.FirstOrderPrice = metaInfo.LastOrderPrice = orderPrice;
                        }

                        writer.WritePrice(orderPrice, metaInfo.LastOrderPrice, metaInfo, SecurityId, true);
                        metaInfo.LastOrderPrice = orderPrice;
                    }
                    else
                    {
                        if (metaInfo.FirstNonSystemPrice == 0)
                        {
                            metaInfo.FirstNonSystemPrice = metaInfo.LastNonSystemPrice = orderPrice;
                        }

                        metaInfo.LastNonSystemPrice = writer.WriteDecimal(orderPrice, metaInfo.LastNonSystemPrice);
                    }
                }

                writer.WriteVolume(volume, metaInfo, SecurityId);

                writer.Write(item.Side == Sides.Buy);

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(item.ServerTime, metaInfo.LastTime, LocalizedStrings.Str1013, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (hasTrade)
                {
                    writer.Write(true);

                    if (metaInfo.FirstTradeId == 0)
                    {
                        metaInfo.FirstTradeId = metaInfo.LastTradeId = tradeId.Value;
                    }

                    metaInfo.LastTradeId = writer.SerializeId(tradeId.Value, metaInfo.LastTradeId);

                    writer.WritePriceEx(item.GetTradePrice(), metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(false);
                    writer.Write(item.OrderState == OrderStates.Active);
                }

                if (metaInfo.Version < MarketDataVersions.Version31)
                {
                    continue;
                }

                writer.WriteNullableInt(item.OrderStatus);

                if (metaInfo.Version < MarketDataVersions.Version33)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version50)
                {
                    writer.WriteInt((int)(item.TimeInForce ?? TimeInForce.PutInQueue));
                }
                else
                {
                    writer.Write(item.TimeInForce != null);

                    if (item.TimeInForce != null)
                    {
                        writer.WriteInt((int)item.TimeInForce.Value);
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version49)
                {
                    writer.Write(item.IsSystem != null);

                    if (item.IsSystem != null)
                    {
                        writer.Write(item.IsSystem.Value);
                    }
                }
                else
                {
                    writer.Write(item.IsSystem ?? true);
                }

                if (metaInfo.Version < MarketDataVersions.Version34)
                {
                    continue;
                }

                metaInfo.LastTransactionId = writer.SerializeId(item.TransactionId, metaInfo.LastTransactionId);

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteLong(0 /*item.Latency.Ticks*/);
                }

                var portfolio = item.PortfolioName;
                var isEmptyPf = portfolio == null || portfolio == Portfolio.AnonymousPortfolio.Name;

                writer.Write(!isEmptyPf);

                if (!isEmptyPf)
                {
                    metaInfo.Portfolios.TryAdd(item.PortfolioName);
                    writer.WriteInt(metaInfo.Portfolios.IndexOf(item.PortfolioName));
                }

                if (metaInfo.Version < MarketDataVersions.Version51)
                {
                    continue;
                }

                writer.Write(item.Currency != null);

                if (item.Currency != null)
                {
                    writer.WriteInt((int)item.Currency.Value);
                }
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <QuoteChangeMessage> messages, QuoteMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstDepth = messages.First();                //FirstOrDefault(d => !d.Bids.IsEmpty() || !d.Asks.IsEmpty());

                //var price = firstDepth != null ? GetDepthPrice(firstDepth) : 0;

                //if (price != 0)
                //{
                //	if ((price % metaInfo.PriceStep) == 0)
                //		metaInfo.LastPrice = metaInfo.FirstPrice = price;
                //	else
                //		metaInfo.LastFractionalPrice = metaInfo.FirstFractionalPrice = price;
                //}

                metaInfo.ServerOffset    = firstDepth.ServerTime.Offset;
                metaInfo.IncrementalOnly = firstDepth.State != null;
                metaInfo.FirstSeqNum     = metaInfo.PrevSeqNum = firstDepth.SeqNum;
            }

            writer.WriteInt(messages.Count());

            QuoteChangeMessage prevQuoteMsg = null;

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version47;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version52;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version53;
            var nonAdjustPrice   = metaInfo.Version >= MarketDataVersions.Version54;
            var useLong          = metaInfo.Version >= MarketDataVersions.Version55;
            var buildFrom        = metaInfo.Version >= MarketDataVersions.Version59;
            var seqNumAndPos     = metaInfo.Version >= MarketDataVersions.Version60;

            foreach (var m in messages)
            {
                var quoteMsg = m;

                if (metaInfo.IncrementalOnly)
                {
                    if (quoteMsg.State == null)
                    {
                        throw new InvalidOperationException(LocalizedStrings.StorageRequiredIncremental.Put(true));
                    }
                }
                else
                {
                    if (quoteMsg.State != null)
                    {
                        throw new InvalidOperationException(LocalizedStrings.StorageRequiredIncremental.Put(false));
                    }
                }

                //if (m.IsFullEmpty())
                //	throw new ArgumentException(LocalizedStrings.Str1309, nameof(messages));

                //var bid = quoteMsg.GetBestBid();
                //var ask = quoteMsg.GetBestAsk();

                // LMAX has equals best bid and ask
                //if (bid != null && ask != null && bid.Price > ask.Price)
                //	throw new ArgumentException(LocalizedStrings.Str932Params.Put(bid.Price, ask.Price, quoteMsg.ServerTime), nameof(messages));

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(quoteMsg.ServerTime, metaInfo.LastTime, LocalizedStrings.MarketDepth, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                QuoteChangeMessage delta;

                if (metaInfo.IncrementalOnly)
                {
                    writer.WriteInt((int)quoteMsg.State.Value);
                    delta = quoteMsg;
                }
                else
                {
                    var isFull = prevQuoteMsg == null;

                    writer.Write(isFull);

                    delta = isFull ? quoteMsg : prevQuoteMsg.GetDelta(quoteMsg);

                    prevQuoteMsg = quoteMsg;
                }

                SerializeQuotes(writer, delta.Bids, metaInfo /*, isFull*/, useLong, nonAdjustPrice);
                SerializeQuotes(writer, delta.Asks, metaInfo /*, isFull*/, useLong, nonAdjustPrice);

                //metaInfo.LastPrice = GetDepthPrice(quoteMsg);

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteLong((quoteMsg.LocalTime - quoteMsg.ServerTime).Ticks);
                }
                else
                {
                    var hasLocalTime = true;

                    if (metaInfo.Version >= MarketDataVersions.Version49)
                    {
                        hasLocalTime = quoteMsg.HasLocalTime(quoteMsg.ServerTime);
                        writer.Write(hasLocalTime);
                    }

                    if (hasLocalTime)
                    {
                        lastOffset               = metaInfo.LastLocalOffset;
                        metaInfo.LastLocalTime   = writer.WriteTime(quoteMsg.LocalTime, metaInfo.LastLocalTime, LocalizedStrings.Str934, allowNonOrdered, isUtc, metaInfo.LocalOffset, allowDiffOffsets, isTickPrecision, ref lastOffset, true);
                        metaInfo.LastLocalOffset = lastOffset;
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version51)
                {
                    continue;
                }

                writer.Write(quoteMsg.Currency != null);

                if (quoteMsg.Currency != null)
                {
                    writer.WriteInt((int)quoteMsg.Currency.Value);
                }

                if (!buildFrom)
                {
                    continue;
                }

                writer.WriteBuildFrom(quoteMsg.BuildFrom);

                if (!seqNumAndPos)
                {
                    continue;
                }

                writer.WriteSeqNum(quoteMsg, metaInfo);
                writer.Write(quoteMsg.HasPositions);
            }
        }
Beispiel #6
0
        public static void WritePrice(this BitArrayWriter writer, decimal price, ref decimal prevPrice, BinaryMetaInfo info, SecurityId securityId, bool useLong = false, bool nonAdjustPrice = false)
        {
            var priceStep = info.LastPriceStep;

            if (priceStep == 0)
            {
                throw new InvalidOperationException(LocalizedStrings.Str2925);
            }

            if ((price % priceStep) != 0)
            {
                if (!nonAdjustPrice)
                {
                    throw new ArgumentException(LocalizedStrings.Str1007Params.Put(priceStep, securityId, price), nameof(info));
                }

                writer.Write(false);

                var priceStepChanged = false;

                if ((price % info.LastPriceStep) != 0)
                {
                    var newPriceStep = 1m;

                    var found = false;

                    for (var i = 0; i < 20; i++)
                    {
                        if ((price % newPriceStep) == 0)
                        {
                            found = true;
                            break;
                        }

                        newPriceStep /= 10;
                    }

                    if (!found)
                    {
                        throw new ArgumentException(LocalizedStrings.Str1007Params.Put(priceStep, securityId, price), nameof(info));
                    }

                    info.LastPriceStep = newPriceStep;

                    //if (info.FirstPriceStep == 0)
                    //	info.FirstPriceStep = info.LastPriceStep;

                    priceStepChanged = true;
                }

                writer.Write(priceStepChanged);

                if (priceStepChanged)
                {
                    WriteDecimal(writer, info.LastPriceStep, 0);
                }

                if (info.FirstFractionalPrice == 0)
                {
                    info.FirstFractionalPrice = info.LastFractionalPrice = price;
                }

                var stepCount = (long)((price - info.LastFractionalPrice) / info.LastPriceStep);

                if (useLong)
                {
                    writer.WriteLong(stepCount);
                }
                else
                {
                    writer.WriteInt((int)stepCount);
                }

                info.LastFractionalPrice = price;
                return;
            }

            if (nonAdjustPrice)
            {
                writer.Write(true);
            }

            try
            {
                var stepCount = (long)((price - prevPrice) / priceStep);

                // ОЛ может содержать заявки с произвольно большими ценами
                if (useLong)
                {
                    writer.WriteLong(stepCount);
                }
                else
                {
                    if (stepCount.Abs() > int.MaxValue)
                    {
                        throw new InvalidOperationException("Range is overflow.");
                    }

                    writer.WriteInt((int)stepCount);
                }

                prevPrice = price;
            }
            catch (OverflowException ex)
            {
                throw new ArgumentException(LocalizedStrings.Str1008Params.Put(price, prevPrice, priceStep, useLong), ex);
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <NewsMessage> messages, NewsMetaInfo metaInfo)
        {
            var isMetaEmpty = metaInfo.IsEmpty();

            writer.WriteInt(messages.Count());

            foreach (var news in messages)
            {
                if (isMetaEmpty)
                {
                    metaInfo.ServerOffset = news.ServerTime.Offset;
                    isMetaEmpty           = false;
                }

                if (news.Id.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Id);
                }

                writer.WriteString(news.Headline);

                if (news.Story.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Story);
                }

                if (news.Source.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Source);
                }

                if (news.BoardCode.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.BoardCode);
                }

                if (news.SecurityId == null)
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.SecurityId.Value.SecurityCode);
                }

                if (news.Url == null)
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Url.To <string>());
                }

                metaInfo.LastTime = writer.WriteTime(news.ServerTime, metaInfo.LastTime, LocalizedStrings.News, true, true, metaInfo.ServerOffset);
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <NewsMessage> messages, NewsMetaInfo metaInfo)
        {
            var isMetaEmpty = metaInfo.IsEmpty();

            writer.WriteInt(messages.Count());

            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version46;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version47;

            foreach (var news in messages)
            {
                if (isMetaEmpty)
                {
                    metaInfo.ServerOffset = news.ServerTime.Offset;
                    isMetaEmpty           = false;
                }

                if (news.Id.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Id);
                }

                writer.WriteString(news.Headline);

                if (news.Story.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Story);
                }

                if (news.Source.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Source);
                }

                if (news.BoardCode.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.BoardCode);
                }

                if (news.SecurityId == null)
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.SecurityId.Value.SecurityCode);
                }

                if (news.Url == null)
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);
                    writer.WriteString(news.Url.To <string>());
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(news.ServerTime, metaInfo.LastTime, LocalizedStrings.News, true, true, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <QuoteChangeMessage> messages, QuoteMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstDepth = messages.FirstOrDefault(d => !d.Bids.IsEmpty() || !d.Asks.IsEmpty());

                metaInfo.LastPrice = metaInfo.FirstPrice = firstDepth != null?GetDepthPrice(firstDepth) : 0;

                metaInfo.ServerOffset = (firstDepth ?? messages.First()).ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            QuoteChangeMessage prevQuoteMsg = null;

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version47;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version52;

            foreach (var m in messages)
            {
                var quoteMsg = m;

                //if (depth.IsFullEmpty())
                //	throw new ArgumentException("Переданный стакан является пустым.", "depths");

                if (!quoteMsg.IsSorted)
                {
                    quoteMsg = (QuoteChangeMessage)quoteMsg.Clone();

                    quoteMsg.Bids = quoteMsg.Bids.OrderByDescending(q => q.Price).ToArray();
                    quoteMsg.Asks = quoteMsg.Asks.OrderBy(q => q.Price).ToArray();
                }

                var bid = quoteMsg.GetBestBid();
                var ask = quoteMsg.GetBestAsk();

                // LMAX has equals best bid and ask
                if (bid != null && ask != null && bid.Price > ask.Price)
                {
                    throw new ArgumentException(LocalizedStrings.Str932Params.Put(bid.Price, ask.Price, quoteMsg.ServerTime), nameof(messages));
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(quoteMsg.ServerTime, metaInfo.LastTime, LocalizedStrings.MarketDepth, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                var isFull = prevQuoteMsg == null;

                writer.Write(isFull);

                var delta = isFull ? quoteMsg : prevQuoteMsg.GetDelta(quoteMsg);

                prevQuoteMsg = quoteMsg;

                SerializeQuotes(writer, delta.Bids, metaInfo /*, isFull*/);
                SerializeQuotes(writer, delta.Asks, metaInfo /*, isFull*/);

                metaInfo.LastPrice = GetDepthPrice(quoteMsg);

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteLong(SecurityId.GetLatency(quoteMsg.ServerTime, quoteMsg.LocalTime).Ticks);
                }
                else
                {
                    var hasLocalTime = true;

                    if (metaInfo.Version >= MarketDataVersions.Version49)
                    {
                        hasLocalTime = !quoteMsg.LocalTime.IsDefault() && quoteMsg.LocalTime != quoteMsg.ServerTime;
                        writer.Write(hasLocalTime);
                    }

                    if (hasLocalTime)
                    {
                        lastOffset               = metaInfo.LastLocalOffset;
                        metaInfo.LastLocalTime   = writer.WriteTime(quoteMsg.LocalTime, metaInfo.LastLocalTime, LocalizedStrings.Str934, allowNonOrdered, isUtc, metaInfo.LocalOffset, allowDiffOffsets, ref lastOffset);
                        metaInfo.LastLocalOffset = lastOffset;
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version51)
                {
                    continue;
                }

                writer.Write(quoteMsg.Currency != null);

                if (quoteMsg.Currency != null)
                {
                    writer.WriteInt((int)quoteMsg.Currency.Value);
                }
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <NewsMessage> messages, NewsMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var first = messages.First();

                metaInfo.ServerOffset = first.ServerTime.Offset;
                metaInfo.FirstSeqNum  = metaInfo.PrevSeqNum = first.SeqNum;
            }

            writer.WriteInt(messages.Count());

            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version46;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version47;
            var seqNum           = metaInfo.Version >= MarketDataVersions.Version51;

            foreach (var news in messages)
            {
                writer.WriteStringEx(news.Id);

                writer.WriteString(news.Headline);

                writer.WriteStringEx(news.Story);
                writer.WriteStringEx(news.Source);
                writer.WriteStringEx(news.BoardCode);
                writer.WriteStringEx(news.SecurityId?.SecurityCode);
                writer.WriteStringEx(news.Url);

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(news.ServerTime, metaInfo.LastTime, LocalizedStrings.News, true, true, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (metaInfo.Version < MarketDataVersions.Version48)
                {
                    continue;
                }

                writer.Write(news.Priority != null);

                if (news.Priority != null)
                {
                    writer.WriteInt((int)news.Priority.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version49)
                {
                    continue;
                }

                writer.WriteStringEx(news.Language);

                if (metaInfo.Version < MarketDataVersions.Version50)
                {
                    continue;
                }

                writer.Write(news.ExpiryDate != null);

                if (news.ExpiryDate != null)
                {
                    writer.WriteLong(news.ExpiryDate.Value.To <long>());
                }

                writer.WriteStringEx(news.SecurityId?.BoardCode);

                if (!seqNum)
                {
                    continue;
                }

                writer.WriteSeqNum(news, metaInfo);
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <PositionChangeMessage> messages, PositionMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var msg = messages.First();

                metaInfo.ServerOffset = msg.ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            foreach (var message in messages)
            {
                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(message.ServerTime, metaInfo.LastTime, "level1", true, true, metaInfo.ServerOffset, true, true, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                var hasLocalTime = message.HasLocalTime(message.ServerTime);

                writer.Write(hasLocalTime);

                if (hasLocalTime)
                {
                    lastOffset               = metaInfo.LastLocalOffset;
                    metaInfo.LastLocalTime   = writer.WriteTime(message.LocalTime, metaInfo.LastLocalTime, LocalizedStrings.Str919, true, true, metaInfo.LocalOffset, true, true, ref lastOffset, true);
                    metaInfo.LastLocalOffset = lastOffset;
                }

                metaInfo.Portfolios.TryAdd(message.PortfolioName);
                writer.WriteInt(metaInfo.Portfolios.IndexOf(message.PortfolioName));

                if (message.ClientCode.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);

                    metaInfo.ClientCodes.TryAdd(message.ClientCode);
                    writer.WriteInt(metaInfo.ClientCodes.IndexOf(message.ClientCode));
                }

                if (message.DepoName.IsEmpty())
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);

                    metaInfo.DepoNames.TryAdd(message.DepoName);
                    writer.WriteInt(metaInfo.DepoNames.IndexOf(message.DepoName));
                }

                writer.Write(message.LimitType != null);

                if (message.LimitType != null)
                {
                    writer.WriteInt((int)message.LimitType.Value);
                }

                var count = message.Changes.Count;

                if (count == 0)
                {
                    throw new ArgumentException(LocalizedStrings.Str920, nameof(messages));
                }

                writer.WriteInt(count);

                foreach (var change in message.Changes)
                {
                    writer.WriteInt((int)change.Key);

                    switch (change.Key)
                    {
                    case PositionChangeTypes.BeginValue:
                        SerializeChange(writer, metaInfo.BeginValue, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.CurrentValue:
                        SerializeChange(writer, metaInfo.CurrentValue, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.BlockedValue:
                        SerializeChange(writer, metaInfo.BlockedValue, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.CurrentPrice:
                        SerializeChange(writer, metaInfo.CurrentPrice, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.AveragePrice:
                        SerializeChange(writer, metaInfo.AveragePrice, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.UnrealizedPnL:
                        SerializeChange(writer, metaInfo.UnrealizedPnL, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.RealizedPnL:
                        SerializeChange(writer, metaInfo.RealizedPnL, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.VariationMargin:
                        SerializeChange(writer, metaInfo.VariationMargin, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.Currency:
                        writer.WriteInt((int)(CurrencyTypes)change.Value);
                        break;

                    case PositionChangeTypes.Leverage:
                        SerializeChange(writer, metaInfo.Leverage, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.Commission:
                        SerializeChange(writer, metaInfo.Commission, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.CurrentValueInLots:
                        SerializeChange(writer, metaInfo.CurrentValueInLots, (decimal)change.Value);
                        break;

                    case PositionChangeTypes.State:
                        writer.WriteInt((int)(PortfolioStates)change.Value);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }
Beispiel #12
0
        public static DateTime WriteTime(this BitArrayWriter writer, DateTimeOffset dto, DateTime prevTime, string name, bool allowNonOrdered, bool isUtc, TimeSpan offset, bool allowDiffOffsets, ref TimeSpan prevOffset)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            if (allowDiffOffsets)
            {
                writer.Write(dto.Offset == prevOffset);

                if (prevOffset != dto.Offset)
                {
                    prevOffset = dto.Offset;

                    writer.WriteInt(prevOffset.Hours);

                    writer.Write(prevOffset.Minutes == 0);

                    if (prevOffset.Minutes != 0)
                    {
                        writer.WriteInt(prevOffset.Minutes);
                    }
                }
            }
            else if (isUtc && dto.Offset != offset)
            {
                throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(dto, offset));
            }

            dto = dto.Truncate();

            var time = isUtc ? dto.UtcDateTime : dto.LocalDateTime;

            var timeDiff = time - prevTime;

            if (allowNonOrdered)
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    writer.Write(false);
                    timeDiff = new TimeSpan(-timeDiff.Ticks);
                }
                else
                {
                    writer.Write(true);
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    if (timeDiff <= TimeSpan.FromHours(32))
                    {
                        writer.Write(true);
                        writer.WriteBits(timeDiff.Hours, 5);
                    }
                    else
                    {
                        writer.Write(false);
                        writer.WriteInt(timeDiff.Hours);
                    }

                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }
            else
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    throw new ArgumentException(LocalizedStrings.Str1009Params.Put(name, prevTime, time), "dto");
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    timeDiff = time.TimeOfDay;

                    writer.WriteBits(timeDiff.Hours, 5);
                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }

            writer.WriteInt(timeDiff.Milliseconds);

            return(time);
        }
Beispiel #13
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <ExecutionMessage> messages, OrderLogMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty() && !messages.IsEmpty())
            {
                var item = messages.First();

                metaInfo.FirstOrderId       = metaInfo.LastOrderId = item.OrderId ?? default;
                metaInfo.FirstTransactionId = metaInfo.LastTransactionId = item.TransactionId;
                metaInfo.ServerOffset       = item.ServerTime.Offset;
                metaInfo.FirstSeqNum        = metaInfo.PrevSeqNum = item.SeqNum;
            }

            writer.WriteInt(messages.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version47;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version48;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version52;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version53;
            var useBalance       = metaInfo.Version >= MarketDataVersions.Version54;
            var buildFrom        = metaInfo.Version >= MarketDataVersions.Version55;
            var seqNum           = metaInfo.Version >= MarketDataVersions.Version56;
            var useLong          = metaInfo.Version >= MarketDataVersions.Version57;
            var largeDecimal     = metaInfo.Version >= MarketDataVersions.Version57;
            var stringId         = metaInfo.Version >= MarketDataVersions.Version58;

            foreach (var message in messages)
            {
                var hasTrade = message.TradeId != null || message.TradePrice != null || !message.TradeStringId.IsEmpty();
                var orderId  = message.OrderId;

                if (orderId is null)
                {
                    if (!stringId)
                    {
                        throw new ArgumentOutOfRangeException(nameof(messages), message.TransactionId, LocalizedStrings.Str925);
                    }
                }

                if (message.ExecutionType != ExecutionTypes.OrderLog)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), message.ExecutionType, LocalizedStrings.Str1695Params.Put(message));
                }

                // sell market orders has zero price (if security do not have min allowed price)
                // execution ticks (like option execution) may be a zero cost
                // ticks for spreads may be a zero cost or less than zero
                //if (item.Price < 0)
                //	throw new ArgumentOutOfRangeException(nameof(messages), item.Price, LocalizedStrings.Str926Params.Put(item.OrderId));

                var volume = message.SafeGetVolume();
                if (volume <= 0 && message.OrderState != OrderStates.Done)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), volume, LocalizedStrings.Str927Params.Put(message.TransactionId));
                }

                long?tradeId = null;

                if (hasTrade)
                {
                    tradeId = message.TradeId;

                    if (tradeId is null || tradeId <= 0)
                    {
                        if (!stringId)
                        {
                            throw new ArgumentOutOfRangeException(nameof(messages), tradeId, LocalizedStrings.Str1012Params.Put(message.TransactionId));
                        }
                    }

                    // execution ticks (like option execution) may be a zero cost
                    // ticks for spreads may be a zero cost or less than zero
                    //if (item.TradePrice <= 0)
                    //	throw new ArgumentOutOfRangeException(nameof(messages), item.TradePrice, LocalizedStrings.Str929Params.Put(item.TradeId, item.OrderId));
                }

                metaInfo.LastOrderId = writer.SerializeId(orderId ?? 0, metaInfo.LastOrderId);

                var orderPrice = message.OrderPrice;

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    writer.WritePriceEx(orderPrice, metaInfo, SecurityId, false, false);
                }
                else
                {
                    var isAligned = (orderPrice % metaInfo.LastPriceStep) == 0;
                    writer.Write(isAligned);

                    if (isAligned)
                    {
                        if (metaInfo.FirstOrderPrice == 0)
                        {
                            metaInfo.FirstOrderPrice = metaInfo.LastOrderPrice = orderPrice;
                        }

                        var prevPrice = metaInfo.LastOrderPrice;
                        writer.WritePrice(orderPrice, ref prevPrice, metaInfo, SecurityId, true);
                        metaInfo.LastOrderPrice = prevPrice;
                    }
                    else
                    {
                        if (metaInfo.FirstFractionalPrice == 0)
                        {
                            metaInfo.FirstFractionalPrice = metaInfo.LastFractionalPrice = orderPrice;
                        }

                        metaInfo.LastFractionalPrice = writer.WriteDecimal(orderPrice, metaInfo.LastFractionalPrice);
                    }
                }

                writer.WriteVolume(volume, metaInfo, largeDecimal);

                writer.Write(message.Side == Sides.Buy);

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(message.ServerTime, metaInfo.LastTime, LocalizedStrings.Str1013, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (hasTrade)
                {
                    writer.Write(true);

                    if (metaInfo.FirstTradeId == 0)
                    {
                        metaInfo.FirstTradeId = metaInfo.LastTradeId = tradeId ?? default;
                    }

                    metaInfo.LastTradeId = writer.SerializeId(tradeId ?? default, metaInfo.LastTradeId);

                    writer.WritePriceEx(message.GetTradePrice(), metaInfo, SecurityId, useLong, largeDecimal);

                    if (metaInfo.Version >= MarketDataVersions.Version54)
                    {
                        writer.WriteInt((int)message.OrderState);
                    }
                }
                else
                {
                    writer.Write(false);

                    if (metaInfo.Version >= MarketDataVersions.Version54)
                    {
                        writer.WriteInt((int)message.OrderState);
                    }
                    else
                    {
                        writer.Write(message.OrderState == OrderStates.Active);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version31)
                {
                    continue;
                }

                writer.WriteNullableInt((int?)message.OrderStatus);

                if (metaInfo.Version < MarketDataVersions.Version33)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version50)
                {
                    writer.WriteInt((int)(message.TimeInForce ?? TimeInForce.PutInQueue));
                }
                else
                {
                    writer.Write(message.TimeInForce != null);

                    if (message.TimeInForce != null)
                    {
                        writer.WriteInt((int)message.TimeInForce.Value);
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version49)
                {
                    writer.Write(message.IsSystem != null);

                    if (message.IsSystem != null)
                    {
                        writer.Write(message.IsSystem.Value);
                    }
                }
                else
                {
                    writer.Write(message.IsSystem ?? true);
                }

                if (metaInfo.Version < MarketDataVersions.Version34)
                {
                    continue;
                }

                metaInfo.LastTransactionId = writer.SerializeId(message.TransactionId, metaInfo.LastTransactionId);

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteLong(0 /*item.Latency.Ticks*/);
                }

                var portfolio   = message.PortfolioName;
                var isEmptyPf   = portfolio == null;
                var isAnonymous = !isEmptyPf && portfolio == Portfolio.AnonymousPortfolio.Name;

                if (isEmptyPf)
                {
                    writer.Write(false);
                }
                else
                {
                    if (isAnonymous)
                    {
                        if (metaInfo.Version < MarketDataVersions.Version54)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.Write(true);                             // is anonymous
                        }
                    }
                    else
                    {
                        writer.Write(true);

                        if (metaInfo.Version > MarketDataVersions.Version54)
                        {
                            writer.Write(false);                             // not anonymous
                        }
                        metaInfo.Portfolios.TryAdd(message.PortfolioName);
                        writer.WriteInt(metaInfo.Portfolios.IndexOf(message.PortfolioName));
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version51)
                {
                    continue;
                }

                writer.WriteNullableInt((int?)message.Currency);

                if (!useBalance)
                {
                    continue;
                }

                if (message.Balance == null)
                {
                    writer.Write(false);
                }
                else
                {
                    writer.Write(true);

                    if (message.Balance.Value == 0)
                    {
                        writer.Write(false);
                    }
                    else
                    {
                        writer.Write(true);
                        writer.WriteDecimal(message.Balance.Value, 0);
                    }
                }

                if (!buildFrom)
                {
                    continue;
                }

                writer.WriteBuildFrom(message.BuildFrom);

                if (!seqNum)
                {
                    continue;
                }

                writer.WriteSeqNum(message, metaInfo);

                if (!stringId)
                {
                    continue;
                }

                writer.Write(orderId is null);
                writer.WriteStringEx(message.OrderStringId);

                writer.Write(tradeId is null);
                writer.WriteStringEx(message.TradeStringId);

                if (message.OrderBuyId != null)
                {
                    writer.Write(true);
                    metaInfo.LastOrderId = writer.SerializeId(message.OrderBuyId.Value, metaInfo.LastOrderId);
                }
                else
                {
                    writer.Write(false);
                }

                if (message.OrderSellId != null)
                {
                    writer.Write(true);
                    metaInfo.LastOrderId = writer.SerializeId(message.OrderSellId.Value, metaInfo.LastOrderId);
                }
                else
                {
                    writer.Write(false);
                }

                writer.WriteNullableBool(message.IsUpTick);
                writer.WriteNullableDecimal(message.Yield);
                writer.WriteNullableInt(message.TradeStatus);
                writer.WriteNullableDecimal(message.OpenInterest);
                writer.WriteNullableInt((int?)message.OriginSide);
            }
        }
Beispiel #14
0
        public static DateTime WriteTime(this BitArrayWriter writer, DateTimeOffset dto, DateTime prevTime, string name, bool allowNonOrdered, bool isUtc, TimeSpan offset)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            if (isUtc && dto.Offset != offset)
            {
                throw new ArgumentException("Время {0} имеет неправильное смещение. Ожидается {1}.".Put(dto, offset));
            }

            dto = dto.Truncate();

            var time = isUtc ? dto.UtcDateTime : dto.LocalDateTime;

            var timeDiff = time - prevTime;

            if (allowNonOrdered)
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    writer.Write(false);
                    timeDiff = new TimeSpan(-timeDiff.Ticks);
                }
                else
                {
                    writer.Write(true);
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    if (timeDiff <= TimeSpan.FromHours(32))
                    {
                        writer.Write(true);
                        writer.WriteBits(timeDiff.Hours, 5);
                    }
                    else
                    {
                        writer.Write(false);
                        writer.WriteInt(timeDiff.Hours);
                    }

                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }
            else
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    throw new ArgumentException(LocalizedStrings.Str1009Params.Put(name, prevTime, time), "dto");
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    timeDiff = time.TimeOfDay;

                    writer.WriteBits(timeDiff.Hours, 5);
                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }

            writer.WriteInt(timeDiff.Milliseconds);

            return(time);
        }
Beispiel #15
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <ExecutionMessage> messages, TickMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var first = messages.First();

                metaInfo.FirstId      = metaInfo.PrevId = first.TradeId ?? 0;
                metaInfo.ServerOffset = first.ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version48;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version54;

            foreach (var msg in messages)
            {
                if (msg.ExecutionType != ExecutionTypes.Tick)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), msg.ExecutionType, LocalizedStrings.Str1695Params.Put(msg.TradeId));
                }

                var tradeId = msg.TradeId ?? 0;

                // сделки для индексов имеют нулевой номер
                if (tradeId < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), tradeId, LocalizedStrings.Str1020);
                }

                // execution ticks (like option execution) may be a zero cost
                // ticks for spreads may be a zero cost or less than zero
                //if (msg.TradePrice < 0)
                //	throw new ArgumentOutOfRangeException(nameof(messages), msg.TradePrice, LocalizedStrings.Str1021Params.Put(msg.TradeId));

                metaInfo.PrevId = writer.SerializeId(tradeId, metaInfo.PrevId);

                // pyhta4og.
                // http://stocksharp.com/forum/yaf_postsm6450_Oshibka-pri-importie-instrumientov-s-Finama.aspx#post6450

                var volume = msg.TradeVolume;

                if (metaInfo.Version < MarketDataVersions.Version53)
                {
                    if (volume == null)
                    {
                        throw new ArgumentException(LocalizedStrings.Str1022Params.Put((object)msg.TradeId ?? msg.TradeStringId), nameof(messages));
                    }

                    if (volume < 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(messages), volume, LocalizedStrings.Str1022Params.Put(msg.TradeId));
                    }

                    writer.WriteVolume(volume.Value, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(volume != null);

                    if (volume != null)
                    {
                        if (volume < 0)
                        {
                            throw new ArgumentOutOfRangeException(nameof(messages), volume, LocalizedStrings.Str1022Params.Put(msg.TradeId));
                        }

                        writer.WriteVolume(volume.Value, metaInfo, SecurityId);
                    }
                }

                writer.WritePriceEx(msg.GetTradePrice(), metaInfo, SecurityId);
                writer.WriteSide(msg.OriginSide);

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(msg.ServerTime, metaInfo.LastTime, LocalizedStrings.Str985, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (metaInfo.Version < MarketDataVersions.Version40)
                {
                    continue;
                }

                if (metaInfo.Version < MarketDataVersions.Version47)
                {
                    writer.WriteLong((msg.LocalTime - msg.ServerTime).Ticks);
                }
                else
                {
                    var hasLocalTime = true;

                    if (metaInfo.Version >= MarketDataVersions.Version49)
                    {
                        hasLocalTime = !msg.LocalTime.IsDefault() && msg.LocalTime != msg.ServerTime;
                        writer.Write(hasLocalTime);
                    }

                    if (hasLocalTime)
                    {
                        lastOffset               = metaInfo.LastLocalOffset;
                        metaInfo.LastLocalTime   = writer.WriteTime(msg.LocalTime, metaInfo.LastLocalTime, LocalizedStrings.Str1024, allowNonOrdered, isUtc, metaInfo.LocalOffset, allowDiffOffsets, ref lastOffset);
                        metaInfo.LastLocalOffset = lastOffset;
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version42)
                {
                    continue;
                }

                if (metaInfo.Version >= MarketDataVersions.Version51)
                {
                    writer.Write(msg.IsSystem != null);

                    if (msg.IsSystem != null)
                    {
                        writer.Write(msg.IsSystem.Value);
                    }
                }
                else
                {
                    writer.Write(msg.IsSystem ?? true);
                }

                if (msg.IsSystem == false)
                {
                    if (metaInfo.Version >= MarketDataVersions.Version51)
                    {
                        writer.WriteNullableInt(msg.TradeStatus);
                    }
                    else
                    {
                        writer.WriteInt(msg.TradeStatus ?? 0);
                    }
                }

                var oi = msg.OpenInterest;

                if (metaInfo.Version < MarketDataVersions.Version46)
                {
                    writer.WriteVolume(oi ?? 0m, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(oi != null);

                    if (oi != null)
                    {
                        writer.WriteVolume(oi.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    continue;
                }

                writer.Write(msg.IsUpTick != null);

                if (msg.IsUpTick != null)
                {
                    writer.Write(msg.IsUpTick.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    continue;
                }

                writer.Write(msg.Currency != null);

                if (msg.Currency != null)
                {
                    writer.WriteInt((int)msg.Currency.Value);
                }
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <ExecutionMessage> messages, TransactionSerializerMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var msg = messages.First();

                metaInfo.FirstOrderId               = metaInfo.LastOrderId = msg.OrderId ?? 0;
                metaInfo.FirstTradeId               = metaInfo.LastTradeId = msg.TradeId ?? 0;
                metaInfo.FirstTransactionId         = metaInfo.LastTransactionId = msg.TransactionId;
                metaInfo.FirstOriginalTransactionId = metaInfo.LastOriginalTransactionId = msg.OriginalTransactionId;
                metaInfo.FirstCommission            = metaInfo.LastCommission = msg.Commission ?? 0;
                metaInfo.FirstPnL             = metaInfo.LastPnL = msg.PnL ?? 0;
                metaInfo.FirstPosition        = metaInfo.LastPosition = msg.Position ?? 0;
                metaInfo.FirstSlippage        = metaInfo.LastSlippage = msg.Slippage ?? 0;
                metaInfo.FirstItemLocalTime   = metaInfo.LastItemLocalTime = msg.LocalTime.UtcDateTime;
                metaInfo.FirstItemLocalOffset = metaInfo.LastItemLocalOffset = msg.LocalTime.Offset;
                metaInfo.ServerOffset         = msg.ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version48;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version51;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version56;
            var isTickPrecision  = metaInfo.Version >= MarketDataVersions.Version60;
            var buildFrom        = metaInfo.Version >= MarketDataVersions.Version67;

            foreach (var msg in messages)
            {
                if (msg.ExecutionType != ExecutionTypes.Transaction)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), msg.ExecutionType, LocalizedStrings.Str1695Params.Put(msg));
                }

                // нулевой номер заявки возможен при сохранении в момент регистрации
                if (msg.OrderId < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), msg.OrderId, LocalizedStrings.Str925);
                }

                // нулевая цена возможна, если идет "рыночная" продажа по инструменту без планок
                if (msg.OrderPrice < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(messages), msg.OrderPrice, LocalizedStrings.Str926Params.Put(msg.OrderId == null ? msg.OrderStringId : msg.OrderId.To <string>()));
                }

                //var volume = msg.Volume;

                //if (volume < 0)
                //	throw new ArgumentOutOfRangeException(nameof(messages), volume, LocalizedStrings.Str927Params.Put(msg.OrderId == null ? msg.OrderStringId : msg.OrderId.To<string>()));

                if (msg.HasTradeInfo())
                {
                    //if ((msg.TradeId == null && msg.TradeStringId.IsEmpty()) || msg.TradeId <= 0)
                    //	throw new ArgumentOutOfRangeException(nameof(messages), msg.TradeId, LocalizedStrings.Str928Params.Put(msg.TransactionId));

                    if (msg.TradePrice == null || msg.TradePrice <= 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(messages), msg.TradePrice, LocalizedStrings.Str929Params.Put(msg.TradeId, msg.OrderId));
                    }
                }

                metaInfo.LastTransactionId         = writer.SerializeId(msg.TransactionId, metaInfo.LastTransactionId);
                metaInfo.LastOriginalTransactionId = writer.SerializeId(msg.OriginalTransactionId, metaInfo.LastOriginalTransactionId);

                writer.Write(msg.HasOrderInfo);
                writer.Write(msg.HasTradeInfo);

                writer.Write(msg.OrderId != null);

                if (msg.OrderId != null)
                {
                    metaInfo.LastOrderId = writer.SerializeId(msg.OrderId.Value, metaInfo.LastOrderId);
                }
                else
                {
                    writer.WriteStringEx(msg.OrderStringId);
                }

                writer.WriteStringEx(msg.OrderBoardId);

                writer.Write(msg.TradeId != null);

                if (msg.TradeId != null)
                {
                    metaInfo.LastTradeId = writer.SerializeId(msg.TradeId.Value, metaInfo.LastTradeId);
                }
                else
                {
                    writer.WriteStringEx(msg.TradeStringId);
                }

                if (msg.OrderPrice != 0)
                {
                    writer.Write(true);
                    writer.WritePriceEx(msg.OrderPrice, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(false);
                }

                if (msg.TradePrice != null)
                {
                    writer.Write(true);
                    writer.WritePriceEx(msg.TradePrice.Value, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(false);
                }

                writer.Write(msg.Side == Sides.Buy);

                writer.Write(msg.OrderVolume != null);

                if (msg.OrderVolume != null)
                {
                    writer.WriteVolume(msg.OrderVolume.Value, metaInfo, SecurityId);
                }

                writer.Write(msg.TradeVolume != null);

                if (msg.TradeVolume != null)
                {
                    writer.WriteVolume(msg.TradeVolume.Value, metaInfo, SecurityId);
                }

                writer.Write(msg.VisibleVolume != null);

                if (msg.VisibleVolume != null)
                {
                    writer.WriteVolume(msg.VisibleVolume.Value, metaInfo, SecurityId);
                }

                writer.Write(msg.Balance != null);

                if (msg.Balance != null)
                {
                    writer.WriteVolume(msg.Balance.Value, metaInfo, SecurityId);
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(msg.ServerTime, metaInfo.LastTime, LocalizedStrings.Str930, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                writer.WriteNullableInt((int?)msg.OrderType);
                writer.WriteNullableInt((int?)msg.OrderState);
                writer.WriteNullableLong(msg.OrderStatus);
                writer.WriteNullableInt(msg.TradeStatus);
                writer.WriteNullableInt((int?)msg.TimeInForce);

                writer.Write(msg.IsSystem != null);

                if (msg.IsSystem != null)
                {
                    writer.Write(msg.IsSystem.Value);
                }

                writer.Write(msg.IsUpTick != null);

                if (msg.IsUpTick != null)
                {
                    writer.Write(msg.IsUpTick.Value);
                }

                writer.WriteDto(msg.ExpiryDate);

                metaInfo.LastCommission = Write(writer, msg.Commission, metaInfo.LastCommission);
                metaInfo.LastPnL        = Write(writer, msg.PnL, metaInfo.LastPnL);
                metaInfo.LastPosition   = Write(writer, msg.Position, metaInfo.LastPosition);
                metaInfo.LastSlippage   = Write(writer, msg.Slippage, metaInfo.LastSlippage);

                WriteString(writer, metaInfo.Portfolios, msg.PortfolioName);
                WriteString(writer, metaInfo.ClientCodes, msg.ClientCode);
                WriteString(writer, metaInfo.BrokerCodes, msg.BrokerCode);
                WriteString(writer, metaInfo.DepoNames, msg.DepoName);
                WriteString(writer, metaInfo.UserOrderIds, msg.UserOrderId);
                WriteString(writer, metaInfo.Comments, msg.Comment);
                WriteString(writer, metaInfo.SystemComments, msg.SystemComment);
                WriteString(writer, metaInfo.Errors, msg.Error?.Message);

                writer.WriteNullableInt((int?)msg.Currency);

                writer.Write(msg.Latency != null);

                if (msg.Latency != null)
                {
                    writer.WriteLong(msg.Latency.Value.Ticks);
                }

                writer.Write(msg.OriginSide != null);

                if (msg.OriginSide != null)
                {
                    writer.Write(msg.OriginSide.Value == Sides.Buy);
                }

                if (metaInfo.Version < MarketDataVersions.Version59)
                {
                    continue;
                }

                WriteItemLocalTime(writer, metaInfo, msg, isTickPrecision);

                if (metaInfo.Version < MarketDataVersions.Version61)
                {
                    continue;
                }

                writer.Write(msg.IsMarketMaker != null);

                if (msg.IsMarketMaker != null)
                {
                    writer.Write(msg.IsMarketMaker.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version62)
                {
                    continue;
                }

                writer.Write(msg.IsMargin != null);

                if (msg.IsMargin != null)
                {
                    writer.Write(msg.IsMargin.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version63)
                {
                    continue;
                }

                writer.WriteStringEx(msg.CommissionCurrency);

                if (metaInfo.Version < MarketDataVersions.Version64)
                {
                    continue;
                }

                writer.Write(msg.IsManual != null);

                if (msg.IsManual != null)
                {
                    writer.Write(msg.IsManual.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version65)
                {
                    continue;
                }

                writer.Write(msg.PositionEffect != null);

                if (msg.PositionEffect != null)
                {
                    writer.WriteInt((int)msg.PositionEffect.Value);
                }

                writer.Write(msg.PostOnly != null);

                if (msg.PostOnly != null)
                {
                    writer.Write(msg.PostOnly.Value);
                }

                writer.Write(msg.Initiator != null);

                if (msg.Initiator != null)
                {
                    writer.Write(msg.Initiator.Value);
                }

                if (metaInfo.Version < MarketDataVersions.Version66)
                {
                    continue;
                }

                writer.WriteLong(msg.SeqNum);
                WriteString(writer, metaInfo.StrategyIds, msg.StrategyId);

                if (!buildFrom)
                {
                    continue;
                }

                writer.WriteBuildFrom(msg.BuildFrom);
            }
        }
Beispiel #17
0
        public static DateTime WriteTime(this BitArrayWriter writer, DateTimeOffset dto, DateTime prevTime, string name, bool allowNonOrdered, bool isUtc, TimeSpan offset, bool allowDiffOffsets, bool isTickPrecision, ref TimeSpan prevOffset, bool bigRange = false)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            if (allowDiffOffsets)
            {
                writer.Write(dto.Offset == prevOffset);

                if (prevOffset != dto.Offset)
                {
                    prevOffset = dto.Offset;

                    writer.WriteInt(prevOffset.Hours);

                    writer.Write(prevOffset.Minutes == 0);

                    if (prevOffset.Minutes != 0)
                    {
                        writer.WriteInt(prevOffset.Minutes);
                    }
                }
            }
            else if (isUtc && dto.Offset != offset)
            {
                throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(dto, offset));
            }

            if (!isTickPrecision)
            {
                dto = dto.StorageBinaryOldTruncate();
            }

            var time = isUtc ? dto.UtcDateTime : dto.LocalDateTime;

            var timeDiff = time - prevTime;

            if (allowNonOrdered)
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    writer.Write(false);
                    timeDiff = new TimeSpan(-timeDiff.Ticks);
                }
                else
                {
                    writer.Write(true);
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    if (timeDiff <= TimeSpan.FromHours(32))
                    {
                        writer.Write(true);
                        writer.WriteBits(timeDiff.Days * 24 + timeDiff.Hours, 5);
                    }
                    else
                    {
                        writer.Write(false);
                        writer.WriteInt(timeDiff.Hours);

                        if (timeDiff.Days > 0)
                        {
                            if (!bigRange)
                            {
                                throw new ArgumentOutOfRangeException(nameof(dto), LocalizedStrings.BigRangeError.Put(prevTime, dto));
                            }

                            writer.Write(true);
                            writer.WriteInt(timeDiff.Days);
                        }
                        else
                        {
                            writer.Write(false);
                        }
                    }

                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }
            else
            {
                if (timeDiff < TimeSpan.Zero)
                {
                    throw new ArgumentException(LocalizedStrings.Str1009Params.Put(name, prevTime, time), nameof(dto));
                }

                if (timeDiff >= TimeSpan.FromMinutes(1))
                {
                    writer.Write(true);

                    timeDiff = time.TimeOfDay;

                    writer.WriteBits(timeDiff.Hours, 5);
                    writer.WriteBits(timeDiff.Minutes, 6);
                    writer.WriteBits(timeDiff.Seconds, 6);
                }
                else
                {
                    writer.Write(false);

                    writer.WriteInt(timeDiff.Seconds);
                }
            }

            writer.WriteInt(timeDiff.Milliseconds);

            if (isTickPrecision)
            {
                writer.WriteInt((int)(timeDiff.Ticks % 10000));
            }

            return(time);
        }
Beispiel #18
0
        protected override void OnSave(BitArrayWriter writer, IEnumerable <TCandleMessage> candles, CandleMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstCandle = candles.First();

                metaInfo.FirstPrice   = firstCandle.LowPrice;
                metaInfo.LastPrice    = firstCandle.LowPrice;
                metaInfo.ServerOffset = firstCandle.OpenTime.Offset;
            }

            writer.WriteInt(candles.Count());

            var allowNonOrdered = metaInfo.Version >= MarketDataVersions.Version49;
            var isUtc           = metaInfo.Version >= MarketDataVersions.Version50;

            foreach (var candle in candles)
            {
                writer.WriteVolume(candle.TotalVolume, metaInfo, SecurityId);

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    writer.WriteVolume(candle.RelativeVolume ?? 0, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(candle.RelativeVolume != null);

                    if (candle.RelativeVolume != null)
                    {
                        writer.WriteVolume(candle.RelativeVolume.Value, metaInfo, SecurityId);
                    }
                }

                writer.WritePrice(candle.LowPrice, metaInfo.LastPrice, metaInfo, SecurityId);
                metaInfo.LastPrice = candle.LowPrice;

                writer.WritePrice(candle.OpenPrice, metaInfo.LastPrice, metaInfo, SecurityId);
                writer.WritePrice(candle.ClosePrice, metaInfo.LastPrice, metaInfo, SecurityId);
                writer.WritePrice(candle.HighPrice, metaInfo.LastPrice, metaInfo, SecurityId);

                metaInfo.LastTime = writer.WriteTime(candle.OpenTime, metaInfo.LastTime, LocalizedStrings.Str998, allowNonOrdered, isUtc, metaInfo.ServerOffset);

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    var isAll = !candle.HighTime.IsDefault() && !candle.LowTime.IsDefault();

                    DateTimeOffset first;
                    DateTimeOffset second;

                    writer.Write(isAll);

                    if (isAll)
                    {
                        var isOrdered = candle.HighTime <= candle.LowTime;
                        writer.Write(isOrdered);

                        first  = isOrdered ? candle.HighTime : candle.LowTime;
                        second = isOrdered ? candle.LowTime : candle.HighTime;
                    }
                    else
                    {
                        writer.Write(!candle.HighTime.IsDefault());
                        writer.Write(!candle.LowTime.IsDefault());

                        if (candle.HighTime.IsDefault())
                        {
                            first  = candle.LowTime;
                            second = default(DateTimeOffset);
                        }
                        else
                        {
                            first  = candle.HighTime;
                            second = default(DateTimeOffset);
                        }
                    }

                    if (!first.IsDefault())
                    {
                        metaInfo.LastTime = writer.WriteTime(first, metaInfo.LastTime, LocalizedStrings.Str999, allowNonOrdered, isUtc, metaInfo.ServerOffset);
                    }

                    if (!second.IsDefault())
                    {
                        metaInfo.LastTime = writer.WriteTime(second, metaInfo.LastTime, LocalizedStrings.Str1000, allowNonOrdered, isUtc, metaInfo.ServerOffset);
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version47)
                {
                    writer.Write(!candle.CloseTime.IsDefault());

                    if (!candle.CloseTime.IsDefault())
                    {
                        metaInfo.LastTime = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset);
                    }
                }
                else
                {
                    var time = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset);

                    if (metaInfo.Version >= MarketDataVersions.Version41)
                    {
                        metaInfo.LastTime = time;
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    if (metaInfo.Version < MarketDataVersions.Version51)
                    {
                        writer.WriteVolume(candle.OpenVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.HighVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.LowVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.CloseVolume ?? 0m, metaInfo, SecurityId);
                    }
                    else
                    {
                        if (candle.OpenVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.OpenVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.HighVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.HighVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.LowVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.LowVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.CloseVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.CloseVolume.Value, metaInfo, SecurityId);
                        }
                    }
                }

                writer.WriteInt((int)candle.State);

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    continue;
                }

                var oi = candle.OpenInterest;

                if (metaInfo.Version < MarketDataVersions.Version48)
                {
                    writer.WriteVolume(oi ?? 0m, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(oi != null);

                    if (oi != null)
                    {
                        writer.WriteVolume(oi.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    continue;
                }

                writer.Write(candle.DownTicks != null);

                if (candle.DownTicks != null)
                {
                    writer.WriteInt(candle.DownTicks.Value);
                }

                writer.Write(candle.UpTicks != null);

                if (candle.UpTicks != null)
                {
                    writer.WriteInt(candle.UpTicks.Value);
                }

                writer.Write(candle.TotalTicks != null);

                if (candle.TotalTicks != null)
                {
                    writer.WriteInt(candle.TotalTicks.Value);
                }
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <ExecutionMessage> messages, TransactionSerializerMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var msg = messages.First();

                metaInfo.FirstOrderId               = metaInfo.LastOrderId = msg.OrderId ?? 0;
                metaInfo.FirstTradeId               = metaInfo.LastTradeId = msg.TradeId ?? 0;
                metaInfo.FirstTransactionId         = metaInfo.LastTransactionId = msg.TransactionId;
                metaInfo.FirstOriginalTransactionId = metaInfo.LastOriginalTransactionId = msg.OriginalTransactionId;
                metaInfo.FirstCommission            = metaInfo.LastCommission = msg.Commission ?? 0;
                metaInfo.ServerOffset               = msg.ServerTime.Offset;
            }

            writer.WriteInt(messages.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version48;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version51;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version56;

            foreach (var msg in messages)
            {
                var isTrade = msg.ExecutionType == ExecutionTypes.Trade;

                if (msg.ExecutionType != ExecutionTypes.Order && msg.ExecutionType != ExecutionTypes.Trade)
                {
                    throw new ArgumentOutOfRangeException("messages", msg.ExecutionType, LocalizedStrings.Str1695Params.Put(msg.OrderId ?? msg.TradeId));
                }

                // нулевой номер заявки возможен при сохранении в момент регистрации
                if (msg.OrderId < 0)
                {
                    throw new ArgumentOutOfRangeException("messages", msg.OrderId, LocalizedStrings.Str925);
                }

                // нулевая цена возможна, если идет "рыночная" продажа по инструменту без планок
                if (msg.OrderPrice < 0)
                {
                    throw new ArgumentOutOfRangeException("messages", msg.OrderPrice, LocalizedStrings.Str926Params.Put(msg.OrderId == null ? msg.OrderStringId : msg.OrderId.To <string>()));
                }

                var volume = msg.SafeGetVolume();

                if (volume < 0)
                {
                    throw new ArgumentOutOfRangeException("messages", volume, LocalizedStrings.Str927Params.Put(msg.OrderId == null ? msg.OrderStringId : msg.OrderId.To <string>()));
                }

                if (isTrade)
                {
                    if ((msg.TradeId == null && msg.TradeStringId.IsEmpty()) || msg.TradeId <= 0)
                    {
                        throw new ArgumentOutOfRangeException("messages", msg.TradeId, LocalizedStrings.Str928Params.Put(msg.TransactionId));
                    }

                    if (msg.TradePrice == null || msg.TradePrice <= 0)
                    {
                        throw new ArgumentOutOfRangeException("messages", msg.TradePrice, LocalizedStrings.Str929Params.Put(msg.TradeId, msg.OrderId));
                    }
                }

                writer.WriteInt((int)msg.ExecutionType);

                metaInfo.LastTransactionId         = writer.SerializeId(msg.TransactionId, metaInfo.LastTransactionId);
                metaInfo.LastOriginalTransactionId = writer.SerializeId(msg.OriginalTransactionId, metaInfo.LastOriginalTransactionId);

                if (!isTrade)
                {
                    if (metaInfo.Version < MarketDataVersions.Version50)
                    {
                        metaInfo.LastOrderId = writer.SerializeId(msg.OrderId ?? 0, metaInfo.LastOrderId);
                    }
                    else
                    {
                        writer.Write(msg.OrderId != null);

                        if (msg.OrderId != null)
                        {
                            metaInfo.LastOrderId = writer.SerializeId(msg.OrderId.Value, metaInfo.LastOrderId);
                        }
                        else
                        {
                            writer.Write(!msg.OrderStringId.IsEmpty());

                            if (!msg.OrderStringId.IsEmpty())
                            {
                                writer.WriteString(msg.OrderStringId);
                            }
                        }

                        writer.Write(!msg.OrderBoardId.IsEmpty());

                        if (!msg.OrderBoardId.IsEmpty())
                        {
                            writer.WriteString(msg.OrderBoardId);
                        }
                    }
                }
                else
                {
                    if (metaInfo.Version < MarketDataVersions.Version50)
                    {
                        metaInfo.LastTradeId = writer.SerializeId(msg.TradeId ?? 0, metaInfo.LastTradeId);
                    }
                    else
                    {
                        writer.Write(msg.TradeId != null);

                        if (msg.TradeId != null)
                        {
                            metaInfo.LastTradeId = writer.SerializeId(msg.TradeId.Value, metaInfo.LastTradeId);
                        }
                        else
                        {
                            writer.Write(!msg.TradeStringId.IsEmpty());

                            if (!msg.TradeStringId.IsEmpty())
                            {
                                writer.WriteString(msg.TradeStringId);
                            }
                        }
                    }
                }

                writer.Write(msg.Side == Sides.Buy);
                writer.WritePriceEx(!isTrade ? msg.OrderPrice : msg.GetTradePrice(), metaInfo, SecurityId);

                writer.WriteVolume(volume, metaInfo, SecurityId);

                if (metaInfo.Version < MarketDataVersions.Version54)
                {
                    writer.WriteVolume(msg.VisibleVolume ?? 0, metaInfo, SecurityId);
                    writer.WriteVolume(msg.Balance ?? 0, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(msg.VisibleVolume != null);

                    if (msg.VisibleVolume != null)
                    {
                        writer.WriteVolume(msg.VisibleVolume.Value, metaInfo, SecurityId);
                    }

                    writer.Write(msg.Balance != null);

                    if (msg.Balance != null)
                    {
                        writer.WriteVolume(msg.Balance.Value, metaInfo, SecurityId);
                    }
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(msg.ServerTime, metaInfo.LastTime, LocalizedStrings.Str930, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                writer.WriteInt((int)msg.OrderType);

                writer.WriteNullableInt(msg.OrderState);
                writer.WriteNullableInt(msg.OrderStatus);

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    writer.WriteInt(msg.TradeStatus ?? 0);
                }
                else
                {
                    writer.WriteNullableInt(msg.TradeStatus);
                }

                if (metaInfo.Version < MarketDataVersions.Version53)
                {
                    writer.WriteInt((int)(msg.TimeInForce ?? TimeInForce.PutInQueue));
                }
                else
                {
                    writer.Write(msg.TimeInForce != null);

                    if (msg.TimeInForce != null)
                    {
                        writer.WriteInt((int)msg.TimeInForce.Value);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    writer.Write(msg.IsSystem ?? true);
                }
                else
                {
                    writer.Write(msg.IsSystem != null);

                    if (msg.IsSystem != null)
                    {
                        writer.Write(msg.IsSystem.Value);
                    }
                }

                writer.WriteLong(msg.ExpiryDate != null ? msg.ExpiryDate.Value.Ticks : 0L);

                WriteCommission(writer, metaInfo, msg.Commission);

                WriteString(writer, metaInfo.Portfolios, msg.PortfolioName);
                WriteString(writer, metaInfo.StrategyIds, msg.UserOrderId);
                WriteString(writer, metaInfo.Comments, msg.Comment);
                WriteString(writer, metaInfo.Errors, msg.Error != null ? msg.Error.Message : null);

                if (metaInfo.Version < MarketDataVersions.Version55)
                {
                    continue;
                }

                writer.Write(msg.Currency != null);

                if (msg.Currency != null)
                {
                    writer.WriteInt((int)msg.Currency.Value);
                }
            }
        }
        protected override void OnSave(BitArrayWriter writer, IEnumerable <TCandleMessage> candles, CandleMetaInfo metaInfo)
        {
            if (metaInfo.IsEmpty())
            {
                var firstCandle = candles.First();

                var low = firstCandle.LowPrice;

                if ((low % metaInfo.PriceStep) == 0)
                {
                    metaInfo.FirstPrice = metaInfo.LastPrice = low;
                }
                else
                {
                    metaInfo.FirstFractionalPrice = metaInfo.LastFractionalPrice = low;
                }

                metaInfo.ServerOffset = firstCandle.OpenTime.Offset;
            }

            writer.WriteInt(candles.Count());

            var allowNonOrdered  = metaInfo.Version >= MarketDataVersions.Version49;
            var isUtc            = metaInfo.Version >= MarketDataVersions.Version50;
            var allowDiffOffsets = metaInfo.Version >= MarketDataVersions.Version53;
            var useLevels        = metaInfo.Version >= MarketDataVersions.Version54;
            var bigRange         = metaInfo.Version >= MarketDataVersions.Version57;
            var isTickPrecision  = bigRange;
            var useLong          = metaInfo.Version >= MarketDataVersions.Version58;
            var buildFrom        = metaInfo.Version >= MarketDataVersions.Version59;

            foreach (var candle in candles)
            {
                if (candle.State == CandleStates.Active)
                {
                    throw new ArgumentException(LocalizedStrings.CandleActiveNotSupport.Put(candle), nameof(candle));
                }

                writer.WriteVolume(candle.TotalVolume, metaInfo, SecurityId);

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    writer.WriteVolume(candle.RelativeVolume ?? 0, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(candle.RelativeVolume != null);

                    if (candle.RelativeVolume != null)
                    {
                        writer.WriteVolume(candle.RelativeVolume.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version56)
                {
                    var prevPrice = metaInfo.LastPrice;
                    writer.WritePrice(candle.LowPrice, ref prevPrice, metaInfo, SecurityId);
                    metaInfo.LastPrice = prevPrice;

                    prevPrice = metaInfo.LastPrice;
                    writer.WritePrice(candle.OpenPrice, ref prevPrice, metaInfo, SecurityId);

                    prevPrice = metaInfo.LastPrice;
                    writer.WritePrice(candle.ClosePrice, ref prevPrice, metaInfo, SecurityId);

                    prevPrice = metaInfo.LastPrice;
                    writer.WritePrice(candle.HighPrice, ref prevPrice, metaInfo, SecurityId);
                }
                else
                {
                    writer.WritePriceEx(candle.LowPrice, metaInfo, SecurityId, useLong);

                    if (candle.OpenPrice <= candle.ClosePrice)
                    {
                        writer.Write(true);

                        writer.WritePriceEx(candle.OpenPrice, metaInfo, SecurityId, useLong);
                        writer.WritePriceEx(candle.ClosePrice, metaInfo, SecurityId, useLong);
                    }
                    else
                    {
                        writer.Write(false);

                        writer.WritePriceEx(candle.ClosePrice, metaInfo, SecurityId, useLong);
                        writer.WritePriceEx(candle.OpenPrice, metaInfo, SecurityId, useLong);
                    }

                    writer.WritePriceEx(candle.HighPrice, metaInfo, SecurityId, useLong);
                }

                if (!candle.CloseTime.IsDefault() && candle.OpenTime > candle.CloseTime)
                {
                    throw new ArgumentException(LocalizedStrings.MoreThanCloseTime.Put(candle.OpenTime, candle.CloseTime));
                }

                var lastOffset = metaInfo.LastServerOffset;
                metaInfo.LastTime         = writer.WriteTime(candle.OpenTime, metaInfo.LastTime, LocalizedStrings.Str998, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset);
                metaInfo.LastServerOffset = lastOffset;

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    var isAll = !candle.HighTime.IsDefault() && !candle.LowTime.IsDefault();

                    DateTimeOffset first;
                    DateTimeOffset second;

                    writer.Write(isAll);

                    if (isAll)
                    {
                        var isOrdered = candle.HighTime <= candle.LowTime;
                        writer.Write(isOrdered);

                        first  = isOrdered ? candle.HighTime : candle.LowTime;
                        second = isOrdered ? candle.LowTime : candle.HighTime;
                    }
                    else
                    {
                        writer.Write(!candle.HighTime.IsDefault());
                        writer.Write(!candle.LowTime.IsDefault());

                        first  = candle.HighTime.IsDefault() ? candle.LowTime : candle.HighTime;
                        second = default;
                    }

                    if (!first.IsDefault())
                    {
                        if (first.Offset != lastOffset && !allowDiffOffsets)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(first, lastOffset));
                        }

                        if (!candle.CloseTime.IsDefault() && first > candle.CloseTime)
                        {
                            throw new ArgumentException(LocalizedStrings.MoreThanCloseTime.Put(first, candle.CloseTime));
                        }

                        metaInfo.LastTime = writer.WriteTime(first, metaInfo.LastTime, LocalizedStrings.Str999, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset, bigRange);
                    }

                    if (!second.IsDefault())
                    {
                        if (second.Offset != lastOffset && !allowDiffOffsets)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(second, lastOffset));
                        }

                        if (!candle.CloseTime.IsDefault() && second > candle.CloseTime)
                        {
                            throw new ArgumentException(LocalizedStrings.MoreThanCloseTime.Put(second, candle.CloseTime));
                        }

                        metaInfo.LastTime = writer.WriteTime(second, metaInfo.LastTime, LocalizedStrings.Str1000, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset, bigRange);
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version47)
                {
                    writer.Write(!candle.CloseTime.IsDefault());

                    if (!candle.CloseTime.IsDefault())
                    {
                        if (candle.CloseTime.Offset != lastOffset && !allowDiffOffsets)
                        {
                            throw new ArgumentException(LocalizedStrings.WrongTimeOffset.Put(candle.CloseTime, lastOffset));
                        }

                        metaInfo.LastTime = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, isTickPrecision, ref lastOffset, bigRange);
                    }
                }
                else
                {
                    var time = writer.WriteTime(candle.CloseTime, metaInfo.LastTime, LocalizedStrings.Str1001, allowNonOrdered, isUtc, metaInfo.ServerOffset, allowDiffOffsets, false, ref lastOffset);

                    if (metaInfo.Version >= MarketDataVersions.Version41)
                    {
                        metaInfo.LastTime = time;
                    }
                }

                if (metaInfo.Version >= MarketDataVersions.Version46)
                {
                    if (metaInfo.Version < MarketDataVersions.Version51)
                    {
                        writer.WriteVolume(candle.OpenVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.HighVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.LowVolume ?? 0m, metaInfo, SecurityId);
                        writer.WriteVolume(candle.CloseVolume ?? 0m, metaInfo, SecurityId);
                    }
                    else
                    {
                        if (candle.OpenVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.OpenVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.HighVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.HighVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.LowVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.LowVolume.Value, metaInfo, SecurityId);
                        }

                        if (candle.CloseVolume == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);
                            writer.WriteVolume(candle.CloseVolume.Value, metaInfo, SecurityId);
                        }
                    }
                }

                writer.WriteInt((int)candle.State);

                if (metaInfo.Version < MarketDataVersions.Version45)
                {
                    continue;
                }

                var oi = candle.OpenInterest;

                if (metaInfo.Version < MarketDataVersions.Version48)
                {
                    writer.WriteVolume(oi ?? 0m, metaInfo, SecurityId);
                }
                else
                {
                    writer.Write(oi != null);

                    if (oi != null)
                    {
                        writer.WriteVolume(oi.Value, metaInfo, SecurityId);
                    }
                }

                if (metaInfo.Version < MarketDataVersions.Version52)
                {
                    continue;
                }

                writer.Write(candle.DownTicks != null);

                if (candle.DownTicks != null)
                {
                    writer.WriteInt(candle.DownTicks.Value);
                }

                writer.Write(candle.UpTicks != null);

                if (candle.UpTicks != null)
                {
                    writer.WriteInt(candle.UpTicks.Value);
                }

                writer.Write(candle.TotalTicks != null);

                if (candle.TotalTicks != null)
                {
                    writer.WriteInt(candle.TotalTicks.Value);
                }

                if (!useLevels)
                {
                    continue;
                }

                var priceLevels = candle.PriceLevels;

                writer.Write(priceLevels != null);

                if (priceLevels != null)
                {
                    priceLevels = priceLevels.ToArray();

                    writer.WriteInt(priceLevels.Count());

                    foreach (var level in priceLevels)
                    {
                        if (metaInfo.Version < MarketDataVersions.Version56)
                        {
                            var prevPrice = metaInfo.LastPrice;
                            writer.WritePrice(level.Price, ref prevPrice, metaInfo, SecurityId);
                            metaInfo.LastPrice = prevPrice;
                        }
                        else
                        {
                            writer.WritePriceEx(level.Price, metaInfo, SecurityId);
                        }

                        writer.WriteInt(level.BuyCount);
                        writer.WriteInt(level.SellCount);

                        writer.WriteVolume(level.BuyVolume, metaInfo, SecurityId);
                        writer.WriteVolume(level.SellVolume, metaInfo, SecurityId);

                        if (metaInfo.Version >= MarketDataVersions.Version55)
                        {
                            writer.WriteVolume(level.TotalVolume, metaInfo, SecurityId);
                        }

                        var volumes = level.BuyVolumes;

                        if (volumes == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);

                            volumes = volumes.ToArray();

                            writer.WriteInt(volumes.Count());

                            foreach (var volume in volumes)
                            {
                                writer.WriteVolume(volume, metaInfo, SecurityId);
                            }
                        }

                        volumes = level.SellVolumes;

                        if (volumes == null)
                        {
                            writer.Write(false);
                        }
                        else
                        {
                            writer.Write(true);

                            volumes = volumes.ToArray();

                            writer.WriteInt(volumes.Count());

                            foreach (var volume in volumes)
                            {
                                writer.WriteVolume(volume, metaInfo, SecurityId);
                            }
                        }
                    }
                }

                if (!buildFrom)
                {
                    continue;
                }

                writer.WriteBuildFrom(candle.BuildFrom);
            }
        }