/// <summary>
		/// To initialize the generator state.
		/// </summary>
		public override void Init()
		{
			base.Init();

			_lastTradePrice = null;
			_prevTradePrice = null;
			_bestAskPrice = null;
			_bestBidPrice = null;

			_newTrades = false;
			_currGenerations = MaxGenerations;

			_boardDefinition = null;
		}
		/// <summary>
		/// Process message.
		/// </summary>
		/// <param name="message">Message.</param>
		/// <returns>The result of processing. If <see langword="null" /> is returned, then generator has no sufficient data to generate new message.</returns>
		protected override Message OnProcess(Message message)
		{
			if (_boardDefinition == null)
			{
				if (message.Type == MessageTypes.Board)
					_boardDefinition = (BoardMessage)message.Clone();
				
				return null;
			}

			DateTimeOffset time;

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

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

					if (value != null)
						_lastTradePrice = (decimal)value;

					value = l1Msg.Changes.TryGetValue(Level1Fields.BestBidPrice);

					if (value != null)
						_bestBidPrice = (decimal)value;

					value = l1Msg.Changes.TryGetValue(Level1Fields.BestAskPrice);

					if (value != null)
						_bestAskPrice = (decimal)value;

					time = l1Msg.ServerTime;

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

					switch (execMsg.ExecutionType)
					{
						case ExecutionTypes.Tick:
						{
							var tradePrice = execMsg.TradePrice;

							if (null == _prevTradePrice)
							{
								_prevTradePrice = tradePrice;
								_bestAskPrice = tradePrice;
								_bestBidPrice = tradePrice;
							}

							switch (execMsg.OriginSide)
							{
								case null:
								{
									if (tradePrice > _prevTradePrice)
									{
										_bestAskPrice = tradePrice;
										//BestBid = PrevTrade;
										_prevTradePrice = tradePrice;
									}
									else if (tradePrice < _prevTradePrice)
									{
										_bestBidPrice = tradePrice;
										//BestAsk = PrevTrade;
										_prevTradePrice = tradePrice;
									}

									break;
								}
								case Sides.Buy:
									_bestAskPrice = tradePrice;
									break;
								default:
									_bestBidPrice = tradePrice;
									break;
							}

							_lastTradePrice = tradePrice;
							_newTrades = true;

							break;
						}
						default:
							return null;
					}

					time = execMsg.ServerTime;

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

					time = timeMsg.ServerTime;

					break;
				}
				default:
					return null;
			}

			if (_currGenerations == 0 || _bestBidPrice == null || _bestAskPrice == null)
				return null;

			var isTradeTime = _boardDefinition.IsTradeTime(time);

			var canProcess = GenerateDepthOnEachTrade && _newTrades
				? isTradeTime
				: (IsTimeToGenerate(time) && isTradeTime);

			if (!canProcess)
				return null;

			_currGenerations = MaxGenerations;

			var depth = new QuoteChangeMessage
			{
				SecurityId = SecurityId,
				ServerTime = time,
				LocalTime = time.LocalDateTime,
			};

			if (_bestBidPrice == null || _bestAskPrice == null)
			{
				if (_lastTradePrice == null)
					throw new InvalidOperationException(LocalizedStrings.Str1142);

				_bestBidPrice = _bestAskPrice = _lastTradePrice;
			}

			if (_currGenerations == 0)
				throw new InvalidOperationException(LocalizedStrings.Str1143);

			var bidPrice = _bestBidPrice;
			var askPrice = _bestAskPrice;

			var minSpred = MinSpreadStepCount * SecurityDefinition.PriceStep;
			var maxStread = MaxSpreadStepCount * SecurityDefinition.PriceStep;

			if ((askPrice - bidPrice) < minSpred)
			{
				if (_bestBidPrice == _lastTradePrice) // up trend
					askPrice = bidPrice + minSpred;
				else
					bidPrice = askPrice - minSpred;
			}
			else if ((askPrice - bidPrice) > maxStread)
			{
				if (_bestBidPrice == _lastTradePrice) // down trend
					askPrice = bidPrice + maxStread;
				else
					bidPrice = askPrice - maxStread;
			}

			var bids = new List<QuoteChange>
			{
				new QuoteChange(Sides.Buy, bidPrice.Value, Volumes.Next())
			};

			var count = MaxBidsDepth - bids.Count;

			for (var i = 0; i < count; i++)
			{
				var quote = CreateQuote(bidPrice.Value, Sides.Buy);

				if (quote.Price <= 0)
					break;

				bids.Add(quote);
				bidPrice = quote.Price;
			}

			var asks = new List<QuoteChange>
			{
				new QuoteChange(Sides.Sell, askPrice.Value, Volumes.Next())
			};

			count = MaxAsksDepth - asks.Count;

			for (var i = 0; i < count; i++)
			{
				var quote = CreateQuote(askPrice.Value, Sides.Sell);

				if (quote.Price <= 0)
					break;

				asks.Add(quote);
				askPrice = quote.Price;
			}

			depth.Bids = bids;
			depth.Asks = asks;

			_newTrades = false;

			_currGenerations--;

			return depth;
		}
		public void UpdateBoardDefinition(BoardMessage message)
		{
			if (message == null)
				throw new ArgumentNullException("message");

			_timeZoneInfo = message.TimeZoneInfo;
		}