コード例 #1
0
 public LayeringRuleBreach(
     IFactorValue factorValue,
     ISystemProcessOperationContext operationContext,
     string correlationId,
     ILayeringRuleEquitiesParameters equitiesParameters,
     TimeSpan window,
     ITradePosition trades,
     FinancialInstrument security,
     RuleBreachDescription bidirectionalTradeBreach,
     RuleBreachDescription dailyVolumeTradeBreach,
     RuleBreachDescription windowVolumeTradeBreach,
     RuleBreachDescription priceMovementBreach,
     string description,
     string caseTitle,
     DateTime universeDateTime)
 {
     this.FactorValue              = factorValue;
     this.EquitiesParameters       = equitiesParameters;
     this.Window                   = window;
     this.Trades                   = trades;
     this.Security                 = security;
     this.BidirectionalTradeBreach = bidirectionalTradeBreach;
     this.DailyVolumeTradeBreach   = dailyVolumeTradeBreach;
     this.WindowVolumeTradeBreach  = windowVolumeTradeBreach;
     this.PriceMovementBreach      = priceMovementBreach;
     this.RuleParameterId          = equitiesParameters?.Id ?? string.Empty;
     this.SystemOperationId        = operationContext.Id.ToString();
     this.CorrelationId            = correlationId;
     this.RuleParameters           = equitiesParameters;
     this.Description              = description ?? string.Empty;
     this.CaseTitle                = caseTitle ?? string.Empty;
     this.UniverseDateTime         = universeDateTime;
 }
コード例 #2
0
        /// <summary>
        /// The build description.
        /// </summary>
        /// <param name="mostRecentTrade">
        /// The most recent trade.
        /// </param>
        /// <param name="priceMovement">
        /// The price movement.
        /// </param>
        /// <param name="startTick">
        /// The start tick.
        /// </param>
        /// <param name="endTick">
        /// The end tick.
        /// </param>
        /// <returns>
        /// The <see cref="RuleBreachDescription"/>.
        /// </returns>
        private RuleBreachDescription BuildDescription(
            Order mostRecentTrade,
            decimal priceMovement,
            EquityInstrumentIntraDayTimeBar startTick,
            EquityInstrumentIntraDayTimeBar endTick)
        {
            switch (mostRecentTrade.OrderDirection)
            {
            case OrderDirections.BUY:
            case OrderDirections.COVER:
                return(priceMovement < 0
                        ? new RuleBreachDescription {
                    RuleBreached = true, Description = $" Prices in {mostRecentTrade.Instrument.Name} moved from ({endTick.SpreadTimeBar.Price.Currency}) {endTick.SpreadTimeBar.Price.Value} to ({startTick.SpreadTimeBar.Price.Currency}) {startTick.SpreadTimeBar.Price.Value} for a net change of {startTick.SpreadTimeBar.Price.Currency} {priceMovement} in line with the layering price pressure influence."
                }
                        : RuleBreachDescription.False());

            case OrderDirections.SELL:
            case OrderDirections.SHORT:
                return(priceMovement > 0
                        ? new RuleBreachDescription {
                    RuleBreached = true, Description = $" Prices in {mostRecentTrade.Instrument.Name} moved from ({endTick.SpreadTimeBar.Price.Currency}) {endTick.SpreadTimeBar.Price.Value} to ({startTick.SpreadTimeBar.Price.Currency}) {startTick.SpreadTimeBar.Price.Value} for a net change of {startTick.SpreadTimeBar.Price.Currency} {priceMovement} in line with the layering price pressure influence."
                } : RuleBreachDescription.False());

            default:
                this.logger.LogError($"Layering rule is not taking into account a new order position value (handles buy/sell) {mostRecentTrade.OrderDirection} (Arg Out of Range)");
                this.ruleContext.EventException($"Layering rule is not taking into account a new order position value (handles buy/sell) {mostRecentTrade.OrderDirection} (Arg Out of Range)");
                return(RuleBreachDescription.False());
            }
        }
コード例 #3
0
        /// <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());
        }
コード例 #4
0
 /// <summary>
 /// The has rule breach.
 /// </summary>
 /// <param name="hasBidirectionalBreach">
 /// The has bidirectional breach.
 /// </param>
 /// <param name="hasDailyVolumeBreach">
 /// The has daily volume breach.
 /// </param>
 /// <param name="hasWindowVolumeBreach">
 /// The has window volume breach.
 /// </param>
 /// <param name="priceMovementBreach">
 /// The price movement breach.
 /// </param>
 /// <returns>
 /// The <see cref="bool"/>.
 /// </returns>
 private bool HasRuleBreach(
     RuleBreachDescription hasBidirectionalBreach,
     RuleBreachDescription hasDailyVolumeBreach,
     RuleBreachDescription hasWindowVolumeBreach,
     RuleBreachDescription priceMovementBreach)
 {
     return((hasBidirectionalBreach?.RuleBreached ?? false) ||
            (hasDailyVolumeBreach?.RuleBreached ?? false) ||
            (hasWindowVolumeBreach?.RuleBreached ?? false) ||
            (priceMovementBreach?.RuleBreached ?? false));
 }
