Files
mev-beta/test/enhanced_profit_test.go
Krypto Kajun fac8a64092 feat: Implement comprehensive Market Manager with database and logging
- Add complete Market Manager package with in-memory storage and CRUD operations
- Implement arbitrage detection with profit calculations and thresholds
- Add database adapter with PostgreSQL schema for persistence
- Create comprehensive logging system with specialized log files
- Add detailed documentation and implementation plans
- Include example application and comprehensive test suite
- Update Makefile with market manager build targets
- Add check-implementations command for verification
2025-09-18 03:52:33 -05:00

191 lines
5.6 KiB
Go

package test
import (
"context"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/profitcalc"
)
func TestEnhancedProfitCalculationAndRanking(t *testing.T) {
// Create a test logger
log := logger.New("debug", "text", "")
// Create profit calculator and ranker
calc := profitcalc.NewSimpleProfitCalculator(log)
ranker := profitcalc.NewOpportunityRanker(log)
// Test tokens
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
arbAddr := common.HexToAddress("0x912ce59144191c1204e64559fe8253a0e49e6548")
// Create multiple test opportunities with different characteristics
opportunities := []*profitcalc.SimpleOpportunity{
// High profit, high confidence opportunity
calc.AnalyzeSwapOpportunity(
context.Background(),
wethAddr, usdcAddr,
big.NewFloat(5.0), // 5 ETH
big.NewFloat(10000.0), // 10k USDC
"UniswapV3",
),
// Medium profit opportunity
calc.AnalyzeSwapOpportunity(
context.Background(),
wethAddr, arbAddr,
big.NewFloat(1.0), // 1 ETH
big.NewFloat(2500.0), // 2.5k ARB
"SushiSwap",
),
// Lower profit opportunity
calc.AnalyzeSwapOpportunity(
context.Background(),
usdcAddr, arbAddr,
big.NewFloat(100.0), // 100 USDC
big.NewFloat(250.0), // 250 ARB
"TraderJoe",
),
// Very small opportunity (should be filtered out)
calc.AnalyzeSwapOpportunity(
context.Background(),
wethAddr, usdcAddr,
big.NewFloat(0.001), // 0.001 ETH
big.NewFloat(2.0), // 2 USDC
"PancakeSwap",
),
}
// Add opportunities to ranker
var rankedOpps []*profitcalc.RankedOpportunity
for i, opp := range opportunities {
if opp != nil {
rankedOpp := ranker.AddOpportunity(opp)
if rankedOpp != nil {
rankedOpps = append(rankedOpps, rankedOpp)
t.Logf("Added Opportunity %d: ID=%s, NetProfit=%s ETH, ProfitMargin=%.4f%%, Confidence=%.2f",
i+1, opp.ID, calc.FormatEther(opp.NetProfit), opp.ProfitMargin*100, opp.Confidence)
} else {
t.Logf("Opportunity %d filtered out: ID=%s", i+1, opp.ID)
}
}
}
// Get top opportunities
topOpps := ranker.GetTopOpportunities(3)
t.Logf("\n=== Top 3 Opportunities ===")
for _, opp := range topOpps {
t.Logf("Rank %d: ID=%s, Score=%.4f, NetProfit=%s ETH, ProfitMargin=%.4f%%, Confidence=%.2f, Competition=%.2f",
opp.Rank, opp.ID, opp.Score, calc.FormatEther(opp.NetProfit),
opp.ProfitMargin*100, opp.Confidence, opp.CompetitionRisk)
}
// Get executable opportunities
executable := ranker.GetExecutableOpportunities(5)
t.Logf("\n=== Executable Opportunities ===")
for _, opp := range executable {
t.Logf("ID=%s, Executable=%t, Reason=%s, Score=%.4f",
opp.ID, opp.IsExecutable, opp.RejectReason, opp.Score)
}
// Get statistics
stats := ranker.GetStats()
t.Logf("\n=== Ranking Statistics ===")
t.Logf("Total Opportunities: %v", stats["totalOpportunities"])
t.Logf("Executable Opportunities: %v", stats["executableOpportunities"])
t.Logf("Average Score: %.4f", stats["averageScore"])
t.Logf("Average Confidence: %.4f", stats["averageConfidence"])
// Verify ranking behavior
if len(topOpps) > 1 {
// First ranked opportunity should have highest score
if topOpps[0].Score < topOpps[1].Score {
t.Errorf("Ranking error: first opportunity (%.4f) should have higher score than second (%.4f)",
topOpps[0].Score, topOpps[1].Score)
}
}
// Test opportunity updates
t.Logf("\n=== Testing Opportunity Updates ===")
if len(opportunities) > 0 && opportunities[0] != nil {
// Create a similar opportunity (same tokens, similar amount)
similarOpp := calc.AnalyzeSwapOpportunity(
context.Background(),
opportunities[0].TokenA, opportunities[0].TokenB,
big.NewFloat(5.1), // Slightly different amount
big.NewFloat(10200.0),
"UniswapV3",
)
if similarOpp != nil {
rankedSimilar := ranker.AddOpportunity(similarOpp)
if rankedSimilar != nil {
t.Logf("Updated similar opportunity: UpdateCount=%d", rankedSimilar.UpdateCount)
if rankedSimilar.UpdateCount < 2 {
t.Errorf("Expected UpdateCount >= 2, got %d", rankedSimilar.UpdateCount)
}
}
}
}
}
func TestOpportunityAging(t *testing.T) {
// Create a test logger
log := logger.New("debug", "text", "")
// Create profit calculator and ranker with short TTL for testing
calc := profitcalc.NewSimpleProfitCalculator(log)
ranker := profitcalc.NewOpportunityRanker(log)
// Create a test opportunity
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
opp := calc.AnalyzeSwapOpportunity(
context.Background(),
wethAddr, usdcAddr,
big.NewFloat(1.0),
big.NewFloat(2000.0),
"UniswapV3",
)
if opp == nil {
t.Fatal("Failed to create test opportunity")
}
// Add opportunity
rankedOpp := ranker.AddOpportunity(opp)
if rankedOpp == nil {
t.Fatal("Failed to add opportunity to ranker")
}
initialScore := rankedOpp.Score
t.Logf("Initial opportunity score: %.4f", initialScore)
// Wait a moment and check that freshness affects score
time.Sleep(100 * time.Millisecond)
// Re-rank to update scores based on age
topOpps := ranker.GetTopOpportunities(1)
if len(topOpps) > 0 {
newScore := topOpps[0].Score
t.Logf("Score after aging: %.4f", newScore)
// Score should decrease due to freshness component (though might be minimal for 100ms)
if newScore > initialScore {
t.Logf("Note: Score increased slightly, this is normal for short time periods")
}
}
// Test stats
stats := ranker.GetStats()
t.Logf("Ranker stats: %+v", stats)
}