Files
2026-02-22 15:21:18 -05:00

109 lines
2.4 KiB
Go

package bot
import (
"context"
"fmt"
"sync"
"github.com/kjannette/trahn-backend/internal/config"
"github.com/kjannette/trahn-backend/internal/external"
"github.com/kjannette/trahn-backend/internal/notifications"
"github.com/kjannette/trahn-backend/internal/repository"
"github.com/kjannette/trahn-backend/internal/scheduler"
"github.com/kjannette/trahn-backend/internal/strategy"
)
type Service struct {
mu sync.Mutex
bot *GridBot
}
func NewService() *Service {
return &Service{}
}
func (s *Service) Start(ctx context.Context, cfg *config.Config,
priceRepo *repository.PriceRepo,
tradeRepo *repository.TradeRepo,
gridRepo *repository.GridStateRepo,
notify *notifications.Sender,
dune *external.DuneClient,
) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.bot != nil && s.bot.IsRunning() {
fmt.Println("[BOT] Already running")
return nil
}
mode := "LIVE MODE"
if cfg.PaperTradingEnabled {
mode = "PAPER MODE"
}
notify.Send(fmt.Sprintf("Starting ETH Grid Trader (ETH/%s) - %s", cfg.QuoteTokenSymbol, mode))
b := NewGridBot(cfg, priceRepo, tradeRepo, gridRepo, notify, dune)
if err := b.Init(ctx); err != nil {
return fmt.Errorf("bot init: %w", err)
}
s.bot = b
fmt.Println("[BOT] Grid trading bot initialized")
fmt.Println("[BOT] State loaded from database")
go func() {
b.Run(ctx)
fmt.Println("[BOT] Run loop exited")
}()
fmt.Println("[BOT] Started successfully")
return nil
}
func (s *Service) Stop() {
s.mu.Lock()
defer s.mu.Unlock()
if s.bot != nil {
s.bot.Shutdown()
s.bot = nil
}
fmt.Println("[BOT] Stopped")
}
// BotState returns the current bot state for the scheduler's decision-making.
func (s *Service) BotState() *scheduler.BotState {
s.mu.Lock()
defer s.mu.Unlock()
if s.bot == nil || len(s.bot.Grid) == 0 {
return nil
}
// Copy grid to avoid data races
grid := make([]strategy.GridLevel, len(s.bot.Grid))
copy(grid, s.bot.Grid)
return &scheduler.BotState{
Grid: grid,
LastETHPrice: s.bot.LastETHPrice,
}
}
// InitializeGrid triggers a grid recalculation (called by scheduler).
func (s *Service) InitializeGrid(ctx context.Context) {
s.mu.Lock()
b := s.bot
s.mu.Unlock()
if b == nil {
return
}
fmt.Println("[BOT] Recalculating grid with new S/R midpoint...")
b.BasePrice = 0 // force recalculation from S/R
if err := b.InitializeGrid(ctx); err != nil {
fmt.Printf("[BOT] Grid recalculation failed: %v\n", err)
}
}