コード例 #5
0
        /// <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));
        }
コード例 #6
0
        /// <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());
        }
コード例 #7
0
        /// <summary>
        /// The check position for layering.
        /// </summary>
        /// <param name="tradeWindow">
        /// The trade window.
        /// </param>
        /// <param name="buyPosition">
        /// The buy position.
        /// </param>
        /// <param name="sellPosition">
        /// The sell position.
        /// </param>
        /// <param name="tradingPosition">
        /// The trading position.
        /// </param>
        /// <param name="opposingPosition">
        /// The opposing position.
        /// </param>
        /// <param name="mostRecentTrade">
        /// The most recent trade.
        /// </param>
        /// <returns>
        /// The <see cref="ILayeringRuleBreach"/>.
        /// </returns>
        private ILayeringRuleBreach CheckPositionForLayering(
            Stack <Order> tradeWindow,
            ITradePosition buyPosition,
            ITradePosition sellPosition,
            ITradePosition tradingPosition,
            ITradePosition opposingPosition,
            Order mostRecentTrade)
        {
            var hasTradesInWindow = tradeWindow.Any();
            RuleBreachDescription hasBidirectionalBreach = RuleBreachDescription.False();
            RuleBreachDescription hasDailyVolumeBreach   = RuleBreachDescription.False();
            RuleBreachDescription hasWindowVolumeBreach  = RuleBreachDescription.False();
            RuleBreachDescription priceMovementBreach    = RuleBreachDescription.False();

            // ReSharper disable once LoopVariableIsNeverChangedInsideLoop
            while (hasTradesInWindow)
            {
                if (!tradeWindow.Any())
                {
                    // ReSharper disable once RedundantAssignment
                    hasTradesInWindow = false;
                    break;
                }

                var nextTrade = tradeWindow.Pop();
                this.AddToPositions(buyPosition, sellPosition, nextTrade);

                if (!tradingPosition.Get().Any() ||
                    !opposingPosition.Get().Any())
                {
                    continue;
                }

                // IF ALL PARAMETERS ARE NULL JUST DO THE BIDIRECTIONAL TRADE CHECK
                if (this.equitiesParameters.PercentageOfMarketDailyVolume == null &&
                    this.equitiesParameters.PercentageOfMarketWindowVolume == null &&
                    this.equitiesParameters.CheckForCorrespondingPriceMovement == null)
                {
                    hasBidirectionalBreach = new RuleBreachDescription
                    {
                        RuleBreached = true,
                        Description  = " Trading in both buy/sell positions simultaneously was detected."
                    };
                }

                if (this.equitiesParameters.PercentageOfMarketDailyVolume != null)
                {
                    hasDailyVolumeBreach = this.CheckDailyVolumeBreach(opposingPosition, mostRecentTrade);
                }

                if (this.equitiesParameters.PercentageOfMarketWindowVolume != null)
                {
                    hasWindowVolumeBreach = this.CheckWindowVolumeBreach(opposingPosition, mostRecentTrade);
                }

                if (this.equitiesParameters.CheckForCorrespondingPriceMovement != null &&
                    this.equitiesParameters.CheckForCorrespondingPriceMovement.Value)
                {
                    priceMovementBreach = this.CheckForPriceMovement(opposingPosition, mostRecentTrade);
                }
            }

            var allTradesInPositions = opposingPosition.Get().Concat(tradingPosition.Get()).ToList();
            var allTrades            = new TradePosition(allTradesInPositions);

            if (!this.HasRuleBreach(
                    hasBidirectionalBreach,
                    hasDailyVolumeBreach,
                    hasWindowVolumeBreach,
                    priceMovementBreach))
            {
                return(null);
            }

            return(new LayeringRuleBreach(
                       this.OrganisationFactorValue,
                       this.ruleContext.SystemProcessOperationContext(),
                       this.ruleContext.CorrelationId(),
                       this.equitiesParameters,
                       this.equitiesParameters.Windows?.BackwardWindowSize ?? TimeSpan.Zero,
                       allTrades,
                       mostRecentTrade.Instrument,
                       hasBidirectionalBreach,
                       hasDailyVolumeBreach,
                       hasWindowVolumeBreach,
                       priceMovementBreach,
                       null,
                       null,
                       UniverseDateTime));
        }