first commit of restructured project

This commit is contained in:
KS Jannette
2026-02-22 15:21:18 -05:00
commit 9fca234606
75 changed files with 8299 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
package config
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/joho/godotenv"
)
type Config struct {
// Secrets (from .env)
DuneAPIKey string
WalletAddress string
PrivateKey string
EthereumAPIEndpoint string
WebhookURL string
BotName string
APIKey string
CORSAllowOrigin string
// Database
DBHost string
DBPort int
DBName string
DBUser string
DBPassword string
// Blockchain
ChainID int
QuoteTokenAddress string
QuoteTokenSymbol string
QuoteTokenDecimals int
WETHAddress string
UniswapRouterAddress string
// Support/Resistance
SRMethod string
SRRefreshHours int
SRLookbackDays int
// Risk Management
MaxDailyTrades int
MaxPositionSizeUSD float64
StopLossPercent float64
TakeProfitPercent float64
// Paper Trading
PaperTradingEnabled bool
PaperInitialETH float64
PaperInitialUSDC float64
PaperSlippagePercent float64
PaperSimulateGas bool
// Grid Configuration
GridLevels int
GridSpacingPercent float64
GridBasePrice float64
AmountPerGrid float64
// Trading Parameters
SlippageTolerance float64
GasMultiplier float64
MinProfitPercent float64
GasLimit int
// Timing
PriceCheckIntervalSeconds int
StatusReportIntervalMinutes int
PostTradeCooldownSeconds int
}
func Load() (*Config, error) {
_ = godotenv.Load()
cfg := &Config{
// Secrets
DuneAPIKey: envStr("DUNE_API_KEY", ""),
WalletAddress: envStr("WALLET_ADDRESS", ""),
PrivateKey: envStr("PRIVATE_KEY", ""),
EthereumAPIEndpoint: envStr("ETHEREUM_API_ENDPOINT", ""),
WebhookURL: envStr("WEBHOOK_URL", ""),
BotName: envStr("BOT_NAME", "TrahnGridTrader"),
APIKey: envStr("API_KEY", ""),
CORSAllowOrigin: envStr("CORS_ALLOW_ORIGIN", "*"),
// Database
DBHost: envStr("DB_HOST", "localhost"),
DBPort: envInt("DB_PORT", 5432),
DBName: envStr("DB_NAME", "trahn_grid_trader"),
DBUser: envStr("DB_USER", ""),
DBPassword: envStr("DB_PASSWORD", ""),
// Blockchain
ChainID: envInt("CHAIN_ID", 1),
QuoteTokenAddress: envStr("QUOTE_TOKEN_ADDRESS", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
QuoteTokenSymbol: envStr("QUOTE_TOKEN_SYMBOL", "USDC"),
QuoteTokenDecimals: envInt("QUOTE_TOKEN_DECIMALS", 6),
WETHAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
UniswapRouterAddress: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
// Support/Resistance
SRMethod: envStr("SR_METHOD", "simple"),
SRRefreshHours: envInt("SR_REFRESH_HOURS", 48),
SRLookbackDays: envInt("SR_LOOKBACK_DAYS", 14),
// Risk Management
MaxDailyTrades: envInt("MAX_DAILY_TRADES", 50),
MaxPositionSizeUSD: envFloat("MAX_POSITION_SIZE_USD", 10000),
StopLossPercent: envFloat("STOP_LOSS_PERCENT", 0),
TakeProfitPercent: envFloat("TAKE_PROFIT_PERCENT", 0),
// Paper Trading
PaperTradingEnabled: envBool("PAPER_TRADING_ENABLED", true),
PaperInitialETH: envFloat("PAPER_INITIAL_ETH", 1.0),
PaperInitialUSDC: envFloat("PAPER_INITIAL_USDC", 1000),
PaperSlippagePercent: envFloat("PAPER_SLIPPAGE_PERCENT", 0.5),
PaperSimulateGas: envBool("PAPER_SIMULATE_GAS", true),
// Grid
GridLevels: envInt("GRID_LEVELS", 10),
GridSpacingPercent: envFloat("GRID_SPACING_PERCENT", 2),
GridBasePrice: envFloat("GRID_BASE_PRICE", 0),
AmountPerGrid: envFloat("AMOUNT_PER_GRID", 100),
// Trading Parameters
SlippageTolerance: envFloat("SLIPPAGE_TOLERANCE", 1.5),
GasMultiplier: envFloat("GAS_MULTIPLIER", 1.2),
MinProfitPercent: envFloat("MIN_PROFIT_PERCENT", 0.5),
GasLimit: envInt("GAS_LIMIT", 250000),
// Timing
PriceCheckIntervalSeconds: envInt("PRICE_CHECK_INTERVAL_SECONDS", 30),
StatusReportIntervalMinutes: envInt("STATUS_REPORT_INTERVAL_MINUTES", 60),
PostTradeCooldownSeconds: envInt("POST_TRADE_COOLDOWN_SECONDS", 60),
}
return cfg, nil
}
func (c *Config) Validate() error {
var errs []string
if c.WalletAddress == "" {
errs = append(errs, "WALLET_ADDRESS is required")
}
if !c.PaperTradingEnabled && c.PrivateKey == "" {
errs = append(errs, "PRIVATE_KEY is required for live trading")
}
if c.DuneAPIKey == "" {
fmt.Println("[WARN] DUNE_API_KEY not set — will use current price for grid center (fallback mode)")
}
if c.StopLossPercent == 0 && c.TakeProfitPercent == 0 {
fmt.Println("[WARN] STOP_LOSS_PERCENT and TAKE_PROFIT_PERCENT are both 0 — no portfolio circuit breakers active")
}
if c.MaxDailyTrades == 0 && c.MaxPositionSizeUSD == 0 {
fmt.Println("[WARN] MAX_DAILY_TRADES and MAX_POSITION_SIZE_USD are both 0 — no per-trade limits active")
}
if c.APIKey == "" {
fmt.Println("[WARN] API_KEY not set — REST API has no authentication")
}
if len(errs) > 0 {
return fmt.Errorf("config validation failed:\n %s", strings.Join(errs, "\n "))
}
return nil
}
func (c *Config) Print() {
fmt.Println("=== ETH Grid Trading Bot Configuration ===")
if c.PaperTradingEnabled {
fmt.Println("════════════════════════════════════════")
fmt.Println(" PAPER TRADING MODE ENABLED")
fmt.Println(" No real transactions will execute")
fmt.Println("════════════════════════════════════════")
fmt.Printf("Paper Initial ETH: %.4f\n", c.PaperInitialETH)
fmt.Printf("Paper Initial %s: %.2f\n", c.QuoteTokenSymbol, c.PaperInitialUSDC)
fmt.Printf("Paper Slippage: 0-%.1f%%\n", c.PaperSlippagePercent)
fmt.Printf("Paper Gas Simulation: %v\n", c.PaperSimulateGas)
} else {
fmt.Println(" LIVE TRADING MODE")
}
fmt.Println("--------------------------------------")
fmt.Printf("Chain ID: %d\n", c.ChainID)
if len(c.WalletAddress) > 16 {
fmt.Printf("Wallet: %s...%s\n", c.WalletAddress[:10], c.WalletAddress[len(c.WalletAddress)-6:])
}
fmt.Printf("Trading Pair: ETH/%s\n", c.QuoteTokenSymbol)
fmt.Printf("Quote Token: %s (%s...)\n", c.QuoteTokenSymbol, truncAddr(c.QuoteTokenAddress))
fmt.Println("--------------------------------------")
fmt.Println("Grid Configuration:")
fmt.Printf(" Levels: %d\n", c.GridLevels)
fmt.Printf(" Spacing: %.1f%%\n", c.GridSpacingPercent)
fmt.Printf(" Amount/Grid: $%.0f\n", c.AmountPerGrid)
fmt.Println("--------------------------------------")
fmt.Println("Support/Resistance Configuration:")
fmt.Printf(" S/R Method: %s\n", c.SRMethod)
fmt.Printf(" S/R Refresh: every %d hours\n", c.SRRefreshHours)
fmt.Printf(" S/R Lookback: %d days\n", c.SRLookbackDays)
fmt.Printf(" Dune API: %s\n", boolLabel(c.DuneAPIKey != "", "configured", "not set (fallback mode)"))
fmt.Println("======================================")
}
func (c *Config) DSN() string {
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable",
c.DBUser, c.DBPassword, c.DBHost, c.DBPort, c.DBName)
}
// --- helpers ---
func envStr(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
func envInt(key string, fallback int) int {
if v := os.Getenv(key); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return fallback
}
func envFloat(key string, fallback float64) float64 {
if v := os.Getenv(key); v != "" {
if f, err := strconv.ParseFloat(v, 64); err == nil {
return f
}
}
return fallback
}
func envBool(key string, fallback bool) bool {
if v := os.Getenv(key); v != "" {
v = strings.ToLower(v)
return v == "true" || v == "1" || v == "yes"
}
return fallback
}
func truncAddr(addr string) string {
if len(addr) > 10 {
return addr[:10]
}
return addr
}
func boolLabel(cond bool, ifTrue, ifFalse string) string {
if cond {
return ifTrue
}
return ifFalse
}