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;
            var leverage         = metaInfo.Version >= MarketDataVersions.Version68;

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

                if (!leverage)
                {
                    continue;
                }

                writer.WriteNullableInt(msg.Leverage);
            }
        }
        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());

            var buildFrom = metaInfo.Version >= MarketDataVersions.Version35;
            var side      = metaInfo.Version >= MarketDataVersions.Version36;

            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;

                    case PositionChangeTypes.ExpirationDate:
                        writer.WriteDto((DateTimeOffset)change.Value);
                        break;

                    case PositionChangeTypes.CommissionMaker:
                    case PositionChangeTypes.CommissionTaker:
                    case PositionChangeTypes.BuyOrdersMargin:
                    case PositionChangeTypes.SellOrdersMargin:
                    case PositionChangeTypes.OrdersMargin:
                        writer.WriteDecimal((decimal)change.Value, 0);
                        break;

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

                    case PositionChangeTypes.BuyOrdersCount:
                    case PositionChangeTypes.SellOrdersCount:
                    case PositionChangeTypes.OrdersCount:
                    case PositionChangeTypes.TradesCount:
                        writer.WriteInt((int)change.Value);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }

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

                writer.WriteStringEx(message.Description);
                writer.WriteStringEx(message.StrategyId);

                if (!buildFrom)
                {
                    continue;
                }

                writer.WriteBuildFrom(message.BuildFrom);

                if (!side)
                {
                    continue;
                }

                writer.WriteNullableSide(message.Side);
            }
        }