fix: Alpaca integration bugs — field mapping, market hours, stock symbol handling

- Fix critical bug: Alpaca GetBalance returns wrong field names (total_equity
  vs totalEquity) — auto_trader was getting 0 equity, breaking all decisions
- Fix critical bug: Alpaca GetPositions missing positionAmt/unRealizedProfit
  fields — positions invisible to trading AI
- Fix CancelAllOrders: was nuking ALL orders globally, now filters by symbol
- Implement GetClosedPnL: was returning nil, now returns filled sell orders
- Add IsMarketOpen: checks Alpaca clock endpoint for market hours
- Add market hours check in trading loop: skip cycles when US market closed
  (saves LLM tokens and prevents failed orders)
- Fix parseTradeCommand: 'BUY AAPL 10' no longer becomes 'AAPLUSDT'
- Fix toolGetMarketPrice: route stock symbols to Alpaca, crypto to others
- Add exchange field to toolGetPositions output for multi-exchange clarity
This commit is contained in:
shinchan-zhai
2026-03-23 19:05:25 +08:00
parent d0aebe9f18
commit bbaa109223
4 changed files with 138 additions and 20 deletions

View File

@@ -112,7 +112,8 @@ func parseTradeCommand(text string) *TradeAction {
return nil
}
symbol = words[1]
if !strings.HasSuffix(symbol, "USDT") {
// Only append USDT for crypto symbols, not stock tickers
if !isStockSymbol(symbol) && !strings.HasSuffix(symbol, "USDT") {
symbol += "USDT"
}
@@ -226,7 +227,10 @@ func formatTradeConfirmation(trade *TradeAction, lang string) string {
"close_short": "平空 (Close Short)",
}
symbol := strings.TrimSuffix(trade.Symbol, "USDT")
symbol := trade.Symbol
if strings.HasSuffix(symbol, "USDT") {
symbol = strings.TrimSuffix(symbol, "USDT")
}
actionName := actionNames[trade.Action]
if actionName == "" {
actionName = trade.Action
@@ -308,7 +312,10 @@ func (a *Agent) handleTradeConfirmation(ctx context.Context, userID int64, text,
}
trade.Status = "executed"
symbol := strings.TrimSuffix(trade.Symbol, "USDT")
symbol := trade.Symbol
if strings.HasSuffix(symbol, "USDT") {
symbol = strings.TrimSuffix(symbol, "USDT")
}
actionEmoji := "📈"
if strings.Contains(trade.Action, "short") {
actionEmoji = "📉"