/// <summary> /// Обработать сообщение. /// </summary> /// <param name="message">Сообщение.</param> /// <returns>Результат обработки. Если будет возрвщено <see langword="null"/>, /// то генератору пока недостаточно данных для генерации нового сообщения.</returns> protected override Message OnProcess(Message message) { DateTimeOffset time; switch (message.Type) { case MessageTypes.Board: return(null); case MessageTypes.Level1Change: { var l1Msg = (Level1ChangeMessage)message; var value = l1Msg.Changes.TryGetValue(Level1Fields.LastTradePrice); if (value != null) { _lastTradePrice = (decimal)value; } time = l1Msg.ServerTime; break; } case MessageTypes.Execution: { var execMsg = (ExecutionMessage)message; switch (execMsg.ExecutionType) { case ExecutionTypes.Tick: case ExecutionTypes.Trade: _lastTradePrice = execMsg.TradePrice.Value; break; case ExecutionTypes.OrderLog: if (execMsg.TradePrice != null) { _lastTradePrice = execMsg.TradePrice.Value; } break; default: return(null); } time = execMsg.ServerTime; break; } case MessageTypes.Time: { var timeMsg = (TimeMessage)message; time = timeMsg.ServerTime; break; } default: return(null); } if (!IsTimeToGenerate(time)) { return(null); } var trade = new ExecutionMessage { SecurityId = SecurityId, TradeId = IdGenerator.GetNextId(), ServerTime = time, LocalTime = time.LocalDateTime, OriginSide = GenerateDirection ? RandomGen.GetEnum <Sides>() : (Sides?)null, Volume = Volumes.Next(), ExecutionType = ExecutionTypes.Tick }; var priceStep = SecurityDefinition.PriceStep ?? 0.01m; _lastTradePrice += RandomGen.GetInt(-MaxPriceStepCount, MaxPriceStepCount) * priceStep; if (_lastTradePrice <= 0) { _lastTradePrice = priceStep; } trade.TradePrice = _lastTradePrice; LastGenerationTime = time; return(trade); }
/// <summary> /// Обработать сообщение. /// </summary> /// <param name="message">Сообщение.</param> /// <returns>Результат обработки. Если будет возрвщено <see langword="null"/>, /// то генератору пока недостаточно данных для генерации нового сообщения.</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.WorkingTime.IsTradeTime(message.LocalTime); 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); }
/// <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) { DateTimeOffset time; switch (message.Type) { case MessageTypes.Level1Change: { var l1Msg = (Level1ChangeMessage)message; var value = l1Msg.Changes.TryGetValue(Level1Fields.LastTradePrice); if (value != null) { _lastOrderPrice = (decimal)value; } TradeGenerator.Process(message); time = l1Msg.ServerTime; break; } case MessageTypes.Execution: { var execMsg = (ExecutionMessage)message; switch (execMsg.ExecutionType) { case ExecutionTypes.Tick: _lastOrderPrice = execMsg.GetTradePrice(); break; default: return(null); } time = execMsg.ServerTime; break; } case MessageTypes.Time: { var timeMsg = (TimeMessage)message; time = timeMsg.ServerTime; break; } default: return(null); } if (!IsTimeToGenerate(time)) { return(null); } // TODO более реалистичную генерацию, так как сейчас объемы, цены и сделки c потолка var action = RandomGen.GetInt(0, 5); var isNew = action < 3 || _activeOrders.IsEmpty(); ExecutionMessage item; if (isNew) { var priceStep = SecurityDefinition.PriceStep ?? 0.01m; _lastOrderPrice += RandomGen.GetInt(-MaxPriceStepCount, MaxPriceStepCount) * priceStep; if (_lastOrderPrice <= 0) { _lastOrderPrice = priceStep; } item = new ExecutionMessage { OrderId = IdGenerator.GetNextId(), SecurityId = SecurityId, ServerTime = time, OrderState = OrderStates.Active, Volume = Volumes.Next(), Side = RandomGen.GetEnum <Sides>(), OrderPrice = _lastOrderPrice, ExecutionType = ExecutionTypes.OrderLog, }; _activeOrders.Enqueue((ExecutionMessage)item.Clone()); } else { var activeOrder = _activeOrders.Peek(); item = (ExecutionMessage)activeOrder.Clone(); item.ServerTime = time; var isMatched = action == 5; ExecutionMessage trade = null; if (isMatched) { trade = (ExecutionMessage)TradeGenerator.Process(message); } if (isMatched && trade != null) { item.Volume = RandomGen.GetInt(1, (int)activeOrder.SafeGetVolume()); item.TradeId = trade.TradeId; item.TradePrice = trade.TradePrice; item.TradeStatus = trade.TradeStatus; // TODO //quote.Trade = TradeGenerator.Generate(time); //item.Volume = activeOrder.Volume; //if (item.Side == Sides.Buy && quote.Trade.Price > quote.Order.Price) // item.TradePrice = item.Price; //else if (item.Side == Sides.Sell && quote.Trade.Price < quote.Order.Price) // item.TradePrice = item.Price; activeOrder.Volume -= item.Volume; if (activeOrder.Volume == 0) { item.OrderState = OrderStates.Done; _activeOrders.Dequeue(); } else { item.OrderState = OrderStates.Active; } } else { item.OrderState = OrderStates.Done; item.IsCancelled = true; _activeOrders.Dequeue(); } } LastGenerationTime = time; return(item); }
/// <inheritdoc /> protected override Message OnProcess(Message message) { DateTimeOffset time; switch (message.Type) { case MessageTypes.Board: return(null); case MessageTypes.Level1Change: { var l1Msg = (Level1ChangeMessage)message; var value = l1Msg.TryGetDecimal(Level1Fields.LastTradePrice); if (value != null) { _lastTradePrice = value.Value; } time = l1Msg.ServerTime; break; } case MessageTypes.Execution: { var execMsg = (ExecutionMessage)message; var price = execMsg.TradePrice; if (price != null) { _lastTradePrice = price.Value; } else if (execMsg.DataType != DataType.OrderLog) { return(null); } time = execMsg.ServerTime; break; } case MessageTypes.Time: { var timeMsg = (TimeMessage)message; time = timeMsg.ServerTime; break; } default: return(null); } if (!IsTimeToGenerate(time)) { return(null); } var v = Volumes.Next(); if (v == 0) { v = 1; } var trade = new ExecutionMessage { SecurityId = SecurityId, TradeId = IdGenerator.GetNextId(), ServerTime = time, LocalTime = time, OriginSide = GenerateOriginSide ? RandomGen.GetEnum <Sides>() : null, TradeVolume = v * (SecurityDefinition?.VolumeStep ?? 1m), DataTypeEx = DataType.Ticks }; var priceStep = SecurityDefinition.PriceStep ?? 0.01m; _lastTradePrice += RandomGen.GetInt(-MaxPriceStepCount, MaxPriceStepCount) * priceStep; if (_lastTradePrice <= 0) { _lastTradePrice = priceStep; } trade.TradePrice = _lastTradePrice; LastGenerationTime = time; return(trade); }