266 lines
9.6 KiB
Go
266 lines
9.6 KiB
Go
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)
|
|
}
|