/// <summary> /// The retrieve market traded volume. /// </summary> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <param name="tradingHours"> /// The trading hours. /// </param> /// <param name="activeHistory"> /// The active history. /// </param> /// <returns> /// The <see cref="long?"/>. /// </returns> private long?RetrieveMarketTradedVolume(Order mostRecentTrade, ITradingHours tradingHours, Stack <Order> activeHistory) { var closeTime = this.source == DataSource.AnyIntraday ? UniverseDateTime : tradingHours.ClosingInUtcForDay(UniverseDateTime); var marketDataRequest = new MarketDataRequest( mostRecentTrade.Market?.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), closeTime, RuleCtx.Id(), this.source); var hadMissingData = false; long?marketTradedVolume = null; switch (this.source) { case DataSource.AnyInterday: var securityResultInterday = UniverseEquityInterdayCache.GetMarkets(marketDataRequest); hadMissingData = securityResultInterday.HadMissingData; if (!hadMissingData) { marketTradedVolume = this.InterdayMarketTradedVolume(securityResultInterday); } break; case DataSource.AnyIntraday: var securityResultIntraday = UniverseEquityIntradayCache.GetMarkets(marketDataRequest); hadMissingData = securityResultIntraday.HadMissingData; if (!hadMissingData) { marketTradedVolume = this.IntradayMarketTradedVolume(securityResultIntraday); } break; } if (hadMissingData && RunMode == RuleRunMode.ForceRun) { this.UpdatePassedFilterWithOrders(activeHistory); return(null); } if (hadMissingData && RunMode == RuleRunMode.ValidationRun) { this.logger.LogInformation($"market traded volume was not calculable for {mostRecentTrade.Instrument.Identifiers} due to missing data"); this.hadMissingData = true; return(null); } return(marketTradedVolume); }
/// <summary> /// The check daily volume breach. /// </summary> /// <param name="opposingPosition"> /// The opposing position. /// </param> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <returns> /// The <see cref="RuleBreachDescription"/>. /// </returns> private RuleBreachDescription CheckDailyVolumeBreach( ITradePosition opposingPosition, Order mostRecentTrade) { var tradingHoursManager = this.tradingHoursService.GetTradingHoursForMic(mostRecentTrade.Market.MarketIdentifierCode); if (!tradingHoursManager.IsValid) { this.logger.LogInformation($"unable to fetch market data for ({mostRecentTrade.Market.MarketIdentifierCode}) for the most recent trade {mostRecentTrade?.Instrument?.Identifiers} the market data did not contain the security indicated as trading in that market"); this.hadMissingData = true; return(RuleBreachDescription.False()); } var marketRequest = new MarketDataRequest( mostRecentTrade.Market.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, tradingHoursManager.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHoursManager.ClosingInUtcForDay(UniverseDateTime), this.ruleContext?.Id(), DataSource.AnyInterday); var marketResult = UniverseEquityInterdayCache.Get(marketRequest); if (marketResult.HadMissingData) { this.logger.LogInformation($"unable to fetch market data for ({mostRecentTrade.Market.MarketIdentifierCode}) for the most recent trade {mostRecentTrade?.Instrument?.Identifiers} the market data did not contain the security indicated as trading in that market"); this.hadMissingData = true; return(RuleBreachDescription.False()); } var marketSecurityData = marketResult.Response; if (marketSecurityData?.DailySummaryTimeBar?.DailyVolume.Traded <= 0 || opposingPosition.TotalVolumeOrderedOrFilled() <= 0) { this.logger.LogInformation($"unable to evaluate for {mostRecentTrade?.Instrument?.Identifiers} either the market daily volume data was not available or the opposing position had a bad total volume value (daily volume){marketSecurityData?.DailySummaryTimeBar?.DailyVolume.Traded} - (opposing position){opposingPosition.TotalVolumeOrderedOrFilled()}"); this.hadMissingData = true; return(RuleBreachDescription.False()); } var percentageDailyVolume = (decimal)opposingPosition.TotalVolumeOrderedOrFilled() / (decimal)marketSecurityData?.DailySummaryTimeBar?.DailyVolume.Traded; if (percentageDailyVolume >= this.equitiesParameters.PercentageOfMarketDailyVolume) { return(new RuleBreachDescription { RuleBreached = true, Description = $" Percentage of market daily volume traded within a {this.equitiesParameters.Windows.BackwardWindowSize.TotalSeconds} second window exceeded the layering window threshold of {this.equitiesParameters.PercentageOfMarketDailyVolume * 100}%." }); } return(RuleBreachDescription.False()); }
/// <summary> /// The window volume check. /// </summary> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <param name="tradedVolume"> /// The traded volume. /// </param> /// <returns> /// The <see cref="BreachDetails"/>. /// </returns> private HighVolumeRuleBreach.BreachDetails WindowVolumeCheck(Order mostRecentTrade, decimal tradedVolume) { var tradingHours = this.TradingHoursService.GetTradingHoursForMic(mostRecentTrade.Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.Logger.LogError($"Request for trading hours was invalid. MIC - {mostRecentTrade.Market?.MarketIdentifierCode}"); } var tradingDates = this.TradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), mostRecentTrade.Market?.MarketIdentifierCode); var marketRequest = new MarketDataRequest( mostRecentTrade.Market?.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), this.RuleCtx?.Id(), DataSource.AnyIntraday); var marketResult = this.UniverseEquityIntradayCache.GetMarketsForRange(marketRequest, tradingDates, RunMode); if (marketResult.HadMissingData) { this.Logger.LogTrace($"Unable to fetch market data frames for {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.HadMissingData = true; return(HighVolumeRuleBreach.BreachDetails.None()); } var securityDataTicks = marketResult.Response; var windowVolume = securityDataTicks.Sum(sdt => sdt.SpreadTimeBar.Volume.Traded); var threshold = (long)Math.Ceiling(this.EquitiesParameters.HighVolumePercentageWindow.GetValueOrDefault(0) * windowVolume); var breachPercentage = windowVolume != 0 && tradedVolume != 0 ? (decimal)tradedVolume / (decimal)windowVolume : 0; if (threshold <= 0) { this.HadMissingData = true; this.Logger.LogInformation($"Window volume threshold of {threshold} was recorded."); return(HighVolumeRuleBreach.BreachDetails.None()); } if (tradedVolume >= threshold) { return(new HighVolumeRuleBreach.BreachDetails(true, breachPercentage, threshold, mostRecentTrade.Market)); } return(HighVolumeRuleBreach.BreachDetails.None()); }
/// <summary> /// The check window volume traded. /// </summary> /// <param name="securities"> /// The securities. /// </param> /// <returns> /// The <see cref="VolumeBreach"/>. /// </returns> private VolumeBreach CheckWindowVolumeTraded(Stack <Order> securities) { if (!securities.Any()) { return(new VolumeBreach()); } var tradingHours = this.tradingHoursService.GetTradingHoursForMic(securities.Peek().Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.logger.LogError($"Request for trading hours was invalid. MIC - {securities.Peek().Market?.MarketIdentifierCode}"); return(new VolumeBreach()); } var tradingDates = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), securities.Peek().Market?.MarketIdentifierCode); var marketDataRequest = new MarketDataRequest( securities.Peek().Market.MarketIdentifierCode, securities.Peek().Instrument.Cfi, securities.Peek().Instrument.Identifiers, UniverseDateTime.Subtract(this.TradeBackwardWindowSize), // implicitly correct (market closure event trigger) UniverseDateTime, this.ruleContext?.Id(), DataSource.AnyIntraday); // marking the close should not have windows exceeding a few hours var marketResult = UniverseEquityIntradayCache.GetMarketsForRange(marketDataRequest, tradingDates, RunMode); if (marketResult.HadMissingData) { this.hadMissingData = true; return(new VolumeBreach()); } var securityUpdates = marketResult.Response; var securityVolume = securityUpdates.Sum(su => su.SpreadTimeBar.Volume.Traded); var thresholdVolumeTraded = securityVolume * this.equitiesParameters.PercentageThresholdWindowVolume; if (thresholdVolumeTraded == null) { this.hadMissingData = true; return(new VolumeBreach()); } var result = this.CalculateVolumeBreaches( securities, thresholdVolumeTraded.GetValueOrDefault(0), securityVolume); return(result); }
/// <summary> /// The daily volume check. /// </summary> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <param name="tradedVolume"> /// The traded volume. /// </param> /// <returns> /// The <see cref="BreachDetails"/>. /// </returns> private HighVolumeRuleBreach.BreachDetails DailyVolumeCheck(Order mostRecentTrade, decimal tradedVolume) { if (mostRecentTrade == null) { return(HighVolumeRuleBreach.BreachDetails.None()); } var tradingHours = this.TradingHoursService.GetTradingHoursForMic(mostRecentTrade.Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.Logger.LogError($"Request for trading hours was invalid. MIC - {mostRecentTrade.Market?.MarketIdentifierCode}"); } var marketDataRequest = new MarketDataRequest( mostRecentTrade.Market?.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), this.RuleCtx?.Id(), DataSource.AnyInterday); var securityResult = UniverseEquityInterdayCache.Get(marketDataRequest); if (securityResult.HadMissingData) { this.HadMissingData = true; this.Logger.LogWarning($"Missing data for {marketDataRequest}."); return(HighVolumeRuleBreach.BreachDetails.None()); } var security = securityResult.Response; var threshold = (long)Math.Ceiling( this.EquitiesParameters.HighVolumePercentageDaily.GetValueOrDefault(0) * security.DailySummaryTimeBar.DailyVolume.Traded); if (threshold <= 0) { this.HadMissingData = true; this.Logger.LogInformation($"Daily volume threshold of {threshold} was recorded."); return(HighVolumeRuleBreach.BreachDetails.None()); } var breachPercentage = security.DailySummaryTimeBar.DailyVolume.Traded != 0 && tradedVolume != 0 ? (decimal)tradedVolume / (decimal)security.DailySummaryTimeBar.DailyVolume.Traded : 0; if (tradedVolume >= threshold) { return(new HighVolumeRuleBreach.BreachDetails(true, breachPercentage, threshold, mostRecentTrade.Market)); } return(HighVolumeRuleBreach.BreachDetails.None()); }
/// <summary> /// The check daily volume traded. /// </summary> /// <param name="securities"> /// The securities. /// </param> /// <returns> /// The <see cref="VolumeBreach"/>. /// </returns> private VolumeBreach CheckDailyVolumeTraded( Stack <Order> securities) { if (!securities.Any()) { return(new VolumeBreach()); } var marketDataRequest = new MarketDataRequest( securities.Peek().Market.MarketIdentifierCode, securities.Peek().Instrument.Cfi, securities.Peek().Instrument.Identifiers, UniverseDateTime.Subtract(this.TradeBackwardWindowSize), // implicitly correct (market closure event trigger) UniverseDateTime, this.ruleContext?.Id(), DataSource.AnyInterday); var dataResponse = UniverseEquityInterdayCache.Get(marketDataRequest); if (dataResponse.HadMissingData) { this.hadMissingData = true; this.logger.LogInformation($"had missing data for {securities.Peek().Instrument.Identifiers} on {UniverseDateTime}"); return(new VolumeBreach()); } var tradedSecurity = dataResponse.Response; var thresholdVolumeTraded = tradedSecurity.DailySummaryTimeBar.DailyVolume.Traded * this.equitiesParameters.PercentageThresholdDailyVolume; if (thresholdVolumeTraded == null) { this.hadMissingData = true; return(new VolumeBreach()); } var result = this.CalculateVolumeBreaches( securities, thresholdVolumeTraded.GetValueOrDefault(0), tradedSecurity.DailySummaryTimeBar.DailyVolume.Traded); return(result); }
/// <summary> /// The exceeds trading volume in window threshold. /// </summary> /// <param name="orders"> /// The orders. /// </param> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> private bool ExceedsTradingVolumeInWindowThreshold(List <Order> orders, Order mostRecentTrade) { if (this.rampingParameters?.ThresholdVolumePercentageWindow == null || this.rampingParameters.ThresholdVolumePercentageWindow <= 0 || orders == null || !orders.Any()) { return(true); } var tradingHours = this.tradingHoursService.GetTradingHoursForMic(mostRecentTrade.Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.logger.LogError($"Request for trading hours was invalid. MIC - {mostRecentTrade.Market?.MarketIdentifierCode}"); } var tradingDates = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), mostRecentTrade.Market?.MarketIdentifierCode); var marketRequest = new MarketDataRequest( mostRecentTrade.Market?.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), this.ruleContext?.Id(), DataSource.AnyIntraday); var marketResult = UniverseEquityIntradayCache.GetMarketsForRange(marketRequest, tradingDates, RunMode); if (marketResult.HadMissingData) { this.logger.LogTrace($"Unable to fetch market data frames for {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(false); } var securityDataTicks = marketResult.Response; var windowVolume = securityDataTicks.Sum(sdt => sdt.SpreadTimeBar.Volume.Traded); var threshold = (long)Math.Ceiling(this.rampingParameters.ThresholdVolumePercentageWindow.GetValueOrDefault(0) * windowVolume); if (threshold <= 0) { this.hadMissingData = true; this.logger.LogInformation($"Daily volume threshold of {threshold} was recorded."); return(false); } var tradedVolume = orders.Sum(_ => _.OrderFilledVolume.GetValueOrDefault(0)); if (tradedVolume >= threshold) { return(true); } return(false); }
/// <summary> /// The run order filled event. /// </summary> /// <param name="history"> /// The history. /// </param> public override void RunOrderFilledEvent(ITradingHistoryStack history) { var tradeWindow = history?.ActiveTradeHistory(); if (tradeWindow == null || !tradeWindow.Any()) { return; } if (!this.ExceedsTradingFrequencyThreshold(tradeWindow)) { // LOG THEN EXIT this.logger.LogInformation($"Trading Frequency of {this.rampingParameters.ThresholdOrdersExecutedInWindow} was not exceeded. Returning."); return; } if (!this.ExceedsTradingVolumeInWindowThreshold(tradeWindow.ToList(), tradeWindow.Any() ? tradeWindow.Peek() : null)) { // LOG THEN EXIT this.logger.LogInformation($"Trading Volume of {this.rampingParameters.ThresholdVolumePercentageWindow} was not exceeded. Returning."); return; } var lastTrade = tradeWindow.Any() ? tradeWindow.Peek() : null; var tradingHours = this.tradingHoursService.GetTradingHoursForMic(lastTrade.Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.logger.LogError($"Request for trading hours was invalid. MIC - {lastTrade.Market?.MarketIdentifierCode}"); return; } var tradingDates = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), lastTrade.Market?.MarketIdentifierCode); var subtractDate = this.TradeBackwardWindowSize > TimeSpan.FromDays(30) ? this.TradeBackwardWindowSize : TimeSpan.FromDays(30); var marketDataRequest = new MarketDataRequest( lastTrade.Market?.MarketIdentifierCode, lastTrade.Instrument.Cfi, lastTrade.Instrument.Identifiers, tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(subtractDate)), tradingHours.ClosingInUtcForDay(UniverseDateTime), this.ruleContext?.Id(), DataSource.AnyIntraday); var marketData = UniverseEquityIntradayCache.GetMarketsForRange(marketDataRequest, tradingDates, RunMode); if (marketData.HadMissingData) { this.hadMissingData = true; this.logger.LogWarning($"Missing data for {marketDataRequest}."); return; } var windowStackCopy = new Stack <Order>(tradeWindow); var rampingOrders = new Stack <Order>(windowStackCopy); var rampingAnalysisResults = new List <IRampingStrategySummaryPanel>(); while (rampingOrders.Any()) { var rampingOrderList = rampingOrders.ToList(); var rootOrder = rampingOrders.Any() ? rampingOrders.Peek() : null; var marketDataSubset = marketData.Response.Where(_ => _.TimeStamp <= rootOrder.FilledDate).ToList(); var rampingAnalysisResult = this.rampingAnalyzer.Analyse(rampingOrderList, marketDataSubset); rampingAnalysisResults.Add(rampingAnalysisResult); rampingOrders.Pop(); } if (!rampingAnalysisResults.Any() || !rampingAnalysisResults.First().HasRampingStrategy() || rampingAnalysisResults.All(_ => !_.HasRampingStrategy())) { // LOG THEN EXIT this.logger.LogInformation($"A rule breach was not detected for {lastTrade?.Instrument?.Identifiers}. Returning."); return; } var rampingPrevalence = this.RampingPrevalence(rampingAnalysisResults); if (rampingPrevalence < this.rampingParameters.AutoCorrelationCoefficient) { // LOG THEN EXIT this.logger.LogInformation($"A rule breach was not detected due to an auto correlation of {rampingPrevalence} for {lastTrade?.Instrument?.Identifiers}. Returning."); return; } var tradePosition = new TradePosition(tradeWindow.ToList()); // wrong but should be a judgement var breach = new RampingRuleBreach( this.TradeBackwardWindowSize, tradePosition, lastTrade.Instrument, this.rampingParameters.Id, this.ruleContext?.Id(), this.ruleContext?.CorrelationId(), this.OrganisationFactorValue, rampingAnalysisResults.Last(), this.rampingParameters, null, null, this.UniverseDateTime); this.logger.LogInformation($"RunRule has breached parameter conditions for {lastTrade?.Instrument?.Identifiers}. Adding message to alert stream."); var message = new UniverseAlertEvent(Domain.Surveillance.Scheduling.Rules.Ramping, breach, this.ruleContext); this.alertStream.Add(message); }
/// <summary> /// The check for price movement. /// </summary> /// <param name="opposingPosition"> /// The opposing position. /// </param> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <returns> /// The <see cref="RuleBreachDescription"/>. /// </returns> private RuleBreachDescription CheckForPriceMovement( ITradePosition opposingPosition, Order mostRecentTrade) { var startDate = opposingPosition.Get().Where(op => op.PlacedDate != null).Min(op => op.PlacedDate).GetValueOrDefault(); var endDate = opposingPosition.Get().Where(op => op.PlacedDate != null).Max(op => op.PlacedDate).GetValueOrDefault(); if (endDate.Subtract(startDate) < TimeSpan.FromMinutes(1)) { endDate = endDate.AddMinutes(1); } var marketRequest = new MarketDataRequest( mostRecentTrade.Market.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, startDate.Subtract(this.TradeBackwardWindowSize), endDate, this.ruleContext?.Id(), DataSource.AnyIntraday); var tradingDays = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( UniverseDateTime.Subtract(this.TradeBackwardWindowSize), UniverseDateTime, mostRecentTrade.Market.MarketIdentifierCode); var marketResult = UniverseEquityIntradayCache.GetMarketsForRange(marketRequest, tradingDays, RunMode); if (marketResult.HadMissingData) { this.logger.LogInformation($"unable to fetch market data frames for {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } if (mostRecentTrade.PlacedDate > endDate) { endDate = mostRecentTrade.PlacedDate.GetValueOrDefault(); } var securityDataTicks = marketResult.Response; var startTick = this.StartTick(securityDataTicks, startDate); if (startTick == null) { this.logger.LogInformation($"unable to fetch starting exchange tick data for ({startDate}) {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } var endTick = this.EndTick(securityDataTicks, endDate); if (endTick == null) { this.logger.LogInformation($"unable to fetch ending exchange tick data for ({endDate}) {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } var priceMovement = endTick.SpreadTimeBar.Price.Value - startTick.SpreadTimeBar.Price.Value; return(this.BuildDescription(mostRecentTrade, priceMovement, startTick, endTick)); }
/// <summary> /// The check window volume breach. /// </summary> /// <param name="opposingPosition"> /// The opposing position. /// </param> /// <param name="mostRecentTrade"> /// The most recent trade. /// </param> /// <returns> /// The <see cref="RuleBreachDescription"/>. /// </returns> private RuleBreachDescription CheckWindowVolumeBreach( ITradePosition opposingPosition, Order mostRecentTrade) { var marketDataRequest = new MarketDataRequest( mostRecentTrade.Market.MarketIdentifierCode, mostRecentTrade.Instrument.Cfi, mostRecentTrade.Instrument.Identifiers, UniverseDateTime.Subtract(this.TradeBackwardWindowSize), UniverseDateTime, this.ruleContext?.Id(), DataSource.AnyIntraday); var tradingDays = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( UniverseDateTime.Subtract(this.TradeBackwardWindowSize), UniverseDateTime, mostRecentTrade.Market.MarketIdentifierCode); var securityResult = UniverseEquityIntradayCache.GetMarketsForRange(marketDataRequest, tradingDays, RunMode); if (securityResult.HadMissingData) { this.logger.LogWarning($"unable to fetch market data frames for {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } var windowVolume = securityResult.Response.Sum(sdt => sdt?.SpreadTimeBar.Volume.Traded); if (windowVolume <= 0) { this.logger.LogInformation($"unable to sum meaningful volume from market data frames for volume window in {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } if (opposingPosition.TotalVolumeOrderedOrFilled() <= 0) { this.logger.LogInformation($"unable to calculate opposing position volume window in {mostRecentTrade.Market.MarketIdentifierCode} at {UniverseDateTime}."); this.hadMissingData = true; return(RuleBreachDescription.False()); } var percentageWindowVolume = (decimal)opposingPosition.TotalVolumeOrderedOrFilled() / (decimal)windowVolume; if (percentageWindowVolume >= this.equitiesParameters.PercentageOfMarketWindowVolume) { return(new RuleBreachDescription { RuleBreached = true, Description = $" Percentage of market volume traded within a {this.equitiesParameters.Windows.BackwardWindowSize.TotalSeconds} second window exceeded the layering window threshold of {this.equitiesParameters.PercentageOfMarketWindowVolume * 100}%." }); } return(RuleBreachDescription.False()); }
/// <summary> /// The run post order event. /// </summary> /// <param name="history"> /// The history. /// </param> protected override void RunPostOrderEvent(ITradingHistoryStack history) { if (!this.processingMarketClose || this.latestMarketClosure == null) { return; } var ordersToCheck = history .ActiveTradeHistory() .Where(_ => _.OrderLimitPrice?.Value != null && _.OrderType == OrderTypes.LIMIT && ( _.OrderStatus() == OrderStatus.Placed || _.OrderStatus() == OrderStatus.Booked || _.OrderStatus() == OrderStatus.Amended || _.OrderStatus() == OrderStatus.Cancelled || _.OrderStatus() == OrderStatus.Rejected)) .ToList(); if (!ordersToCheck.Any()) { this.logger.LogInformation("RunPostOrderEvent did not have any orders to check after filtering for invalid order status values"); return; } var openingHours = this.latestMarketClosure.MarketClose - this.latestMarketClosure.MarketOpen; var benchmarkOrder = ordersToCheck.First(); var tradingHours = this.tradingHoursService.GetTradingHoursForMic(benchmarkOrder.Market?.MarketIdentifierCode); if (!tradingHours.IsValid) { this.logger.LogError($"Request for trading hours was invalid. MIC - {benchmarkOrder.Market?.MarketIdentifierCode}"); } var tradingDates = this.tradingHoursService.GetTradingDaysWithinRangeAdjustedToTime( tradingHours.OpeningInUtcForDay(UniverseDateTime.Subtract(this.TradeBackwardWindowSize)), tradingHours.ClosingInUtcForDay(UniverseDateTime), benchmarkOrder.Market?.MarketIdentifierCode); var marketDataRequest = new MarketDataRequest( benchmarkOrder.Market.MarketIdentifierCode, benchmarkOrder.Instrument.Cfi, benchmarkOrder.Instrument.Identifiers, UniverseDateTime.Subtract(openingHours), // implicitly correct (market closure event trigger) UniverseDateTime, this.ruleContext?.Id(), DataSource.AnyIntraday); var dataResponse = UniverseEquityIntradayCache.GetMarketsForRange(marketDataRequest, tradingDates, RunMode); if (dataResponse.HadMissingData || dataResponse.Response == null || !dataResponse.Response.Any()) { this.logger.LogInformation($"RunPostOrderEvent could not find relevant market data for {benchmarkOrder.Instrument?.Identifiers} on {this.latestMarketClosure?.MarketId}"); this.hadMissingData = true; return; } // ReSharper disable once AssignNullToNotNullAttribute var pricesInTimeBars = dataResponse.Response.Select(_ => (double)_.SpreadTimeBar.Price.Value).ToList(); var sd = (decimal)MathNet.Numerics.Statistics.Statistics.StandardDeviation(pricesInTimeBars); var mean = (decimal)MathNet.Numerics.Statistics.Statistics.Mean(pricesInTimeBars); var ruleBreaches = ordersToCheck .Select(_ => this.ReferenceOrderSigma(_, sd, mean)) .Where(_ => _.Item1 > 0 && _.Item1 > this.parameters.Sigma) .Where(_ => this.CheckIfOrderWouldNotOfExecuted(_, dataResponse.Response)) .ToList(); if (!ruleBreaches.Any()) { return; } var position = new TradePosition(ruleBreaches.Select(_ => _.Item2).ToList()); var poe = ruleBreaches.Select(_ => this.ExecutionUnderNormalDistribution(_.Item2, mean, sd, _.Item1)).ToList(); // wrong but should be a judgement var breach = new PlacingOrderWithNoIntentToExecuteRuleRuleBreach( this.parameters.Windows.BackwardWindowSize, position, benchmarkOrder.Instrument, this.OrganisationFactorValue, mean, sd, poe, this.parameters, this.ruleContext, null, null, this.UniverseDateTime); var alertEvent = new UniverseAlertEvent(Domain.Surveillance.Scheduling.Rules.PlacingOrderWithNoIntentToExecute, breach, this.ruleContext); this.alertStream.Add(alertEvent); }