feat: add API endpoints for positions, close, close-all, pnl, plus DB persistence before goroutine

This commit is contained in:
jackyu66git
2026-05-04 20:28:52 +08:00
parent 06a7e37836
commit 88462f9829
2 changed files with 157 additions and 0 deletions
+45
View File
@@ -337,6 +337,51 @@ func (t *Trader) Start() {
log.Println("[Trader] ▶ Trading RESUMED")
}
// ClosePosition closes a single position by coin name.
func (t *Trader) ClosePosition(coin string) error {
t.mu.Lock()
pos, ok := t.positions[coin]
t.mu.Unlock()
if !ok {
return fmt.Errorf("no open position for %s", coin)
}
if pos.Status != "open" && pos.Status != "close_failed" {
return fmt.Errorf("position %s is in status %s, cannot close", coin, pos.Status)
}
t.closeBothLegs(pos)
pos.Status = "closed"
pos.ExitedAt = time.Now()
t.mu.Lock()
delete(t.positions, coin)
t.mu.Unlock()
log.Printf("[Trader] Manually closed %s %s", coin, pos.Direction)
return nil
}
// CloseAllPositions closes every open position.
func (t *Trader) CloseAllPositions() int {
t.mu.Lock()
positions := make([]*ArbPosition, 0, len(t.positions))
for _, pos := range t.positions {
positions = append(positions, pos)
}
t.mu.Unlock()
count := 0
for _, pos := range positions {
if pos.Status == "open" || pos.Status == "close_failed" {
t.closeBothLegs(pos)
pos.Status = "closed"
pos.ExitedAt = time.Now()
t.mu.Lock()
delete(t.positions, pos.Coin)
t.mu.Unlock()
log.Printf("[Trader] Force-closed %s %s", pos.Coin, pos.Direction)
count++
}
}
return count
}
// Tick is called every scanner cycle — checks scaling and exit.
func (t *Trader) Tick(store *PriceStore, notifier *Notifier) {
if !t.IsConfigured() {