package validation import ( "fmt" "math/big" ) // PriceImpactThresholds defines the acceptable price impact levels type PriceImpactThresholds struct { // Low risk: < 0.5% price impact LowThreshold float64 // Medium risk: 0.5% - 2% price impact MediumThreshold float64 // High risk: 2% - 5% price impact HighThreshold float64 // Extreme risk: > 5% price impact (typically unprofitable due to slippage) ExtremeThreshold float64 // Maximum acceptable: Reject anything above this (e.g., 10%) MaxAcceptable float64 } // DefaultPriceImpactThresholds returns conservative production-ready thresholds func DefaultPriceImpactThresholds() *PriceImpactThresholds { return &PriceImpactThresholds{ LowThreshold: 0.5, // 0.5% MediumThreshold: 2.0, // 2% HighThreshold: 5.0, // 5% ExtremeThreshold: 10.0, // 10% MaxAcceptable: 15.0, // 15% - reject anything higher } } // AggressivePriceImpactThresholds returns more aggressive thresholds for higher volumes func AggressivePriceImpactThresholds() *PriceImpactThresholds { return &PriceImpactThresholds{ LowThreshold: 1.0, // 1% MediumThreshold: 3.0, // 3% HighThreshold: 7.0, // 7% ExtremeThreshold: 15.0, // 15% MaxAcceptable: 25.0, // 25% } } // ConservativePriceImpactThresholds returns very conservative thresholds for safety func ConservativePriceImpactThresholds() *PriceImpactThresholds { return &PriceImpactThresholds{ LowThreshold: 0.1, // 0.1% MediumThreshold: 0.5, // 0.5% HighThreshold: 1.0, // 1% ExtremeThreshold: 2.0, // 2% MaxAcceptable: 5.0, // 5% } } // PriceImpactRiskLevel represents the risk level of a price impact type PriceImpactRiskLevel string const ( RiskLevelNegligible PriceImpactRiskLevel = "Negligible" // < 0.1% RiskLevelLow PriceImpactRiskLevel = "Low" // 0.1-0.5% RiskLevelMedium PriceImpactRiskLevel = "Medium" // 0.5-2% RiskLevelHigh PriceImpactRiskLevel = "High" // 2-5% RiskLevelExtreme PriceImpactRiskLevel = "Extreme" // 5-10% RiskLevelUnacceptable PriceImpactRiskLevel = "Unacceptable" // > 10% ) // PriceImpactValidationResult contains the result of price impact validation type PriceImpactValidationResult struct { PriceImpact float64 // The calculated price impact percentage RiskLevel PriceImpactRiskLevel // The risk categorization IsAcceptable bool // Whether this price impact is acceptable Recommendation string // Human-readable recommendation Details map[string]interface{} // Additional details } // PriceImpactValidator validates price impacts against configured thresholds type PriceImpactValidator struct { thresholds *PriceImpactThresholds } // NewPriceImpactValidator creates a new price impact validator func NewPriceImpactValidator(thresholds *PriceImpactThresholds) *PriceImpactValidator { if thresholds == nil { thresholds = DefaultPriceImpactThresholds() } return &PriceImpactValidator{ thresholds: thresholds, } } // ValidatePriceImpact validates a price impact percentage func (piv *PriceImpactValidator) ValidatePriceImpact(priceImpact float64) *PriceImpactValidationResult { result := &PriceImpactValidationResult{ PriceImpact: priceImpact, Details: make(map[string]interface{}), } // Determine risk level result.RiskLevel = piv.categorizePriceImpact(priceImpact) // Determine if acceptable result.IsAcceptable = priceImpact <= piv.thresholds.MaxAcceptable // Generate recommendation result.Recommendation = piv.generateRecommendation(priceImpact, result.RiskLevel) // Add threshold details result.Details["thresholds"] = map[string]float64{ "low": piv.thresholds.LowThreshold, "medium": piv.thresholds.MediumThreshold, "high": piv.thresholds.HighThreshold, "extreme": piv.thresholds.ExtremeThreshold, "max": piv.thresholds.MaxAcceptable, } // Add risk-specific details result.Details["risk_level"] = string(result.RiskLevel) result.Details["acceptable"] = result.IsAcceptable result.Details["price_impact_percent"] = priceImpact return result } // categorizePriceImpact categorizes the price impact into risk levels func (piv *PriceImpactValidator) categorizePriceImpact(priceImpact float64) PriceImpactRiskLevel { switch { case priceImpact < 0.1: return RiskLevelNegligible case priceImpact < piv.thresholds.LowThreshold: return RiskLevelLow case priceImpact < piv.thresholds.MediumThreshold: return RiskLevelMedium case priceImpact < piv.thresholds.HighThreshold: return RiskLevelHigh case priceImpact < piv.thresholds.ExtremeThreshold: return RiskLevelExtreme default: return RiskLevelUnacceptable } } // generateRecommendation generates a recommendation based on price impact func (piv *PriceImpactValidator) generateRecommendation(priceImpact float64, riskLevel PriceImpactRiskLevel) string { switch riskLevel { case RiskLevelNegligible: return fmt.Sprintf("Excellent: Price impact of %.4f%% is negligible. Safe to execute.", priceImpact) case RiskLevelLow: return fmt.Sprintf("Good: Price impact of %.4f%% is low. Execute with standard slippage protection.", priceImpact) case RiskLevelMedium: return fmt.Sprintf("Moderate: Price impact of %.4f%% is medium. Use enhanced slippage protection and consider splitting the trade.", priceImpact) case RiskLevelHigh: return fmt.Sprintf("Caution: Price impact of %.4f%% is high. Strongly recommend splitting into smaller trades or waiting for better liquidity.", priceImpact) case RiskLevelExtreme: return fmt.Sprintf("Warning: Price impact of %.4f%% is extreme. Trade size is too large for current liquidity. Split trade or skip.", priceImpact) case RiskLevelUnacceptable: return fmt.Sprintf("Reject: Price impact of %.4f%% exceeds maximum acceptable threshold (%.2f%%). Do not execute.", priceImpact, piv.thresholds.MaxAcceptable) default: return "Unknown risk level" } } // ValidatePriceImpactWithLiquidity validates price impact considering trade size and liquidity func (piv *PriceImpactValidator) ValidatePriceImpactWithLiquidity(tradeSize, liquidity *big.Int) *PriceImpactValidationResult { if tradeSize == nil || liquidity == nil || liquidity.Sign() == 0 { return &PriceImpactValidationResult{ PriceImpact: 0, RiskLevel: RiskLevelUnacceptable, IsAcceptable: false, Recommendation: "Invalid input: trade size or liquidity is nil/zero", Details: make(map[string]interface{}), } } // Calculate price impact: tradeSize / (liquidity + tradeSize) * 100 tradeSizeFloat := new(big.Float).SetInt(tradeSize) liquidityFloat := new(big.Float).SetInt(liquidity) // Price impact = tradeSize / (liquidity + tradeSize) denominator := new(big.Float).Add(liquidityFloat, tradeSizeFloat) priceImpactRatio := new(big.Float).Quo(tradeSizeFloat, denominator) priceImpactPercent, _ := priceImpactRatio.Float64() priceImpactPercent *= 100.0 result := piv.ValidatePriceImpact(priceImpactPercent) // Add liquidity-specific details result.Details["trade_size"] = tradeSize.String() result.Details["liquidity"] = liquidity.String() result.Details["trade_to_liquidity_ratio"] = new(big.Float).Quo(tradeSizeFloat, liquidityFloat).Text('f', 6) return result } // ShouldRejectTrade determines if a trade should be rejected based on price impact func (piv *PriceImpactValidator) ShouldRejectTrade(priceImpact float64) bool { return priceImpact > piv.thresholds.MaxAcceptable } // ShouldSplitTrade determines if a trade should be split based on price impact func (piv *PriceImpactValidator) ShouldSplitTrade(priceImpact float64) bool { return priceImpact >= piv.thresholds.MediumThreshold } // GetRecommendedSplitCount recommends how many parts to split a trade into func (piv *PriceImpactValidator) GetRecommendedSplitCount(priceImpact float64) int { switch { case priceImpact < piv.thresholds.MediumThreshold: return 1 // No split needed case priceImpact < piv.thresholds.HighThreshold: return 2 // Split into 2 case priceImpact < piv.thresholds.ExtremeThreshold: return 4 // Split into 4 case priceImpact < piv.thresholds.MaxAcceptable: return 8 // Split into 8 default: return 0 // Reject trade } } // CalculateMaxTradeSize calculates the maximum trade size for a given price impact target func (piv *PriceImpactValidator) CalculateMaxTradeSize(liquidity *big.Int, targetPriceImpact float64) *big.Int { if liquidity == nil || liquidity.Sign() == 0 { return big.NewInt(0) } // From: priceImpact = tradeSize / (liquidity + tradeSize) // Solve for tradeSize: tradeSize = (priceImpact * liquidity) / (1 - priceImpact) priceImpactDecimal := targetPriceImpact / 100.0 if priceImpactDecimal >= 1.0 { return big.NewInt(0) // Invalid: 100% price impact or more } liquidityFloat := new(big.Float).SetInt(liquidity) priceImpactFloat := big.NewFloat(priceImpactDecimal) // numerator = priceImpact * liquidity numerator := new(big.Float).Mul(priceImpactFloat, liquidityFloat) // denominator = 1 - priceImpact denominator := new(big.Float).Sub(big.NewFloat(1.0), priceImpactFloat) // maxTradeSize = numerator / denominator maxTradeSize := new(big.Float).Quo(numerator, denominator) result, _ := maxTradeSize.Int(nil) return result } // GetThresholds returns the current threshold configuration func (piv *PriceImpactValidator) GetThresholds() *PriceImpactThresholds { return piv.thresholds } // SetThresholds updates the threshold configuration func (piv *PriceImpactValidator) SetThresholds(thresholds *PriceImpactThresholds) { if thresholds != nil { piv.thresholds = thresholds } } // FormatPriceImpact formats a price impact value for display func FormatPriceImpact(priceImpact float64) string { return fmt.Sprintf("%.4f%%", priceImpact) }