refactor: move all remaining files to orig/ directory
Completed clean root directory structure: - Root now contains only: .git, .env, docs/, orig/ - Moved all remaining files and directories to orig/: - Config files (.claude, .dockerignore, .drone.yml, etc.) - All .env variants (except active .env) - Git config (.gitconfig, .github, .gitignore, etc.) - Tool configs (.golangci.yml, .revive.toml, etc.) - Documentation (*.md files, @prompts) - Build files (Dockerfiles, Makefile, go.mod, go.sum) - Docker compose files - All source directories (scripts, tests, tools, etc.) - Runtime directories (logs, monitoring, reports) - Dependency files (node_modules, lib, cache) - Special files (--delete) - Removed empty runtime directories (bin/, data/) V2 structure is now clean: - docs/planning/ - V2 planning documents - orig/ - Complete V1 codebase preserved - .env - Active environment config (not in git) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
262
orig/tests/calculation-validation/parser.go
Normal file
262
orig/tests/calculation-validation/parser.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package calculation_validation
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogParser parses arbitrage opportunity data from log files
|
||||
type LogParser struct {
|
||||
executablePattern *regexp.Regexp
|
||||
profitPattern *regexp.Regexp
|
||||
thresholdPattern *regexp.Regexp
|
||||
v3CalcPattern *regexp.Regexp
|
||||
opportunityPattern *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewLogParser creates a new log parser
|
||||
func NewLogParser() *LogParser {
|
||||
return &LogParser{
|
||||
executablePattern: regexp.MustCompile(`EXECUTABLE OPPORTUNITY: ID=([^,]+), Profit=([0-9.]+) ETH \(threshold=([0-9.]+) ETH\)`),
|
||||
profitPattern: regexp.MustCompile(`Estimated Profit: \$([0-9,.]+) USD`),
|
||||
thresholdPattern: regexp.MustCompile(`Profit threshold check: netProfit=([0-9.]+) ETH, minThreshold=([0-9.]+) ETH`),
|
||||
v3CalcPattern: regexp.MustCompile(`V3 calculation: amountIn=([0-9]+), amountOut=([0-9]+), fee=([0-9]+), finalOut=([0-9]+)`),
|
||||
opportunityPattern: regexp.MustCompile(`ARBITRAGE OPPORTUNITY DETECTED`),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseExecutableOpportunities parses executable opportunities from log file
|
||||
func (lp *LogParser) ParseExecutableOpportunities(logFile string) ([]*OpportunityTestData, error) {
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var opportunities []*OpportunityTestData
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Parse executable opportunity
|
||||
if matches := lp.executablePattern.FindStringSubmatch(line); matches != nil {
|
||||
opp := &OpportunityTestData{
|
||||
ID: matches[1],
|
||||
Timestamp: time.Now(), // Would need to parse from log timestamp
|
||||
IsExecutable: true,
|
||||
}
|
||||
|
||||
// Parse profit
|
||||
profit, err := strconv.ParseFloat(matches[2], 64)
|
||||
if err == nil {
|
||||
opp.NetProfitETH = big.NewFloat(profit)
|
||||
}
|
||||
|
||||
// Parse threshold
|
||||
threshold, err := strconv.ParseFloat(matches[3], 64)
|
||||
if err == nil {
|
||||
opp.ThresholdCheck = &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(profit),
|
||||
MinThreshold: big.NewFloat(threshold),
|
||||
Passed: profit >= threshold,
|
||||
}
|
||||
}
|
||||
|
||||
opportunities = append(opportunities, opp)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading log file: %w", err)
|
||||
}
|
||||
|
||||
return opportunities, nil
|
||||
}
|
||||
|
||||
// ParseV3Calculations parses V3 swap calculations from log file
|
||||
func (lp *LogParser) ParseV3Calculations(logFile string) ([]SwapCalculation, error) {
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var calculations []SwapCalculation
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if matches := lp.v3CalcPattern.FindStringSubmatch(line); matches != nil {
|
||||
amountIn, _ := new(big.Int).SetString(matches[1], 10)
|
||||
amountOut, _ := new(big.Int).SetString(matches[2], 10)
|
||||
fee, _ := strconv.ParseUint(matches[3], 10, 32)
|
||||
finalOut, _ := new(big.Int).SetString(matches[4], 10)
|
||||
|
||||
calculations = append(calculations, SwapCalculation{
|
||||
AmountIn: amountIn,
|
||||
AmountOut: amountOut,
|
||||
Fee: uint32(fee),
|
||||
FinalOut: finalOut,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading log file: %w", err)
|
||||
}
|
||||
|
||||
return calculations, nil
|
||||
}
|
||||
|
||||
// ParseThresholdChecks parses profit threshold validation checks
|
||||
func (lp *LogParser) ParseThresholdChecks(logFile string) ([]*ThresholdCheck, error) {
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var checks []*ThresholdCheck
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if matches := lp.thresholdPattern.FindStringSubmatch(line); matches != nil {
|
||||
netProfit, _ := strconv.ParseFloat(matches[1], 64)
|
||||
minThreshold, _ := strconv.ParseFloat(matches[2], 64)
|
||||
|
||||
checks = append(checks, &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(netProfit),
|
||||
MinThreshold: big.NewFloat(minThreshold),
|
||||
Passed: netProfit >= minThreshold,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading log file: %w", err)
|
||||
}
|
||||
|
||||
return checks, nil
|
||||
}
|
||||
|
||||
// ExtractProfitValues extracts all profit values from opportunities
|
||||
func ExtractProfitValues(opportunities []*OpportunityTestData) []*big.Float {
|
||||
var profits []*big.Float
|
||||
for _, opp := range opportunities {
|
||||
if opp.NetProfitETH != nil {
|
||||
profits = append(profits, opp.NetProfitETH)
|
||||
}
|
||||
}
|
||||
return profits
|
||||
}
|
||||
|
||||
// CalculateStatistics calculates statistics from profit values
|
||||
func CalculateStatistics(profits []*big.Float) (total, average, max, min *big.Float) {
|
||||
if len(profits) == 0 {
|
||||
return big.NewFloat(0), big.NewFloat(0), big.NewFloat(0), big.NewFloat(0)
|
||||
}
|
||||
|
||||
total = big.NewFloat(0)
|
||||
max = new(big.Float).Set(profits[0])
|
||||
min = new(big.Float).Set(profits[0])
|
||||
|
||||
for _, profit := range profits {
|
||||
total.Add(total, profit)
|
||||
if profit.Cmp(max) > 0 {
|
||||
max = new(big.Float).Set(profit)
|
||||
}
|
||||
if profit.Cmp(min) < 0 {
|
||||
min = new(big.Float).Set(profit)
|
||||
}
|
||||
}
|
||||
|
||||
average = new(big.Float).Quo(total, big.NewFloat(float64(len(profits))))
|
||||
return total, average, max, min
|
||||
}
|
||||
|
||||
// ParseOpportunityDetails parses detailed opportunity information from multi-line log entries
|
||||
func (lp *LogParser) ParseOpportunityDetails(logFile string) ([]*OpportunityTestData, error) {
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var opportunities []*OpportunityTestData
|
||||
var currentOpp *OpportunityTestData
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Start of new opportunity
|
||||
if lp.opportunityPattern.MatchString(line) {
|
||||
if currentOpp != nil {
|
||||
opportunities = append(opportunities, currentOpp)
|
||||
}
|
||||
currentOpp = &OpportunityTestData{
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if currentOpp == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse various fields
|
||||
if strings.Contains(line, "Transaction:") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) > 1 {
|
||||
currentOpp.TransactionHash = strings.TrimSpace(parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
if matches := lp.profitPattern.FindStringSubmatch(line); matches != nil {
|
||||
profitStr := strings.ReplaceAll(matches[1], ",", "")
|
||||
profitUSD, _ := strconv.ParseFloat(profitStr, 64)
|
||||
currentOpp.NetProfitUSD = big.NewFloat(profitUSD)
|
||||
}
|
||||
|
||||
if strings.Contains(line, "netProfitETH:") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) > 1 {
|
||||
profitStr := strings.TrimSpace(parts[1])
|
||||
profit, _ := strconv.ParseFloat(profitStr, 64)
|
||||
currentOpp.NetProfitETH = big.NewFloat(profit)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(line, "isExecutable:") {
|
||||
currentOpp.IsExecutable = strings.Contains(line, "isExecutable:true")
|
||||
}
|
||||
|
||||
if strings.Contains(line, "rejectReason:") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) > 1 {
|
||||
currentOpp.RejectReason = strings.TrimSpace(parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't forget the last opportunity
|
||||
if currentOpp != nil {
|
||||
opportunities = append(opportunities, currentOpp)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error reading log file: %w", err)
|
||||
}
|
||||
|
||||
return opportunities, nil
|
||||
}
|
||||
Reference in New Issue
Block a user