private void EmulationConnectorOnNewSecurity(Security security) { var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = EmulationSettings.StartTime }; if (security.PriceStep != null) { level1Info.TryAdd(Level1Fields.PriceStep, security.PriceStep.Value); } if (security.StepPrice != null) { level1Info.TryAdd(Level1Fields.StepPrice, security.StepPrice.Value); } level1Info.TryAdd(Level1Fields.MinPrice, security.MinPrice ?? 1m); level1Info.TryAdd(Level1Fields.MaxPrice, security.MaxPrice ?? 1000000m); if (security.MarginBuy != null) { level1Info.TryAdd(Level1Fields.MarginBuy, security.MarginBuy.Value); } if (security.MarginSell != null) { level1Info.TryAdd(Level1Fields.MarginSell, security.MarginSell.Value); } // fill level1 values EmulationConnector.SendInMessage(level1Info); }
public void UpdateData(Security sec, decimal price) { var ps = sec.PriceStep ?? 1; var msg = new Level1ChangeMessage { SecurityId = sec.ToSecurityId(), ServerTime = DateTimeOffset.Now, }; if (RandomGen.GetBool()) { msg.Changes.TryAdd(Level1Fields.BestBidPrice, price - RandomGen.GetInt(1, 10) * ps); } if (RandomGen.GetBool()) { msg.Changes.TryAdd(Level1Fields.BestAskPrice, price + RandomGen.GetInt(1, 10) * ps); } foreach (var l1Subscriptions in _l1Subscriptions) { _level1Received?.Invoke(l1Subscriptions, msg); } }
private void OnConnectorLevel1Received(Subscription subscription, Level1ChangeMessage message) { if (_subscriptions.ContainsKey(subscription)) { Level1Received?.Invoke(subscription, message); } }
public QuoteChangeMessage Process(Level1ChangeMessage message) { var bidPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidPrice); var askPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskPrice); if (bidPrice == null && askPrice == null) { return(null); } var bidVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidVolume); var askVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskVolume); if (_bidPrice == bidPrice && _askPrice == askPrice && _bidVolume == bidVolume && _askVolume == askVolume) { return(null); } _bidPrice = bidPrice; _askPrice = askPrice; _bidVolume = bidVolume; _askVolume = askVolume; return(new QuoteChangeMessage { SecurityId = SecurityId, ServerTime = message.ServerTime, LocalTime = message.LocalTime, BuildFrom = DataType.Level1, Bids = bidPrice == null?ArrayHelper.Empty <QuoteChange>() : new[] { new QuoteChange(bidPrice.Value, bidVolume ?? 0) }, Asks = askPrice == null?ArrayHelper.Empty <QuoteChange>() : new[] { new QuoteChange(askPrice.Value, askVolume ?? 0) }, }); }
private void ProcessLevel1Depth(Level1ChangeMessage message, decimal bestBidPrice, decimal bestBidVolume, decimal bestAskPrice, decimal bestAskVolume, List <ExecutionMessage> retVal) { if (message.LocalTime.Date == _lastDepthDate) { return; } QuoteChange ask = null; QuoteChange bid = null; if (bestAskPrice != 0 && bestAskVolume != 0) { ask = new QuoteChange(Sides.Sell, bestAskPrice, bestAskVolume); } if (bestBidPrice != 0 && bestBidVolume != 0) { bid = new QuoteChange(Sides.Buy, bestBidPrice, bestBidVolume); } if (ask == null && bid == null) { return; } retVal.AddRange(ProcessQuoteChange(message.LocalTime, bid != null ? new[] { bid } : ArrayHelper <QuoteChange> .EmptyArray, ask != null ? new[] { ask } : ArrayHelper <QuoteChange> .EmptyArray)); }
/// <summary> /// Добавить первый уровень маркет-данных. /// </summary> /// <param name="security">Инструмент.</param> /// <param name="message">Первый уровень маркет-данных.</param> protected virtual void AddLevel1Change(Security security, Level1ChangeMessage message) { if (security == null) { throw new ArgumentNullException("security"); } if (message == null) { throw new ArgumentNullException("message"); } message = (Level1ChangeMessage)message.Clone(); foreach (var change in message.Changes.ToArray()) { if (!_task.Settings.SupportedLevel1Fields.Contains(change.Key)) { message.Changes.Remove(change.Key); } } if (message.Changes.Count > 0) { _level1Buffer.Add(security, message); } }
public QuoteChangeMessage Process(Level1ChangeMessage message) { if (HasDepth) { return(null); } var bidPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidPrice); var askPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskPrice); if (bidPrice == null && askPrice == null) { return(null); } var bidVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidVolume); var askVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskVolume); return(new QuoteChangeMessage { SecurityId = _securityId, ServerTime = message.ServerTime, LocalTime = message.LocalTime, IsByLevel1 = true, Bids = bidPrice == null?Enumerable.Empty <QuoteChange>() : new[] { new QuoteChange(Sides.Buy, bidPrice.Value, bidVolume ?? 0) }, Asks = askPrice == null?Enumerable.Empty <QuoteChange>() : new[] { new QuoteChange(Sides.Sell, askPrice.Value, askVolume ?? 0) }, }); }
private void OnProcessLevel1(string[] data) { var f = Wrapper.FieldsLevel1; foreach (var row in data) { var cols = row.ToColumns(); var paperNo = f.PaperNo.GetValue(cols); var secId = new SecurityId { Native = paperNo }; var l1Msg = new Level1ChangeMessage { SecurityId = secId, ServerTime = (f.LastUpdateDate.GetValue(cols).Date + f.LastUpdateTime.GetValue(cols).TimeOfDay).ApplyTimeZone(TimeHelper.Moscow) }; l1Msg.Add(Level1Fields.State, f.TradingStatus.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MarginBuy, f.GoBuy.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MarginSell, f.GoSell.GetValue(cols)); l1Msg.TryAdd(Level1Fields.OpenInterest, (decimal)f.OpenPosQty.GetValue(cols)); var minPrice = f.MinDeal.GetValue(cols); var maxPrice = f.MaxDeal.GetValue(cols); l1Msg.TryAdd(Level1Fields.OpenPrice, f.OpenPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.ClosePrice, f.ClosePrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.HighPrice, maxPrice); l1Msg.TryAdd(Level1Fields.LowPrice, minPrice); l1Msg.TryAdd(Level1Fields.BestBidPrice, f.Buy.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestBidVolume, (decimal)f.BuyQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestAskPrice, f.Sell.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestAskVolume, (decimal)f.SellQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MinPrice, minPrice); l1Msg.TryAdd(Level1Fields.MaxPrice, maxPrice); l1Msg.TryAdd(Level1Fields.Multiplier, (decimal)f.LotSize.GetValue(cols)); l1Msg.TryAdd(Level1Fields.ImpliedVolatility, f.Volatility.GetValue(cols)); l1Msg.TryAdd(Level1Fields.TheorPrice, f.TheorPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.LastTradePrice, f.LastPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.LastTradeVolume, (decimal)f.LastQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.PriceStep, f.PriceStep.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BidsVolume, (decimal)f.BuySQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BidsCount, f.BuyCount.GetValue(cols)); l1Msg.TryAdd(Level1Fields.AsksVolume, (decimal)f.SellSQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.AsksCount, f.SellCount.GetValue(cols)); SendOutMessage(l1Msg); } }
private void OnQuotationsResponse(QuotationsResponse response) { foreach (var quote in response.Quotations) { var message = new Level1ChangeMessage { SecurityId = new SecurityId { Native = quote.SecId }, ServerTime = SessionHolder.CurrentTime.Convert(TimeHelper.Moscow), }; message.TryAdd(Level1Fields.AccruedCouponIncome, quote.AccruedIntValue); message.TryAdd(Level1Fields.OpenPrice, quote.Open); message.TryAdd(Level1Fields.HighPrice, quote.High); message.TryAdd(Level1Fields.LowPrice, quote.Low); message.TryAdd(Level1Fields.ClosePrice, quote.ClosePrice); message.TryAdd(Level1Fields.BidsCount, quote.BidsCount); message.TryAdd(Level1Fields.BidsVolume, (decimal?)quote.BidsVolume); message.TryAdd(Level1Fields.AsksCount, quote.AsksCount); message.TryAdd(Level1Fields.AsksVolume, (decimal?)quote.AsksVolume); message.TryAdd(Level1Fields.HighBidPrice, quote.HighBid); message.TryAdd(Level1Fields.LowAskPrice, quote.LowAsk); message.TryAdd(Level1Fields.Yield, quote.Yield); message.TryAdd(Level1Fields.MarginBuy, quote.BuyDeposit); message.TryAdd(Level1Fields.MarginSell, quote.SellDeposit); message.TryAdd(Level1Fields.HistoricalVolatility, quote.Volatility); message.TryAdd(Level1Fields.TheorPrice, quote.TheoreticalPrice); message.TryAdd(Level1Fields.Change, quote.Change); message.TryAdd(Level1Fields.Volume, (decimal?)quote.VolToday); message.TryAdd(Level1Fields.StepPrice, quote.PointCost); message.TryAdd(Level1Fields.OpenInterest, (decimal?)quote.OpenInterest); message.TryAdd(Level1Fields.TradesCount, quote.TradesCount); if (quote.Status != null) { message.Add(Level1Fields.State, quote.Status.Value.FromTransaq()); } // Transaq передает только изменения (например, передать только цену сделки, если объем при этом не изменился) message.TryAdd(Level1Fields.LastTradePrice, quote.LastTradePrice); message.TryAdd(Level1Fields.LastTradeVolume, (decimal?)quote.LastTradeVolume); if (quote.LastTradeTime != null) { message.Add(Level1Fields.LastTradeTime, quote.LastTradeTime.Value.ApplyTimeZone(TimeHelper.Moscow)); } message.TryAdd(Level1Fields.BestBidPrice, quote.BestBidPrice); message.TryAdd(Level1Fields.BestBidVolume, (decimal?)quote.BestBidVolume); message.TryAdd(Level1Fields.BestAskPrice, quote.BestAskPrice); message.TryAdd(Level1Fields.BestAskVolume, (decimal?)quote.BestAskVolume); SendOutMessage(message); } }
protected override void AddLevel1Change(Security security, Level1ChangeMessage message) { if (_settings.IsDownloadSecurityChangesHistory) { return; } base.AddLevel1Change(security, message); }
private static string Level1ToString(Level1ChangeMessage level) { var sb = new StringBuilder(); foreach (var kvp in level.Changes) { sb.Append($"{kvp.Key}:{kvp.Value};"); } return($"{level.ServerTime};{level.SecurityId.SecurityCode};{sb}"); }
private static Level1ChangeMessage ToLevel2(string value) { var parts = value.SplitByComma(); var isBidValid = parts[10] == "T"; var isAskValid = parts[11] == "T"; if (!isBidValid && !isAskValid) { return(null); } var date = parts[7].ToDateTime("yyyy-MM-dd"); var l1Msg = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = parts[0], BoardCode = parts[1] }, }; // http://www.iqfeed.net/dev/api/docs/ConditionCodes.cfm l1Msg.Add(Level1Fields.IsSystem, parts[8] == "52"); if (isAskValid) { l1Msg.ServerTime = date.Add(parts[9].To <TimeSpan>()).ApplyTimeZone(TimeHelper.Est); l1Msg .TryAdd(Level1Fields.BestAskPrice, parts[3].To <decimal>()) .TryAdd(Level1Fields.BestAskVolume, parts[5].To <decimal>()) .Add(Level1Fields.BestAskTime, l1Msg.ServerTime); } if (isBidValid) { var bidTime = date.Add(parts[6].To <TimeSpan>()).ApplyTimeZone(TimeHelper.Est); if (bidTime > l1Msg.ServerTime) { l1Msg.ServerTime = bidTime; } l1Msg .TryAdd(Level1Fields.BestBidPrice, parts[2].To <decimal>()) .TryAdd(Level1Fields.BestBidVolume, parts[4].To <decimal>()) .Add(Level1Fields.BestBidTime, bidTime); } return(l1Msg); }
private void OnValuesChanged(Security security, IEnumerable <KeyValuePair <Level1Fields, object> > changes, DateTimeOffset serverTime, DateTime localTime) { var msg = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = serverTime, LocalTime = localTime, }; msg.Changes.AddRange(changes); AddLevel1Change(security, msg); }
/// <summary> /// Преобразовать первый уровень маркет-данных. /// </summary> /// <param name="message">Первый уровень маркет-данных.</param> /// <returns>Поток <see cref="ExecutionMessage"/>.</returns> public IEnumerable <ExecutionMessage> ToExecutionLog(Level1ChangeMessage message) { if (message == null) { throw new ArgumentNullException("message"); } var retVal = new List <ExecutionMessage>(); var bestBidPrice = 0m; var bestBidVolume = 0m; var bestAskPrice = 0m; var bestAskVolume = 0m; var lastTradePrice = 0m; var lastTradeVolume = 0m; foreach (var change in message.Changes) { switch (change.Key) { case Level1Fields.LastTradePrice: lastTradePrice = (decimal)change.Value; break; case Level1Fields.LastTradeVolume: lastTradeVolume = (decimal)change.Value; break; case Level1Fields.BestBidPrice: bestBidPrice = (decimal)change.Value; break; case Level1Fields.BestBidVolume: bestBidVolume = (decimal)change.Value; break; case Level1Fields.BestAskPrice: bestAskPrice = (decimal)change.Value; break; case Level1Fields.BestAskVolume: bestAskVolume = (decimal)change.Value; break; } } ProcessLevel1Depth(message, bestBidPrice, bestBidVolume, bestAskPrice, bestAskVolume, retVal); ProcessLevel1Trade(message, lastTradePrice, lastTradeVolume, retVal); return(retVal); }
/// <summary> /// To process the message, containing market data. /// </summary> /// <param name="levelMsg">The message, containing market data.</param> public void ProcessLevel1(Level1ChangeMessage levelMsg) { var priceStep = levelMsg.Changes.TryGetValue(Level1Fields.PriceStep); if (priceStep != null) { PriceStep = (decimal)priceStep; _recalcUnrealizedPnL = true; } var stepPrice = levelMsg.Changes.TryGetValue(Level1Fields.StepPrice); if (stepPrice != null) { StepPrice = (decimal)stepPrice; _recalcUnrealizedPnL = true; } var lotMultiplier = levelMsg.Changes.TryGetValue(Level1Fields.Multiplier); if (lotMultiplier != null) { LotMultiplier = (decimal)lotMultiplier; _recalcUnrealizedPnL = true; } var tradePrice = levelMsg.Changes.TryGetValue(Level1Fields.LastTradePrice); if (tradePrice != null) { TradePrice = (decimal)tradePrice; _recalcUnrealizedPnL = true; } var bidPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestBidPrice); if (bidPrice != null) { BidPrice = (decimal)bidPrice; _recalcUnrealizedPnL = true; } var askPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestAskPrice); if (askPrice != null) { AskPrice = (decimal)askPrice; _recalcUnrealizedPnL = true; } }
private void SessionOnStiQuoteSnap(ref structSTIQuoteSnap structQuoteSnap) { var l1CngMsg = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = structQuoteSnap.bstrSymbol, BoardCode = structQuoteSnap.bstrExch }, ServerTime = structQuoteSnap.bstrUpdateTime.StrToTime(), }; l1CngMsg.TryAdd(Level1Fields.BestAskPrice, (decimal)structQuoteSnap.fAskPrice); l1CngMsg.TryAdd(Level1Fields.BestBidPrice, (decimal)structQuoteSnap.fAskPrice); l1CngMsg.TryAdd(Level1Fields.BestAskVolume, (decimal)structQuoteSnap.nAskSize); l1CngMsg.TryAdd(Level1Fields.BestBidVolume, (decimal)structQuoteSnap.nBidSize); l1CngMsg.TryAdd(Level1Fields.OpenPrice, (decimal)structQuoteSnap.fOpenPrice); l1CngMsg.TryAdd(Level1Fields.HighPrice, (decimal)structQuoteSnap.fHighPrice); l1CngMsg.TryAdd(Level1Fields.LowPrice, (decimal)structQuoteSnap.fLowPrice); l1CngMsg.TryAdd(Level1Fields.LastTradePrice, (decimal)structQuoteSnap.fLastPrice); l1CngMsg.TryAdd(Level1Fields.LastTradeVolume, (decimal)structQuoteSnap.nLastSize); l1CngMsg.TryAdd(Level1Fields.OpenInterest, (decimal)structQuoteSnap.nOpenInterest); l1CngMsg.TryAdd(Level1Fields.Volume, (decimal)structQuoteSnap.nCumVolume); l1CngMsg.TryAdd(Level1Fields.VWAP, (decimal)structQuoteSnap.fVwap); l1CngMsg.TryAdd(Level1Fields.ClosePrice, (decimal)structQuoteSnap.fClosePrice); // цена закрытия прошлого дня. SendOutMessage(l1CngMsg); if (_subscribedSecuritiesToTrade.Cache.Contains(structQuoteSnap.bstrSymbol) && structQuoteSnap.fLastPrice != 0) { var tickMsg = new ExecutionMessage { ExecutionType = ExecutionTypes.Tick, SecurityId = new SecurityId { SecurityCode = structQuoteSnap.bstrSymbol, BoardCode = structQuoteSnap.bstrExch }, //TradeId = structQuoteSnap., TradePrice = (decimal)structQuoteSnap.fLastPrice, Volume = structQuoteSnap.nLastSize, //OriginSide = action.ToSide(), ServerTime = structQuoteSnap.bstrUpdateTime.StrToTime() }; SendOutMessage(tickMsg); } }
/// <summary> /// To convert first level of market data. /// </summary> /// <param name="message">Level 1.</param> /// <returns>Stream <see cref="Message"/>.</returns> public IEnumerable <Message> ToExecutionLog(Level1ChangeMessage message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } if (message.IsContainsTick()) { yield return(message.ToTick()); } if (message.IsContainsQuotes() && !HasDepth(message.LocalTime)) { var prevBidPrice = _prevBidPrice; var prevBidVolume = _prevBidVolume; var prevAskPrice = _prevAskPrice; var prevAskVolume = _prevAskVolume; _prevBidPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidPrice) ?? _prevBidPrice; _prevBidVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidVolume) ?? _prevBidVolume; _prevAskPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskPrice) ?? _prevAskPrice; _prevAskVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskVolume) ?? _prevAskVolume; if (_prevBidPrice == 0) { _prevBidPrice = null; } if (_prevAskPrice == 0) { _prevAskPrice = null; } if (prevBidPrice == _prevBidPrice && prevBidVolume == _prevBidVolume && prevAskPrice == _prevAskPrice && prevAskVolume == _prevAskVolume) { yield break; } yield return(new QuoteChangeMessage { SecurityId = message.SecurityId, LocalTime = message.LocalTime, ServerTime = message.ServerTime, Bids = _prevBidPrice == null?ArrayHelper.Empty <QuoteChange>() : new[] { new QuoteChange(_prevBidPrice.Value, _prevBidVolume ?? 0) }, Asks = _prevAskPrice == null?ArrayHelper.Empty <QuoteChange>() : new[] { new QuoteChange(_prevAskPrice.Value, _prevAskVolume ?? 0) }, }); } }
private static string Level1ToString(Level1ChangeMessage level) { var sb = new StringBuilder(); foreach (var kvp in level.Changes) { sb.Append(string.Format("{0}:{1};", kvp.Key, kvp.Value)); } return(string.Format("{0};{1};{2}", level.ServerTime, level.SecurityId.SecurityCode, sb )); }
private void OnSecInfoResponse(SecInfoResponse response) { var securityId = new SecurityId { Native = response.SecId, SecurityCode = response.SecCode, BoardCode = _boards[response.Market], }; SendOutMessage(new SecurityMessage { SecurityId = securityId, ExpiryDate = response.MatDate == null ? (DateTimeOffset?)null : response.MatDate.Value.ApplyTimeZone(TimeHelper.Moscow), OptionType = response.PutCall == null ? (OptionTypes?)null : response.PutCall.Value.FromTransaq(), }); var l1Msg = new Level1ChangeMessage { SecurityId = new SecurityId { Native = response.SecId }, ServerTime = SessionHolder.CurrentTime.Convert(TimeHelper.Moscow), }; l1Msg.TryAdd(Level1Fields.MinPrice, response.MinPrice); l1Msg.TryAdd(Level1Fields.MaxPrice, response.MaxPrice); var marginBuy = response.BuyDeposit; if (marginBuy == null || marginBuy == 0m) { marginBuy = response.BgoBuy; } var marginSell = response.SellDeposit; if (marginSell == null || marginSell == 0m) { marginSell = response.BgoC; } l1Msg.TryAdd(Level1Fields.MarginBuy, marginBuy); l1Msg.TryAdd(Level1Fields.MarginSell, marginSell); SendOutMessage(l1Msg); }
/// <summary> /// Initializes a new instance of the <see cref="Level1ChangeCandleBuilderSourceValue"/>. /// </summary> /// <param name="message">The message containing the level1 market data.</param> /// <param name="field">Level one market-data field, which is used as an candle value.</param> public Level1ChangeCandleBuilderSourceValue(Level1ChangeMessage message, Level1Fields field) { if (message == null) { throw new ArgumentNullException(nameof(message)); } Level1Change = message; Field = field; _volume = null; switch (field) { case Level1Fields.BestBidPrice: case Level1Fields.BestAskPrice: { var price = GetValue(message, field); if (price != null) { _price = price.Value; } break; } case Level1Fields.SpreadMiddle: { var bid = GetValue(message, Level1Fields.BestBidPrice); var ask = GetValue(message, Level1Fields.BestAskPrice); if (bid != null && ask != null) { _price = (ask.Value + bid.Value) / 2; } break; } default: throw new ArgumentOutOfRangeException(); } }
private void OnSessionMarketDataChanged(OrderBookEvent orderBookEvent) { var time = TimeHelper.GregorianStart.AddMilliseconds(orderBookEvent.Timestamp).ApplyTimeZone(TimeZoneInfo.Utc); var secId = new SecurityId { Native = orderBookEvent.InstrumentId }; var l1Msg = new Level1ChangeMessage { ServerTime = time, SecurityId = secId, }; if (orderBookEvent.HasMarketClosePrice) { l1Msg.Add(Level1Fields.ClosePrice, orderBookEvent.MktClosePrice); } if (orderBookEvent.HasDailyHighestTradedPrice) { l1Msg.Add(Level1Fields.HighPrice, orderBookEvent.DailyHighestTradedPrice); } if (orderBookEvent.HasDailyLowestTradedPrice) { l1Msg.Add(Level1Fields.LowPrice, orderBookEvent.DailyLowestTradedPrice); } if (orderBookEvent.HasLastTradedPrice) { l1Msg.Add(Level1Fields.LastTradePrice, orderBookEvent.LastTradedPrice); } SendOutMessage(l1Msg); SendOutMessage(new QuoteChangeMessage { SecurityId = secId, Bids = orderBookEvent.BidPrices.Select(p => new QuoteChange(Sides.Buy, p.Price, p.Quantity)), Asks = orderBookEvent.AskPrices.Select(p => new QuoteChange(Sides.Sell, p.Price, p.Quantity)), ServerTime = time }); }
private void TraderOnValuesChanged(Security security, IEnumerable <KeyValuePair <Level1Fields, object> > changes, DateTimeOffset serverTime, DateTime localTime) { var wnd = _level1Windows.TryGetValue(security); if (wnd == null) { return; } var msg = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = serverTime, LocalTime = localTime }; msg.Changes.AddRange(changes); wnd.Level1Grid.Messages.Add(msg); }
/// <summary> /// To process the message, containing market data. /// </summary> /// <param name="levelMsg">The message, containing market data.</param> public void ProcessLevel1(Level1ChangeMessage levelMsg) { var priceStep = levelMsg.Changes.TryGetValue(Level1Fields.PriceStep); if (priceStep != null) { PriceStep = (decimal)priceStep; _unrealizedPnL = null; } var stepPrice = levelMsg.Changes.TryGetValue(Level1Fields.StepPrice); if (stepPrice != null) { StepPrice = (decimal)stepPrice; _unrealizedPnL = null; } var tradePrice = levelMsg.Changes.TryGetValue(Level1Fields.LastTradePrice); if (tradePrice != null) { TradePrice = (decimal)tradePrice; _unrealizedPnL = null; } var bidPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestBidPrice); if (bidPrice != null) { BidPrice = (decimal)bidPrice; _unrealizedPnL = null; } var askPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestAskPrice); if (askPrice != null) { AskPrice = (decimal)askPrice; _unrealizedPnL = null; } }
private void ProcessLevel1ChangeMessage(Level1ChangeMessage message) { var security = LookupSecurity(message.SecurityId); if (UpdateSecurityByLevel1) { security.ApplyChanges(message); RaiseSecurityChanged(security); } var values = GetSecurityValues(security); lock (values.SyncRoot) { foreach (var change in message.Changes) values[(int)change.Key] = change.Value; } RaiseValuesChanged(security, message.Changes, message.ServerTime, message.LocalTime); }
private void ProcessLevel1ChangeMessage(Level1ChangeMessage message) { var security = LookupSecurity(message.SecurityId); if (UpdateSecurityByLevel1) { security.ApplyChanges(message); RaiseSecurityChanged(security); } var values = GetSecurityValues(security); lock (values.SyncRoot) { foreach (var change in message.Changes) values[(int)change.Key] = change.Value; } RaiseValuesChanged(security, message.Changes, message.ServerTime, message.LocalTime); if (CreateDepthFromLevel1) { // генерация стакана из Level1 var quoteMsg = GetBuilder(message.SecurityId).Process(message); if (quoteMsg != null) { ProcessQuotesMessage(security, quoteMsg, true); CreateAssociatedSecurityQuotes(quoteMsg); } } if (CreateAssociatedSecurity && !IsAssociated(message.SecurityId.BoardCode)) { // обновление BestXXX для ALL из конкретных тикеров var clone = (Level1ChangeMessage)message.Clone(); clone.SecurityId = CreateAssociatedId(clone.SecurityId); ProcessLevel1ChangeMessage(clone); } }
private void FlushQuotes(SecurityId secId) { var quotes = _quotes.TryGetValue(secId); if (quotes == null) { return; } _quotes.Remove(secId); foreach (var pair in quotes.CachedPairs) { var message = new Level1ChangeMessage { SecurityId = secId, ServerTime = pair.Key }; var bid = pair.Value.First; if (bid != null) { message .TryAdd(Level1Fields.BestBidPrice, bid.Price.ToDecimal()) .TryAdd(Level1Fields.BestBidVolume, (decimal)bid.Size); } var ask = pair.Value.Second; if (ask != null) { message .TryAdd(Level1Fields.BestAskPrice, ask.Price.ToDecimal()) .TryAdd(Level1Fields.BestAskVolume, (decimal)ask.Size); } SendOutMessage(message); } }
private void SessionOnStiGreeksUpdate(ref structSTIGreeksUpdate structGreeksUpdate) { var message = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = structGreeksUpdate.bstrSymbol, BoardCode = AssociatedBoardCode, }, ServerTime = CurrentTime, }; message.TryAdd(Level1Fields.Delta, (decimal)structGreeksUpdate.fDelta); message.TryAdd(Level1Fields.Gamma, (decimal)structGreeksUpdate.fGamma); message.TryAdd(Level1Fields.Theta, (decimal)structGreeksUpdate.fTheta); message.TryAdd(Level1Fields.Vega, (decimal)structGreeksUpdate.fVega); message.TryAdd(Level1Fields.Rho, (decimal)structGreeksUpdate.fRho); message.TryAdd(Level1Fields.TheorPrice, (decimal)structGreeksUpdate.fTheoPrice); message.TryAdd(Level1Fields.ImpliedVolatility, (decimal)structGreeksUpdate.fImpVol); SendOutMessage(message); }
private void ProcessLevel1Trade(Level1ChangeMessage message, decimal lastTradePrice, decimal lastTradeVolume, List <ExecutionMessage> retVal) { if (message.LocalTime.Date == _lastTradeDate) { return; } if (lastTradePrice == 0 || lastTradeVolume == 0) { return; } var exec = new ExecutionMessage { LocalTime = message.LocalTime, ServerTime = message.ServerTime, SecurityId = message.SecurityId, ExecutionType = ExecutionTypes.Tick, TradePrice = lastTradePrice, Volume = lastTradeVolume, }; retVal.AddRange(ProcessExecution(exec)); }
private void OnQuotationsResponse(QuotationsResponse response) { foreach (var quote in response.Quotations) { var message = new Level1ChangeMessage { SecurityId = new SecurityId { Native = quote.SecId }, ServerTime = CurrentTime.Convert(TimeHelper.Moscow), }; message.TryAdd(Level1Fields.AccruedCouponIncome, quote.AccruedIntValue); message.TryAdd(Level1Fields.OpenPrice, quote.Open); message.TryAdd(Level1Fields.HighPrice, quote.High); message.TryAdd(Level1Fields.LowPrice, quote.Low); message.TryAdd(Level1Fields.ClosePrice, quote.ClosePrice); message.TryAdd(Level1Fields.BidsCount, quote.BidsCount); message.TryAdd(Level1Fields.BidsVolume, (decimal?)quote.BidsVolume); message.TryAdd(Level1Fields.AsksCount, quote.AsksCount); message.TryAdd(Level1Fields.AsksVolume, (decimal?)quote.AsksVolume); message.TryAdd(Level1Fields.HighBidPrice, quote.HighBid); message.TryAdd(Level1Fields.LowAskPrice, quote.LowAsk); message.TryAdd(Level1Fields.Yield, quote.Yield); message.TryAdd(Level1Fields.MarginBuy, quote.BuyDeposit); message.TryAdd(Level1Fields.MarginSell, quote.SellDeposit); message.TryAdd(Level1Fields.HistoricalVolatility, quote.Volatility); message.TryAdd(Level1Fields.TheorPrice, quote.TheoreticalPrice); message.TryAdd(Level1Fields.Change, quote.Change); message.TryAdd(Level1Fields.Volume, (decimal?)quote.VolToday); message.TryAdd(Level1Fields.StepPrice, quote.PointCost); message.TryAdd(Level1Fields.OpenInterest, (decimal?)quote.OpenInterest); message.TryAdd(Level1Fields.TradesCount, quote.TradesCount); if (quote.Status != null) message.Add(Level1Fields.State, quote.Status.Value.FromTransaq()); // Transaq передает только изменения (например, передать только цену сделки, если объем при этом не изменился) message.TryAdd(Level1Fields.LastTradePrice, quote.LastTradePrice); message.TryAdd(Level1Fields.LastTradeVolume, (decimal?)quote.LastTradeVolume); if (quote.LastTradeTime != null) message.Add(Level1Fields.LastTradeTime, quote.LastTradeTime.Value.ToDto()); message.TryAdd(Level1Fields.BestBidPrice, quote.BestBidPrice); message.TryAdd(Level1Fields.BestBidVolume, (decimal?)quote.BestBidVolume); message.TryAdd(Level1Fields.BestAskPrice, quote.BestAskPrice); message.TryAdd(Level1Fields.BestAskVolume, (decimal?)quote.BestAskVolume); SendOutMessage(message); } }
private void OnProcessSecurities(long transactionId, string[] data) { var f = Wrapper.FieldsSecurities; var secMessages = new List<Tuple<int, SecurityMessage>>(); var level1Messages = new List<Level1ChangeMessage>(); foreach (var row in data) { var cols = row.ToColumns(); var secType = f.ATCode.GetValue(cols); if(secType == null) continue; var paperNo = f.PaperNo.GetValue(cols); var code = f.PaperCode.GetValue(cols); var name = f.AnsiName.GetValue(cols); var time = f.ILastUpdate.GetValue(cols); _securityCodes[paperNo] = code; var secId = new SecurityId { Native = paperNo, SecurityCode = code, BoardCode = this.GetBoardCode(f.PlaceCode.GetValue(cols)) }; var msg = new SecurityMessage { SecurityId = secId, Name = name, ShortName = name, SecurityType = secType, Multiplier = f.LotSize.GetValue(cols), PriceStep = f.PriceStep.GetValue(cols), //LocalTime = time, Currency = f.CurrCode.GetValue(cols), Strike = f.Strike.GetValue(cols) }; if(msg.SecurityType == SecurityTypes.Option || msg.SecurityType == SecurityTypes.Future) msg.ExpiryDate = f.MatDate.GetValue(cols).ApplyTimeZone(TimeHelper.Moscow); if (msg.SecurityType == SecurityTypes.Option) { msg.OptionType = f.ATCode.GetStrValue(cols).ATCodeToOptionType(); msg.Strike = f.Strike.GetValue(cols); } secMessages.Add(Tuple.Create(f.BasePaperNo.GetValue(cols), msg)); var l1Msg = new Level1ChangeMessage { SecurityId = secId, ServerTime = time.ApplyTimeZone(TimeHelper.Moscow) }; l1Msg.TryAdd(Level1Fields.MarginBuy, f.GoBuy.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MarginSell, f.GoSell.GetValue(cols)); l1Msg.TryAdd(Level1Fields.StepPrice, f.PriceStepCost.GetValue(cols)); level1Messages.Add(l1Msg); } secMessages.Where(t => t.Item2.SecurityType == SecurityTypes.Option).ForEach(t => t.Item2.UnderlyingSecurityCode = _securityCodes.TryGetValue(t.Item1)); secMessages.ForEach(t => SendOutMessage(t.Item2)); level1Messages.ForEach(SendOutMessage); if (transactionId > 0) { SendOutMessage(new SecurityLookupResultMessage { OriginalTransactionId = transactionId, }); } }
private static Level1ChangeMessage ToLevel2(string value) { var parts = value.SplitByComma(); var isBidValid = parts[10] == "T"; var isAskValid = parts[11] == "T"; if (!isBidValid && !isAskValid) return null; var date = parts[7].ToDateTime("yyyy-MM-dd"); var l1Msg = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = parts[0], BoardCode = parts[1] }, }; // http://www.iqfeed.net/dev/api/docs/ConditionCodes.cfm l1Msg.Add(Level1Fields.IsSystem, parts[8] == "52"); if (isAskValid) { l1Msg.ServerTime = date.Add(parts[9].To<TimeSpan>()).ApplyTimeZone(TimeHelper.Est); l1Msg .TryAdd(Level1Fields.BestAskPrice, parts[3].To<decimal>()) .TryAdd(Level1Fields.BestAskVolume, parts[5].To<decimal>()) .Add(Level1Fields.BestAskTime, l1Msg.ServerTime); } if (isBidValid) { var bidTime = date.Add(parts[6].To<TimeSpan>()).ApplyTimeZone(TimeHelper.Est); if (bidTime > l1Msg.ServerTime) l1Msg.ServerTime = bidTime; l1Msg .TryAdd(Level1Fields.BestBidPrice, parts[2].To<decimal>()) .TryAdd(Level1Fields.BestBidVolume, parts[4].To<decimal>()) .Add(Level1Fields.BestBidTime, bidTime); } return l1Msg; }
private void StartButtonOnClick(object sender, RoutedEventArgs e) { _logManager.Sources.Clear(); _bufferedChart.ClearAreas(); Curve.Clear(); PositionCurve.Clear(); if (HistoryPathTextBox.Text.IsEmpty() || !Directory.Exists(HistoryPathTextBox.Text)) { MessageBox.Show("Wrong path."); return; } if (_connector != null && _connector.State != EmulationStates.Stopped) { MessageBox.Show("Already launched."); return; } if (Composition == null) { MessageBox.Show("No strategy selected."); return; } var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(SecusityTextBox.Text); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = (TimeSpan)TimeFrameComboBox.SelectedItem; var useCandles = (string)MarketDataTypeComboBox.SelectedItem != "Ticks"; // create test security var security = new Security { Id = SecusityTextBox.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPathTextBox.Text) }; var startTime = ((DateTime)FromDatePicker.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)ToDatePicke.Value).ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; _logManager.Sources.Add(_connector); var candleManager = !useCandles ? new CandleManager(new TradeCandleBuilderSourceEx(_connector)) : new CandleManager(_connector); // create strategy based on 80 5-min и 10 5-min var strategy = new DiagramStrategy { Volume = 1, Portfolio = portfolio, Security = security, Connector = _connector, //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, Composition = Composition, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; strategy.SetChart(_bufferedChart); strategy.SetCandleManager(candleManager); _logManager.Sources.Add(strategy); strategy.OrderRegistering += OnStrategyOrderRegistering; strategy.OrderReRegistering += OnStrategyOrderReRegistering; strategy.OrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.StopOrderRegistering += OnStrategyOrderRegistering; strategy.StopOrderReRegistering += OnStrategyOrderReRegistering; strategy.StopOrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.NewMyTrades += OnStrategyNewMyTrade; var pnlCurve = Curve.CreateCurve(LocalizedStrings.PnL + " " + strategy.Name, Colors.DarkGreen, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + strategy.Name, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + strategy.Name, Colors.Red, EquityCurveChartStyles.DashedLine); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; var posItems = PositionCurve.CreateCurve(strategy.Name, Colors.DarkGreen); strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); _connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); if (!useCandles) _connector.RegisterTrades(security); // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); SetIsEnabled(false); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; MessageBox.Show("Done."); } else MessageBox.Show("Cancelled."); }); break; case EmulationStates.Started: SetIsEnabled(true); break; } }; TicksAndDepthsProgress.Value = 0; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void OnSecInfoResponse(SecInfoResponse response) { var securityId = new SecurityId { Native = response.SecId, SecurityCode = response.SecCode, BoardCode = _boards[response.Market], }; SendOutMessage(new SecurityMessage { SecurityId = securityId, ExpiryDate = response.MatDate?.ApplyTimeZone(TimeHelper.Moscow), OptionType = response.PutCall?.FromTransaq(), }); var l1Msg = new Level1ChangeMessage { SecurityId = new SecurityId { Native = response.SecId }, ServerTime = CurrentTime.Convert(TimeHelper.Moscow), }; l1Msg.TryAdd(Level1Fields.MinPrice, response.MinPrice); l1Msg.TryAdd(Level1Fields.MaxPrice, response.MaxPrice); var marginBuy = response.BuyDeposit; if (marginBuy == null || marginBuy == 0m) marginBuy = response.BgoBuy; var marginSell = response.SellDeposit; if (marginSell == null || marginSell == 0m) marginSell = response.BgoC; l1Msg.TryAdd(Level1Fields.MarginBuy, marginBuy); l1Msg.TryAdd(Level1Fields.MarginSell, marginSell); SendOutMessage(l1Msg); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (Math.Abs(TestingProcess.Value - 0) > double.Epsilon) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); // SMA periods var periods = new[] { new Tuple<int, int, Color>(80, 10, Colors.DarkGreen), new Tuple<int, int, Color>(70, 8, Colors.Red), new Tuple<int, int, Color>(60, 6, Colors.DarkBlue) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var timeFrame = TimeSpan.FromMinutes(5); // create test security var security = new Security { Id = "RIZ2@FORTS", // sec id has the same name as folder with historical data Code = "RIZ2", Name = "RTS-12.12", Board = ExchangeBoard.Forts, }; var startTime = new DateTime(2012, 10, 1); var stopTime = new DateTime(2012, 10, 31); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var batchEmulation = new BatchEmulation(new[] { security }, new[] { portfolio }, storageRegistry) { EmulationSettings = { MarketTimeChangedInterval = timeFrame, StartTime = startTime, StopTime = stopTime, // count of parallel testing strategies BatchSize = periods.Length, } }; // handle historical time for update ProgressBar batchEmulation.ProgressChanged += (curr, total) => this.GuiAsync(() => TestingProcess.Value = total); batchEmulation.StateChanged += (oldState, newState) => { if (batchEmulation.State != EmulationStates.Stopped) return; this.GuiAsync(() => { if (batchEmulation.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); }; // получаем подключение для эмуляции var connector = batchEmulation.EmulationConnector; logManager.Sources.Add(connector); connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values connector.SendInMessage(level1Info); connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { // order book freq refresh is 1 sec Interval = TimeSpan.FromSeconds(1), }); }; TestingProcess.Maximum = 100; TestingProcess.Value = 0; _startEmulationTime = DateTime.Now; var strategies = periods .Select(period => { var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based SMA var strategy = new SmaStrategy(series, new SimpleMovingAverage { Length = period.Item1 }, new SimpleMovingAverage { Length = period.Item2 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = connector, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; strategy.SetCandleManager(new CandleManager(connector)); var curveItems = Curve.CreateCurve(LocalizedStrings.Str3026Params.Put(period.Item1, period.Item2), period.Item3); strategy.PnLChanged += () => { var data = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL, }; this.GuiAsync(() => curveItems.Add(data)); }; Stat.AddStrategies(new[] { strategy }); return strategy; }) .ToEx(periods.Length); // start emulation batchEmulation.Start(strategies); }
private static string Level1ToString(Level1ChangeMessage level) { var sb = new StringBuilder(); foreach (var kvp in level.Changes) sb.Append(string.Format("{0}:{1};", kvp.Key, kvp.Value)); return string.Format("{0};{1};{2}", level.ServerTime, level.SecurityId.SecurityCode, sb ); }
private void FlushQuotes(SecurityId secId) { var quotes = _quotes.TryGetValue(secId); if (quotes == null) return; _quotes.Remove(secId); foreach (var pair in quotes.CachedPairs) { var message = new Level1ChangeMessage { SecurityId = secId, ServerTime = pair.Key }; var bid = pair.Value.First; if (bid != null) { message .TryAdd(Level1Fields.BestBidPrice, bid.Price.ToDecimal()) .TryAdd(Level1Fields.BestBidVolume, (decimal)bid.Size); } var ask = pair.Value.Second; if (ask != null) { message .TryAdd(Level1Fields.BestAskPrice, ask.Price.ToDecimal()) .TryAdd(Level1Fields.BestAskVolume, (decimal)ask.Size); } SendOutMessage(message); } }
private void SessionOnStiQuoteSnap(ref structSTIQuoteSnap structQuoteSnap) { var l1CngMsg = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = structQuoteSnap.bstrSymbol, BoardCode = structQuoteSnap.bstrExch }, ServerTime = structQuoteSnap.bstrUpdateTime.StrToTime(), }; if (structQuoteSnap.bAskPrice != 0) l1CngMsg.TryAdd(Level1Fields.BestAskPrice, (decimal)structQuoteSnap.fAskPrice); if (structQuoteSnap.bBidPrice != 0) l1CngMsg.TryAdd(Level1Fields.BestBidPrice, (decimal)structQuoteSnap.fBidPrice); l1CngMsg.TryAdd(Level1Fields.BestAskVolume, (decimal)structQuoteSnap.nAskSize); l1CngMsg.TryAdd(Level1Fields.BestBidVolume, (decimal)structQuoteSnap.nBidSize); if (structQuoteSnap.bOpenPrice != 0) l1CngMsg.TryAdd(Level1Fields.OpenPrice, (decimal)structQuoteSnap.fOpenPrice); if (structQuoteSnap.bHighPrice != 0) l1CngMsg.TryAdd(Level1Fields.HighPrice, (decimal)structQuoteSnap.fHighPrice); if (structQuoteSnap.bLowPrice != 0) l1CngMsg.TryAdd(Level1Fields.LowPrice, (decimal)structQuoteSnap.fLowPrice); if (structQuoteSnap.bLastPrice != 0) l1CngMsg.TryAdd(Level1Fields.LastTradePrice, (decimal)structQuoteSnap.fLastPrice); l1CngMsg.TryAdd(Level1Fields.LastTradeVolume, (decimal)structQuoteSnap.nLastSize); l1CngMsg.TryAdd(Level1Fields.OpenInterest, (decimal)structQuoteSnap.nOpenInterest); l1CngMsg.TryAdd(Level1Fields.Volume, (decimal)structQuoteSnap.nCumVolume); l1CngMsg.TryAdd(Level1Fields.VWAP, (decimal)structQuoteSnap.fVwap); l1CngMsg.TryAdd(Level1Fields.ClosePrice, (decimal)structQuoteSnap.fClosePrice); // öåíà çàêðûòèÿ ïðîøëîãî äíÿ. SendOutMessage(l1CngMsg); if (_subscribedSecuritiesToTrade.Cache.Contains(structQuoteSnap.bstrSymbol) && structQuoteSnap.fLastPrice != 0) { var tickMsg= new ExecutionMessage { ExecutionType = ExecutionTypes.Tick, SecurityId = new SecurityId{SecurityCode = structQuoteSnap.bstrSymbol,BoardCode = structQuoteSnap.bstrExch}, //TradeId = structQuoteSnap., TradePrice = (decimal)structQuoteSnap.fLastPrice, TradeVolume = structQuoteSnap.nLastSize, //OriginSide = action.ToSide(), ServerTime = structQuoteSnap.bstrUpdateTime.StrToTime() }; SendOutMessage(tickMsg); } }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var secIdParts = SecId.Text.Split('@'); if (secIdParts.Length != 2) { MessageBox.Show(this, LocalizedStrings.Str3016); return; } var timeFrame = TimeSpan.FromMinutes(5); // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksTestingProcess, TicksParameterGrid, // ticks new EmulationInfo {UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks}), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsTestingProcess, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo {UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757}), Tuple.Create( DepthsCheckBox, DepthsTestingProcess, DepthsParameterGrid, // order book new EmulationInfo {UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths}), Tuple.Create( CandlesCheckBox, CandlesTestingProcess, CandlesParameterGrid, // candles new EmulationInfo {UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles}), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsTestingProcess, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo {UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635}), Tuple.Create( OrderLogCheckBox, OrderLogTestingProcess, OrderLogParameterGrid, // order log new EmulationInfo {UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog}) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var startTime = ((DateTime)From.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)To.Value).ChangeKind(DateTimeKind.Utc); // ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To<int>(); var maxVolume = MaxVolume.Text.To<int>(); var secCode = secIdParts[0]; var board = ExchangeBoard.GetOrCreateBoard(secIdParts[1]); foreach (var set in settings) { if (set.Item1.IsChecked == false) continue; var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { MarketEmulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } }, UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var candleManager = emulationInfo.UseCandleTimeFrame == null ? new CandleManager(new TradeCandleBuilderSourceEx(connector)) : new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; _bufferedChart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; _bufferedChart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(_bufferedChart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>() }; logManager.Sources.Add(strategy); connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values connector.SendInMessage(level1Info); if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // if order book will be generated generateDepths || // of backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } if (emulationInfo.UseTicks) { connector.RegisterTrades(security); } // start strategy before emulation started strategy.Start(); candleManager.Start(series); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve("P&L " + emulationInfo.StrategyName, emulationInfo.CurveColor, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + emulationInfo.StrategyName, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, EquityCurveChartStyles.DashedLine); var posItems = PositionCurve.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; // handle historical time for update ProgressBar connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) return; var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); logManager.Dispose(); _connectors.Clear(); SetIsEnabled(false); this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) MarketDepth.UpdateDepth(quoteMsg); }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors) { // raise NewSecurities and NewPortfolio for full fill strategy properties connector.Connect(); // 1 cent commission for trade connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); } TabControl.Items.Cast<TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
/// <summary> /// To convert first level of market data. /// </summary> /// <param name="message">Level 1.</param> /// <returns>Stream <see cref="Message"/>.</returns> public IEnumerable<Message> ToExecutionLog(Level1ChangeMessage message) { if (message == null) throw new ArgumentNullException(nameof(message)); if (message.IsContainsTick()) yield return message.ToTick(); if (message.IsContainsQuotes()) { var prevBidPrice = _prevBidPrice; var prevBidVolume = _prevBidVolume; var prevAskPrice = _prevAskPrice; var prevAskVolume = _prevAskVolume; _prevBidPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidPrice) ?? _prevBidPrice; _prevBidVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestBidVolume) ?? _prevBidVolume; _prevAskPrice = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskPrice) ?? _prevAskPrice; _prevAskVolume = (decimal?)message.Changes.TryGetValue(Level1Fields.BestAskVolume) ?? _prevAskVolume; if (_prevBidPrice == 0) _prevBidPrice = null; if (_prevAskPrice == 0) _prevAskPrice = null; if (prevBidPrice == _prevBidPrice && prevBidVolume == _prevBidVolume && prevAskPrice == _prevAskPrice && prevAskVolume == _prevAskVolume) yield break; yield return new QuoteChangeMessage { SecurityId = message.SecurityId, LocalTime = message.LocalTime, ServerTime = message.ServerTime, Bids = _prevBidPrice == null ? Enumerable.Empty<QuoteChange>() : new[] { new QuoteChange(Sides.Buy, _prevBidPrice.Value, _prevBidVolume ?? 0) }, Asks = _prevAskPrice == null ? Enumerable.Empty<QuoteChange>() : new[] { new QuoteChange(Sides.Sell, _prevAskPrice.Value, _prevAskVolume ?? 0) }, }; } }
private void SessionOnStiGreeksUpdate(ref structSTIGreeksUpdate structGreeksUpdate) { var message = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = structGreeksUpdate.bstrSymbol, BoardCode = AssociatedBoardCode, }, ServerTime = CurrentTime, }; message.TryAdd(Level1Fields.Delta, (decimal)structGreeksUpdate.fDelta); message.TryAdd(Level1Fields.Gamma, (decimal)structGreeksUpdate.fGamma); message.TryAdd(Level1Fields.Theta, (decimal)structGreeksUpdate.fTheta); message.TryAdd(Level1Fields.Vega, (decimal)structGreeksUpdate.fVega); message.TryAdd(Level1Fields.Rho, (decimal)structGreeksUpdate.fRho); message.TryAdd(Level1Fields.TheorPrice, (decimal)structGreeksUpdate.fTheoPrice); message.TryAdd(Level1Fields.ImpliedVolatility, (decimal)structGreeksUpdate.fImpVol); SendOutMessage(message); }
/// <summary> /// To process the message, containing market data. /// </summary> /// <param name="levelMsg">The message, containing market data.</param> public void ProcessLevel1(Level1ChangeMessage levelMsg) { var priceStep = levelMsg.Changes.TryGetValue(Level1Fields.PriceStep); if (priceStep != null) { PriceStep = (decimal)priceStep; _unrealizedPnL = null; } var stepPrice = levelMsg.Changes.TryGetValue(Level1Fields.StepPrice); if (stepPrice != null) { StepPrice = (decimal)stepPrice; _unrealizedPnL = null; } var tradePrice = levelMsg.Changes.TryGetValue(Level1Fields.LastTradePrice); if (tradePrice != null) { TradePrice = (decimal)tradePrice; _unrealizedPnL = null; } var bidPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestBidPrice); if (bidPrice != null) { BidPrice = (decimal)bidPrice; _unrealizedPnL = null; } var askPrice = levelMsg.Changes.TryGetValue(Level1Fields.BestAskPrice); if (askPrice != null) { AskPrice = (decimal)askPrice; _unrealizedPnL = null; } }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) throw new InvalidOperationException(LocalizedStrings.Str3015); if (Strategy == null) throw new InvalidOperationException("Strategy not selected."); var strategy = (EmulationDiagramStrategy)Strategy; if (strategy.DataPath.IsEmpty() || !Directory.Exists(strategy.DataPath)) throw new InvalidOperationException(LocalizedStrings.Str3014); strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) throw new InvalidOperationException(LocalizedStrings.Str1380); }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = strategy.CandlesTimeFrame; var useCandles = strategy.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(strategy.DataPath) }; var startTime = strategy.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = strategy.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService<ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService<LogManager>().Sources.Add(_connector); var candleManager = new CandleManager(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>(); strategy.SetCandleManager(candleManager); _connector.NewSecurity += s => { var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); //if (!useCandles) // _connector.RegisterTrades(s); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) return; // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; }); break; case EmulationStates.Started: break; } }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void OnSecurityChanged(string smartId, Tuple<decimal, decimal, DateTime> lastTrade, decimal open, decimal high, decimal low, decimal close, decimal volume, QuoteChange bid, QuoteChange ask, decimal openInt, Tuple<decimal, decimal> goBuySell, Tuple<decimal, decimal> goBase, Tuple<decimal, decimal> limits, int tradingStatus, Tuple<decimal, decimal> volatTheorPrice) { var secId = new SecurityId { Native = smartId }; var message = new Level1ChangeMessage { SecurityId = secId, ExtensionInfo = new Dictionary<object, object> { { SmartComExtensionInfoHelper.SecurityOptionsMargin, goBase.Item1 }, { SmartComExtensionInfoHelper.SecurityOptionsSyntheticMargin, goBase.Item2 } }, ServerTime = CurrentTime.Convert(TimeHelper.Moscow), }; message.TryAdd(Level1Fields.LastTradePrice, lastTrade.Item1); message.TryAdd(Level1Fields.LastTradeVolume, lastTrade.Item2); message.Add(Level1Fields.LastTradeTime, lastTrade.Item3.ApplyTimeZone(TimeHelper.Moscow)); var prevQuotes = _bestQuotes.TryGetValue(secId); if (bid.Price != 0) { message.Add(Level1Fields.BestBidPrice, bid.Price); if (prevQuotes != null && prevQuotes.First != null && prevQuotes.First.Item1 == bid.Price) message.Add(Level1Fields.BestBidVolume, prevQuotes.First.Item2); } if (ask.Price != 0) { message.Add(Level1Fields.BestAskPrice, ask.Price); if (prevQuotes != null && prevQuotes.Second != null && prevQuotes.Second.Item1 == ask.Price) message.Add(Level1Fields.BestAskVolume, prevQuotes.Second.Item2); } message.TryAdd(Level1Fields.BidsVolume, bid.Volume); message.TryAdd(Level1Fields.AsksVolume, ask.Volume); message.TryAdd(Level1Fields.OpenPrice, open); message.TryAdd(Level1Fields.LowPrice, low); message.TryAdd(Level1Fields.HighPrice, high); message.TryAdd(Level1Fields.ClosePrice, close); message.TryAdd(Level1Fields.MinPrice, limits.Item1); message.TryAdd(Level1Fields.MaxPrice, limits.Item2); message.TryAdd(Level1Fields.MarginBuy, goBuySell.Item1); message.TryAdd(Level1Fields.MarginSell, goBuySell.Item2); message.TryAdd(Level1Fields.OpenInterest, openInt); message.TryAdd(Level1Fields.ImpliedVolatility, volatTheorPrice.Item1); message.TryAdd(Level1Fields.TheorPrice, volatTheorPrice.Item2); message.TryAdd(Level1Fields.Volume, volume); message.Add(Level1Fields.State, tradingStatus == 0 ? SecurityStates.Trading : SecurityStates.Stoped); SendOutMessage(message); }
private static string Level1ToString(Level1ChangeMessage level) { var sb = new StringBuilder(); foreach (var kvp in level.Changes) sb.Append($"{kvp.Key}:{kvp.Value};"); return $"{level.ServerTime};{level.SecurityId.SecurityCode};{sb}"; }
private void ProcessLevel1Trade(Level1ChangeMessage message, decimal lastTradePrice, decimal lastTradeVolume, List<ExecutionMessage> retVal) { if (message.LocalTime.Date == _lastTradeDate) return; if (lastTradePrice == 0 || lastTradeVolume == 0) return; var exec = new ExecutionMessage { LocalTime = message.LocalTime, ServerTime = message.ServerTime, SecurityId = message.SecurityId, ExecutionType = ExecutionTypes.Tick, TradePrice = lastTradePrice, Volume = lastTradeVolume, }; retVal.AddRange(ProcessExecution(exec)); }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) throw new InvalidOperationException(LocalizedStrings.Str3015); if (Strategy == null) throw new InvalidOperationException("Strategy not selected."); var strategy = (EmulationDiagramStrategy)Strategy; var settings = strategy.EmulationSettings; if (settings.MarketDataSettings == null) throw new InvalidOperationException(LocalizedStrings.Str3014); new SetDefaultEmulationSettingsCommand(settings).Process(this); strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) throw new InvalidOperationException(LocalizedStrings.Str1380); }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = settings.CandlesTimeFrame; var useCandles = settings.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StudioStorageRegistry { MarketDataSettings = settings.MarketDataSettings }; var startTime = settings.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = settings.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService<ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = settings.MatchOnTouch, IsSupportAtomicReRegister = settings.IsSupportAtomicReRegister, Latency = settings.EmulatoinLatency, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, StorageFormat = settings.StorageFormat, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)_connector).LogLevel = settings.DebugLog ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService<LogManager>().Sources.Add(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; strategy.LogLevel = settings.DebugLog ? LogLevels.Debug : LogLevels.Info; // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To<TimeSpan>(); strategy.SetCandleManager(new CandleManager(_connector)); _connector.NewSecurity += s => { //TODO send real level1 message var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); if (settings.UseMarketDepths) { _connector.RegisterMarketDepth(security); if ( // if order book will be generated settings.GenerateDepths || // of backtesting will be on candles useCandles ) { // if no have order book historical data, but strategy is required, // use generator based on last prices _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(s)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = settings.MaxDepths, MaxBidsDepth = settings.MaxDepths, UseTradeVolume = true, MaxVolume = settings.MaxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) return; // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; }); break; case EmulationStates.Started: break; } }; _connector.Disconnected += () => { this.GuiAsync(() => _connector.Dispose()); }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void ProcessLevel1Depth(Level1ChangeMessage message, decimal bestBidPrice, decimal bestBidVolume, decimal bestAskPrice, decimal bestAskVolume, List<ExecutionMessage> retVal) { if (message.LocalTime.Date == _lastDepthDate) return; QuoteChange ask = null; QuoteChange bid = null; if (bestAskPrice != 0 && bestAskVolume != 0) ask = new QuoteChange(Sides.Sell, bestAskPrice, bestAskVolume); if (bestBidPrice != 0 && bestBidVolume != 0) bid = new QuoteChange(Sides.Buy, bestBidPrice, bestBidVolume); if (ask == null && bid == null) return; retVal.AddRange(ProcessQuoteChange(message.LocalTime, message.ServerTime, bid != null ? new[] { bid } : ArrayHelper.Empty<QuoteChange>(), ask != null ? new[] { ask } : ArrayHelper.Empty<QuoteChange>())); }
private void TraderOnValuesChanged(Security security, IEnumerable<KeyValuePair<Level1Fields, object>> changes, DateTimeOffset serverTime, DateTime localTime) { var wnd = _level1Windows.TryGetValue(security); if (wnd == null) return; var msg = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = serverTime, LocalTime = localTime }; msg.Changes.AddRange(changes); wnd.Level1Grid.Messages.Add(msg); }
private void StartBtnClick(object sender, RoutedEventArgs e) { // if process was already started, will stop it now if (_connector != null && _connector.State != EmulationStates.Stopped) { _strategy.Stop(); _connector.Disconnect(); _logManager.Sources.Clear(); _connector = null; return; } // create test security var security = new Security { Id = "AAPL@NASDAQ", Code = "AAPL", Name = "AAPL Inc", Board = ExchangeBoard.Nasdaq, }; var startTime = new DateTime(2009, 6, 1); var stopTime = new DateTime(2009, 9, 1); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var timeFrame = TimeSpan.FromMinutes(5); // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { HistoryMessageAdapter = { // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; _logManager.Sources.Add(_connector); var candleManager = new CandleManager(_connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based on 80 5-min и 10 5-min _strategy = new SmaStrategy(series, new SimpleMovingAverage { Length = 80 }, new SimpleMovingAverage { Length = 10 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = _connector, }; _connector.NewSecurities += securities => { if (securities.All(s => s != security)) return; // fill level1 values _connector.SendInMessage(level1Info); _connector.RegisterTrades(new RandomWalkTradeGenerator(_connector.GetSecurityId(security))); _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(security)) { GenerateDepthOnEachTrade = false }); // start strategy before emulation started _strategy.Start(); candleManager.Start(series); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; // fill parameters panel ParameterGrid.Parameters.Clear(); ParameterGrid.Parameters.AddRange(_strategy.StatisticManager.Parameters); _strategy.PnLChanged += () => { var data = new EquityData { Time = _strategy.CurrentTime, Value = _strategy.PnL, }; this.GuiAsync(() => _curveItems.Add(data)); }; _logManager.Sources.Add(_strategy); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To<TimeSpan>(); var nextTime = startTime + progressStep; TestingProcess.Maximum = 100; TestingProcess.Value = 0; // handle historical time for update ProgressBar _connector.MarketTimeChanged += diff => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) return; var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To<TimeSpan>(); this.GuiAsync(() => TestingProcess.Value = steps); }; _connector.StateChanged += () => { if (_connector.State == EmulationStates.Stopped) { this.GuiAsync(() => { Report.IsEnabled = true; if (_connector.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else MessageBox.Show(this, LocalizedStrings.cancelled); }); } }; _curveItems.Clear(); Report.IsEnabled = false; _startEmulationTime = DateTime.Now; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (_connectors.Count > 0) { foreach (var connector in _connectors) { connector.Start(); } return; } if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var id = SecId.Text.ToSecurityId(); //if (secIdParts.Length != 2) //{ // MessageBox.Show(this, LocalizedStrings.Str3016); // return; //} var timeFrame = TimeSpan.FromMinutes(TimeFrame.SelectedIndex == 0 ? 1 : 5); var secCode = id.SecurityCode; var board = _exchangeInfoProvider.GetOrCreateBoard(id.BoardCode); // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; if (FinamCandlesCheckBox.IsChecked == true) { _finamHistorySource.Refresh(new FinamSecurityStorage(security), security, s => {}, () => false); } // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksProgress, TicksParameterGrid, // ticks new EmulationInfo { UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks }, TicksChart, TicksEquity, TicksPosition), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsProgress, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo { UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757 }, TicksAndDepthsChart, TicksAndDepthsEquity, TicksAndDepthsPosition), Tuple.Create( DepthsCheckBox, DepthsProgress, DepthsParameterGrid, // order book new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths }, DepthsChart, DepthsEquity, DepthsPosition), Tuple.Create( CandlesCheckBox, CandlesProgress, CandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles }, CandlesChart, CandlesEquity, CandlesPosition), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsProgress, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635 }, CandlesAndDepthsChart, CandlesAndDepthsEquity, CandlesAndDepthsPosition), Tuple.Create( OrderLogCheckBox, OrderLogProgress, OrderLogParameterGrid, // order log new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog }, OrderLogChart, OrderLogEquity, OrderLogPosition), Tuple.Create( Level1CheckBox, Level1Progress, Level1ParameterGrid, // order log new EmulationInfo { UseLevel1 = true, CurveColor = Colors.Aquamarine, StrategyName = LocalizedStrings.Level1 }, Level1Chart, Level1Equity, Level1Position), Tuple.Create( FinamCandlesCheckBox, FinamCandlesProgress, FinamCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, HistorySource = d => _finamHistorySource.GetCandles(security, timeFrame, d.Date, d.Date), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.FinamCandles }, FinamCandlesChart, FinamCandlesEquity, FinamCandlesPosition), Tuple.Create( YahooCandlesCheckBox, YahooCandlesProgress, YahooCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, HistorySource = d => new YahooHistorySource(_exchangeInfoProvider).GetCandles(security, timeFrame, d.Date, d.Date), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.YahooCandles }, YahooCandlesChart, YahooCandlesEquity, YahooCandlesPosition), }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var startTime = ((DateTime)From.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)To.Value).ChangeKind(DateTimeKind.Utc); // (ru only) ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1).ApplyTimeZone(TimeHelper.Moscow).UtcDateTime; } // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secId = security.ToSecurityId(); SetIsEnabled(false, false, false); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var title = (string)set.Item1.Content; InitChart(set.Item5, set.Item6, set.Item7); var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; var level1Info = new Level1ChangeMessage { SecurityId = secId, ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, //UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, OrderLogMarketDepthBuilders = { { secId, LocalizedStrings.ActiveLanguage == Languages.Russian ? (IOrderLogMarketDepthBuilder) new PlazaOrderLogMarketDepthBuilder(secId) : new ItchOrderLogMarketDepthBuilder(secId) } } }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var candleManager = new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame) { BuildCandlesMode = emulationInfo.UseCandleTimeFrame == null ? BuildCandlesModes.Build : BuildCandlesModes.Load }; _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; var chart = set.Item5; chart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; chart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(chart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, candleManager, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; logManager.Sources.Add(strategy); connector.NewSecurity += s => { if (s != security) { return; } // fill level1 values connector.HistoryMessageAdapter.SendOutMessage(level1Info); if (emulationInfo.HistorySource != null) { if (emulationInfo.UseCandleTimeFrame != null) { connector.RegisterHistorySource(security, MarketDataTypes.CandleTimeFrame, emulationInfo.UseCandleTimeFrame.Value, emulationInfo.HistorySource); } if (emulationInfo.UseTicks) { connector.RegisterHistorySource(security, MarketDataTypes.Trades, null, emulationInfo.HistorySource); } if (emulationInfo.UseLevel1) { connector.RegisterHistorySource(security, MarketDataTypes.Level1, null, emulationInfo.HistorySource); } if (emulationInfo.UseMarketDepth) { connector.RegisterHistorySource(security, MarketDataTypes.MarketDepth, null, emulationInfo.HistorySource); } } else { if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // if order book will be generated generateDepths || // of backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } if (emulationInfo.UseTicks) { connector.RegisterTrades(security); } if (emulationInfo.UseLevel1) { connector.RegisterSecurity(security); } } // start strategy before emulation started strategy.Start(); candleManager.Start(series); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var equity = set.Item6; var pnlCurve = equity.CreateCurve(LocalizedStrings.PnL + " " + emulationInfo.StrategyName, emulationInfo.CurveColor, LineChartStyles.Area); var unrealizedPnLCurve = equity.CreateCurve(LocalizedStrings.PnLUnreal + " " + emulationInfo.StrategyName, Colors.Black); var commissionCurve = equity.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, LineChartStyles.DashedLine); var posItems = set.Item7.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL ?? 0 }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; // handle historical time for update ProgressBar connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) { return; } var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); SetIsChartEnabled(chart, false); if (_connectors.All(c => c.State == EmulationStates.Stopped)) { logManager.Dispose(); _connectors.Clear(); SetIsEnabled(true, false, false); } this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime), title); } else { MessageBox.Show(this, LocalizedStrings.cancelled, title); } }); } else if (connector.State == EmulationStates.Started) { if (_connectors.All(c => c.State == EmulationStates.Started)) { SetIsEnabled(false, true, true); } SetIsChartEnabled(chart, true); } else if (connector.State == EmulationStates.Suspended) { if (_connectors.All(c => c.State == EmulationStates.Suspended)) { SetIsEnabled(true, false, true); } } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors) { // raise NewSecurities and NewPortfolio for full fill strategy properties connector.Connect(); // 1 cent commission for trade connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
/// <summary> /// Преобразовать первый уровень маркет-данных. /// </summary> /// <param name="message">Первый уровень маркет-данных.</param> /// <returns>Поток <see cref="ExecutionMessage"/>.</returns> public IEnumerable<ExecutionMessage> ToExecutionLog(Level1ChangeMessage message) { if (message == null) throw new ArgumentNullException("message"); var retVal = new List<ExecutionMessage>(); var bestBidPrice = 0m; var bestBidVolume = 0m; var bestAskPrice = 0m; var bestAskVolume = 0m; var lastTradePrice = 0m; var lastTradeVolume = 0m; foreach (var change in message.Changes) { switch (change.Key) { case Level1Fields.LastTradePrice: lastTradePrice = (decimal)change.Value; break; case Level1Fields.LastTradeVolume: lastTradeVolume = (decimal)change.Value; break; case Level1Fields.BestBidPrice: bestBidPrice = (decimal)change.Value; break; case Level1Fields.BestBidVolume: bestBidVolume = (decimal)change.Value; break; case Level1Fields.BestAskPrice: bestAskPrice = (decimal)change.Value; break; case Level1Fields.BestAskVolume: bestAskVolume = (decimal)change.Value; break; } } ProcessLevel1Depth(message, bestBidPrice, bestBidVolume, bestAskPrice, bestAskVolume, retVal); ProcessLevel1Trade(message, lastTradePrice, lastTradeVolume, retVal); return retVal; }
private void ProcessSubscriptions() { if (_subscribedLevel1.Count > 0) { var tickerReply = _client.GetTickers(_subscribedLevel1.Cache.Select(id => id.SecurityCode.ToBtceCode())); foreach (var ticker in tickerReply.Items.Values) { var l1Msg = new Level1ChangeMessage { SecurityId = new SecurityId { SecurityCode = ticker.Instrument.ToStockSharpCode(), BoardCode = _boardCode, }, ServerTime = ticker.Timestamp.ApplyTimeZone(TimeHelper.Moscow) } .TryAdd(Level1Fields.Volume, (decimal)ticker.Volume) .TryAdd(Level1Fields.HighPrice, (decimal)ticker.HighPrice) .TryAdd(Level1Fields.LowPrice, (decimal)ticker.LowPrice) .TryAdd(Level1Fields.LastTradePrice, (decimal)ticker.LastPrice) // BTCE транслирует потенциальные цену для покупки и продажи .TryAdd(Level1Fields.BestBidPrice, (decimal)ticker.Ask) .TryAdd(Level1Fields.BestAskPrice, (decimal)ticker.Bid) .TryAdd(Level1Fields.AveragePrice, (decimal)ticker.AveragePrice); if (l1Msg.Changes.Count > 0) SendOutMessage(l1Msg); } } if (_subscribedDepths.Count > 0) { foreach (var group in _subscribedDepths.CachedPairs.GroupBy(p => p.Value)) { var depthReply = _client.GetDepths(group.Key, group.Select(p => p.Key).Select(id => id.SecurityCode.ToBtceCode())); foreach (var pair in depthReply.Items) { SendOutMessage(new QuoteChangeMessage { SecurityId = new SecurityId { SecurityCode = pair.Key.ToStockSharpCode(), BoardCode = _boardCode, }, Bids = pair.Value.Bids.Select(vp => vp.ToStockSharp(Sides.Buy)).ToArray(), Asks = pair.Value.Asks.Select(vp => vp.ToStockSharp(Sides.Sell)).ToArray(), ServerTime = CurrentTime.Convert(TimeHelper.Moscow), }); } } } if (_subscribedTicks.Count > 0) { var tradeReply = _client.GetTrades(_tickCount, _subscribedTicks.Cache.Select(id => id.SecurityCode.ToBtceCode())); // меняем на глубину 50 _tickCount = 50; foreach (var pair in tradeReply.Items) { foreach (var trade in pair.Value.OrderBy(t => t.Id)) { if (_lastTickId >= trade.Id) continue; _lastTickId = trade.Id; SendOutMessage(new ExecutionMessage { SecurityId = new SecurityId { SecurityCode = pair.Key.ToStockSharpCode(), BoardCode = _boardCode, }, ExecutionType = ExecutionTypes.Tick, TradePrice = (decimal)trade.Price, Volume = (decimal)trade.Volume, TradeId = trade.Id, ServerTime = trade.Timestamp.ApplyTimeZone(TimeHelper.Moscow), OriginSide = trade.Side.ToStockSharp() }); } } } }
private void OnProcessLevel1(string[] data) { var f = Wrapper.FieldsLevel1; foreach (var row in data) { var cols = row.ToColumns(); var paperNo = f.PaperNo.GetValue(cols); var secId = new SecurityId { Native = paperNo }; var l1Msg = new Level1ChangeMessage { SecurityId = secId, ServerTime = (f.LastUpdateDate.GetValue(cols).Date + f.LastUpdateTime.GetValue(cols).TimeOfDay).ApplyTimeZone(TimeHelper.Moscow) }; l1Msg.Add(Level1Fields.State, f.TradingStatus.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MarginBuy, f.GoBuy.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MarginSell, f.GoSell.GetValue(cols)); l1Msg.TryAdd(Level1Fields.OpenInterest, (decimal)f.OpenPosQty.GetValue(cols)); var minPrice = f.MinDeal.GetValue(cols); var maxPrice = f.MaxDeal.GetValue(cols); l1Msg.TryAdd(Level1Fields.OpenPrice, f.OpenPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.ClosePrice, f.ClosePrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.HighPrice, maxPrice); l1Msg.TryAdd(Level1Fields.LowPrice, minPrice); l1Msg.TryAdd(Level1Fields.BestBidPrice, f.Buy.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestBidVolume, (decimal)f.BuyQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestAskPrice, f.Sell.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BestAskVolume, (decimal)f.SellQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.MinPrice, minPrice); l1Msg.TryAdd(Level1Fields.MaxPrice, maxPrice); l1Msg.TryAdd(Level1Fields.Multiplier, (decimal)f.LotSize.GetValue(cols)); l1Msg.TryAdd(Level1Fields.ImpliedVolatility, f.Volatility.GetValue(cols)); l1Msg.TryAdd(Level1Fields.TheorPrice, f.TheorPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.LastTradePrice, f.LastPrice.GetValue(cols)); l1Msg.TryAdd(Level1Fields.LastTradeVolume, (decimal)f.LastQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.PriceStep, f.PriceStep.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BidsVolume, (decimal)f.BuySQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.BidsCount, f.BuyCount.GetValue(cols)); l1Msg.TryAdd(Level1Fields.AsksVolume, (decimal)f.SellSQty.GetValue(cols)); l1Msg.TryAdd(Level1Fields.AsksCount, f.SellCount.GetValue(cols)); SendOutMessage(l1Msg); } }
private void OnSecurityChanged(string smartId, Tuple <decimal?, decimal?, DateTime> lastTrade, decimal?open, decimal?high, decimal?low, decimal?close, decimal?volume, QuoteChange bid, QuoteChange ask, decimal?openInt, Tuple <decimal?, decimal?> goBuySell, Tuple <decimal?, decimal?> goBase, Tuple <decimal?, decimal?> limits, int tradingStatus, Tuple <decimal?, decimal?> volatTheorPrice) { var secId = new SecurityId { Native = smartId }; var message = new Level1ChangeMessage { SecurityId = secId, ExtensionInfo = new Dictionary <object, object> { { SmartComExtensionInfoHelper.SecurityOptionsMargin, goBase.Item1 }, { SmartComExtensionInfoHelper.SecurityOptionsSyntheticMargin, goBase.Item2 } }, ServerTime = CurrentTime.Convert(TimeHelper.Moscow), }; message.TryAdd(Level1Fields.LastTradePrice, lastTrade.Item1); message.TryAdd(Level1Fields.LastTradeVolume, lastTrade.Item2); message.Add(Level1Fields.LastTradeTime, lastTrade.Item3.ApplyTimeZone(TimeHelper.Moscow)); var prevQuotes = _bestQuotes.TryGetValue(secId); if (bid.Price != 0) { message.Add(Level1Fields.BestBidPrice, bid.Price); if (prevQuotes != null && prevQuotes.First != null && prevQuotes.First.Item1 == bid.Price) { message.Add(Level1Fields.BestBidVolume, prevQuotes.First.Item2); } } if (ask.Price != 0) { message.Add(Level1Fields.BestAskPrice, ask.Price); if (prevQuotes != null && prevQuotes.Second != null && prevQuotes.Second.Item1 == ask.Price) { message.Add(Level1Fields.BestAskVolume, prevQuotes.Second.Item2); } } message.TryAdd(Level1Fields.BidsVolume, bid.Volume); message.TryAdd(Level1Fields.AsksVolume, ask.Volume); message.TryAdd(Level1Fields.OpenPrice, open); message.TryAdd(Level1Fields.LowPrice, low); message.TryAdd(Level1Fields.HighPrice, high); message.TryAdd(Level1Fields.ClosePrice, close); message.TryAdd(Level1Fields.MinPrice, limits.Item1); message.TryAdd(Level1Fields.MaxPrice, limits.Item2); message.TryAdd(Level1Fields.MarginBuy, goBuySell.Item1); message.TryAdd(Level1Fields.MarginSell, goBuySell.Item2); message.TryAdd(Level1Fields.OpenInterest, openInt); message.TryAdd(Level1Fields.ImpliedVolatility, volatTheorPrice.Item1); message.TryAdd(Level1Fields.TheorPrice, volatTheorPrice.Item2); message.TryAdd(Level1Fields.Volume, volume); message.Add(Level1Fields.State, tradingStatus == 0 ? SecurityStates.Trading : SecurityStates.Stoped); SendOutMessage(message); }
private void OnSessionMarketDataChanged(OrderBookEvent orderBookEvent) { var time = TimeHelper.GregorianStart.AddMilliseconds(orderBookEvent.Timestamp).ApplyTimeZone(TimeZoneInfo.Utc); var secId = new SecurityId { Native = orderBookEvent.InstrumentId }; var l1Msg = new Level1ChangeMessage { ServerTime = time, SecurityId = secId, }; if (orderBookEvent.HasMarketClosePrice) l1Msg.Add(Level1Fields.ClosePrice, orderBookEvent.MktClosePrice); if (orderBookEvent.HasDailyHighestTradedPrice) l1Msg.Add(Level1Fields.HighPrice, orderBookEvent.DailyHighestTradedPrice); if (orderBookEvent.HasDailyLowestTradedPrice) l1Msg.Add(Level1Fields.LowPrice, orderBookEvent.DailyLowestTradedPrice); if (orderBookEvent.HasLastTradedPrice) { l1Msg.Add(Level1Fields.LastTradePrice, orderBookEvent.LastTradedPrice); } SendOutMessage(l1Msg); SendOutMessage(new QuoteChangeMessage { SecurityId = secId, Bids = orderBookEvent.BidPrices.Select(p => new QuoteChange(Sides.Buy, p.Price, p.Quantity)), Asks = orderBookEvent.AskPrices.Select(p => new QuoteChange(Sides.Sell, p.Price, p.Quantity)), ServerTime = time }); }