private string BuildDescription(IWashTradeRuleBreach breach) { if (breach == null) { return(string.Empty); } var preamble = $"Wash trade rule breach. Traded {breach.Security?.Name}."; var positionAverage = string.Empty; var positionPaired = string.Empty; var positionClustering = string.Empty; if (breach.AveragePositionBreach != null && breach.AveragePositionBreach.AveragePositionRuleBreach) { positionAverage = this.BuildAveragePositionDescription(breach); } if (breach.ClusteringPositionBreach != null && breach.ClusteringPositionBreach.ClusteringPositionBreach) { positionClustering = this.BuildPositionClusteringDescription(breach); } return($"{preamble}{positionAverage}{positionPaired}{positionClustering}"); }
private string BuildAveragePositionDescription(IWashTradeRuleBreach breach) { var trades = breach.AveragePositionBreach.AveragePositionAmountOfTrades.GetValueOrDefault(0); var percentageChange = Math.Round( breach.AveragePositionBreach.AveragePositionRelativeValueChange.GetValueOrDefault(0) * 100, 2, MidpointRounding.AwayFromZero); var percentageChangeMax = Math.Round( breach.EquitiesParameters.AveragePositionMaximumPositionValueChange.GetValueOrDefault(0) * 100, 2, MidpointRounding.AwayFromZero); var averagePosition = $" {trades} trades appeared to be part of a series of wash trade activity. These trades netted a total of {percentageChange}% in the value of the traders position, lower values of change are considered to be stronger evidence of wash trading. {percentageChangeMax}% was the configured maximum value change for this to be considered an alert."; if (breach.EquitiesParameters.AveragePositionMaximumAbsoluteValueChangeAmount != null && breach.AveragePositionBreach.AveragePositionAbsoluteValueChange != null) { var absoluteChange = Math.Round( breach.AveragePositionBreach.AveragePositionAbsoluteValueChange.Value.Value, 2, MidpointRounding.AwayFromZero); averagePosition = $"{averagePosition} The absolute value change of the traders position in {breach.Security.Name} changed by ({breach.AveragePositionBreach.AveragePositionAbsoluteValueChange.Value.Currency.Code}){absoluteChange} against a maximum position value change of ({breach.EquitiesParameters.AveragePositionMaximumAbsoluteValueChangeCurrency}){breach.EquitiesParameters.AveragePositionMaximumAbsoluteValueChangeAmount}."; } return(averagePosition); }
/// <summary> /// Receive and cache rule breach in memory /// </summary> public void Send(IWashTradeRuleBreach ruleBreach) { if (ruleBreach == null) { // ReSharper disable once InconsistentlySynchronizedField this._logger.LogInformation("received a rule breach. Returning."); return; } lock (this._lock) { this._logger.LogInformation($"received rule breach for {ruleBreach.Security.Identifiers}"); var duplicates = this._messages.Where(msg => msg.Trades.PositionIsSubsetOf(ruleBreach.Trades)).ToList(); this._messages = this._messages.Except(duplicates).ToList(); this._logger.LogInformation( $"deduplicated {this._messages.Count} alerts when processing {ruleBreach.Security.Identifiers}."); this._messages.Add(ruleBreach); } }
private string BuildPositionClusteringDescription(IWashTradeRuleBreach breach) { var percentageChangeMax = Math.Round( breach.EquitiesParameters.ClusteringPercentageValueDifferenceThreshold.GetValueOrDefault(0) * 100, 2, MidpointRounding.AwayFromZero); var centroids = breach.ClusteringPositionBreach?.CentroidsOfBreachingClusters.Select( ce => Math.Round(ce, 2, MidpointRounding.AwayFromZero)) ?? new List <decimal>(); var initial = $" A clustering (k-means) rule breach was found with {breach.ClusteringPositionBreach.AmountOfBreachingClusters} clusters detected to be trading at thin margins per cluster defined as less than a {percentageChangeMax}% difference between cost and revenues when buying and selling a position."; if (breach.ClusteringPositionBreach != null && breach.ClusteringPositionBreach.CentroidsOfBreachingClusters.Any()) { initial = $"{initial} The centroids of the clusters were at the prices {centroids.Aggregate(string.Empty, (a, b) => $"{a}{b}")}"; } return(initial); }
public async Task Send(IWashTradeRuleBreach breach) { var description = this.BuildDescription(breach); await this.Send(breach, description); }