Files
TradingBot/cmd/backtest/main.go
2026-01-14 22:44:12 +01:00

97 lines
2.7 KiB
Go

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)
}