package main import ( "flag" "os" "path/filepath" "time" "alpaca-bot/internal/alpaca" "alpaca-bot/internal/backtest" "alpaca-bot/internal/config" "alpaca-bot/internal/logger" "alpaca-bot/internal/strategy" "github.com/joho/godotenv" "github.com/xuri/excelize/v2" ) func main() { _ = godotenv.Load() log := logger.New("[BACKTEST]") cfg := config.LoadAlpaca() // CLI flags symbol := flag.String("symbol", "AAPL", "Stock symbol") tf := flag.String("tf", "1Day", "Timeframe") startStr := flag.String("start", "2023-01-01", "Start date") endStr := flag.String("end", "2026-01-01", "End date") cash := flag.Float64("cash", 10000, "Start capital") risk := flag.Float64("risk", 0.02, "Risk per trade (fraction)") atrPeriod := flag.Int("atr", 14, "ATR period") atrMult := flag.Float64("atrMult", 1.5, "ATR multiplier for stop") tpMult := flag.Float64("tpMult", 2.0, "Take profit multiplier") fast := flag.Int("fast", 12, "Fast EMA") slow := flag.Int("slow", 26, "Slow EMA") out := flag.String("out", "output/backtest.xlsx", "Excel output path") flag.Parse() start, err := time.Parse("2006-01-02", *startStr) if err != nil { log.Fatal("invalid start date:", err) } end, err := time.Parse("2006-01-02", *endStr) if err != nil { log.Fatal("invalid end date:", err) } // Create output folder if err := os.MkdirAll(filepath.Dir(*out), 0755); err != nil { log.Fatal(err) } // Load historical bars from Alpaca client := alpaca.NewClient(cfg) bars, err := client.GetHistoricalBars(*symbol, *tf, start, end) if err != nil { log.Fatal(err) } log.Printf("Loaded %d bars for %s", len(bars), *symbol) // Initialize EMA crossover strategy strat := strategy.NewEMACross(*fast, *slow) // Initialize backtest engine with ATR stops and TakeProfit engine := backtest.NewEngine(*symbol, strat, *cash, *risk, *atrPeriod, *atrMult, *tpMult) engine.Run(bars) log.Printf("Backtest finished | Trades: %d | End Cash: %.2f", len(engine.Trades), engine.Cash) // Export trades to Excel f := excelize.NewFile() sheet := "Backtest" f.NewSheet(sheet) headers := []string{"Time", "Symbol", "Side", "Price", "Qty", "Cash", "StopPrice", "TakeProfit"} for i, h := range headers { cell, _ := excelize.CoordinatesToCellName(i+1, 1) f.SetCellValue(sheet, cell, h) } for i, t := range engine.Trades { row := i + 2 values := []interface{}{t.Time.Format("2006-01-02 15:04:05"), t.Symbol, t.Side, t.Price, t.Qty, t.Cash, t.StopPrice, t.TakeProfit} for colIdx, val := range values { cell, _ := excelize.CoordinatesToCellName(colIdx+1, row) f.SetCellValue(sheet, cell, val) } } if err := f.SaveAs(*out); err != nil { log.Fatal("failed to save Excel:", err) } log.Printf("Backtest Excel saved to %s", *out) }