private string SellVolumeDescription(
            IMarkingTheCloseBreach ruleBreach,
            decimal volumePercentageSetByUser,
            decimal sellVolumePercentage,
            bool window)
        {
            var windowType = window ? "Daily" : "Window";

            return
                ($" {windowType} volume threshold of {volumePercentageSetByUser}% was exceeded on sell orders by {sellVolumePercentage}% of {windowType.ToLower()} volume purchased within {ruleBreach.Window.TotalMinutes} minutes of market close.");
        }
        public async Task Send(IMarkingTheCloseBreach breach)
        {
            if (breach == null)
            {
                this.Logger.LogInformation(
                    "MarkingTheCloseMessageSender received a null breach for rule ctx. Returning.");
                return;
            }

            var description = this.BuildDescription(breach);

            await this.Send(breach, description);
        }
        private string BuildDescription(IMarkingTheCloseBreach ruleBreach)
        {
            var header =
                $"Marking the close rule breach detected for {ruleBreach.Security.Name} traded on {ruleBreach.MarketClose.MarketId} which closed at {ruleBreach.MarketClose.MarketClose.ToShortTimeString()}. ";

            if ((ruleBreach.WindowBreach?.HasBuyVolumeBreach ?? false) ||
                (ruleBreach.WindowBreach?.HasSellVolumeBreach ?? false))
            {
                header = this.AppendWindowBreach(ruleBreach, header);
            }

            if ((ruleBreach.DailyBreach?.HasBuyVolumeBreach ?? false) ||
                (ruleBreach.DailyBreach?.HasSellVolumeBreach ?? false))
            {
                header = this.AppendDailyBreach(ruleBreach, header);
            }

            return(header);
        }
        private string AppendWindowBreach(IMarkingTheCloseBreach ruleBreach, string header)
        {
            if (ruleBreach?.WindowBreach == null)
            {
                return(header);
            }

            var windowVolumePercentageSetByUser = Math.Round(
                ruleBreach.EquitiesParameters.PercentageThresholdWindowVolume.GetValueOrDefault(0) * 100m,
                2,
                MidpointRounding.AwayFromZero);

            var windowBuyVolumePercentage = Math.Round(
                ruleBreach.WindowBreach.BuyVolumeBreach.GetValueOrDefault(0) * 100m,
                2,
                MidpointRounding.AwayFromZero);

            var windowSellVolumePercentage = Math.Round(
                ruleBreach.WindowBreach.SellVolumeBreach.GetValueOrDefault(0) * 100m,
                2,
                MidpointRounding.AwayFromZero);

            var windowBuyVolumeMark = ruleBreach.WindowBreach.HasBuyVolumeBreach
                                          ? this.BuyVolumeDescription(
                ruleBreach,
                windowVolumePercentageSetByUser,
                windowBuyVolumePercentage,
                false)
                                          : string.Empty;

            var windowSellVolumeMark = ruleBreach.WindowBreach.HasSellVolumeBreach
                                           ? this.SellVolumeDescription(
                ruleBreach,
                windowVolumePercentageSetByUser,
                windowSellVolumePercentage,
                false)
                                           : string.Empty;

            header = $"{header}{windowBuyVolumeMark}{windowSellVolumeMark}";

            return(header);
        }