feat: Alpaca US stock trader integration (Tasks 8-11)

- trader/alpaca/: Full Trader interface implementation for Alpaca
  - Paper/Live trading support (market orders, stop/limit)
  - GetBalance, GetPositions, GetMarketPrice via Alpaca API
  - Fractional share support (4 decimal places)
  - Commission-free trading

- Agent trade routing: stock symbols (AAPL, TSLA) → Alpaca
  - isStockSymbol() heuristic to distinguish crypto vs stock
  - execute_trade tool updated for stock buy/sell semantics
  - get_market_price works for both crypto and stocks

- TraderManager + API: Alpaca registered as exchange type
  - Factory case in auto_trader.go
  - Config mapping in trader_manager.go
  - Temp trader creation in handler_trader.go for balance query

- Frontend: PositionsPanel distinguishes stock vs crypto
  - US flag emoji for stock positions
  - Dollar prefix for stock PnL/prices
  - 'Shares' label instead of 'Qty' for stocks

- System prompts updated for stock trading capabilities
This commit is contained in:
shinchan-zhai
2026-03-23 17:57:14 +08:00
parent 76a7b88ba8
commit d0aebe9f18
9 changed files with 720 additions and 17 deletions

View File

@@ -153,6 +153,9 @@ func (a *Agent) executeTrade(ctx context.Context, trade *TradeAction) error {
return fmt.Errorf("no traders configured")
}
// Determine if this is a stock trade to route to the right exchange
wantStock := isStockSymbol(trade.Symbol)
// Find a running trader's underlying exchange interface
var underlyingTrader interface {
OpenLong(symbol string, quantity float64, leverage int) (map[string]interface{}, error)
@@ -169,12 +172,24 @@ func (a *Agent) executeTrade(ctx context.Context, trade *TradeAction) error {
if ut == nil {
continue
}
// Route stock symbols to alpaca traders, crypto to others
exchange := t.GetExchange()
isAlpaca := exchange == "alpaca"
if wantStock && !isAlpaca {
continue // Skip non-stock traders for stock symbols
}
if !wantStock && isAlpaca {
continue // Skip stock traders for crypto symbols
}
underlyingTrader = ut
break
}
}
if underlyingTrader == nil {
if wantStock {
return fmt.Errorf("no running stock trader (Alpaca) found — configure one to trade stocks")
}
return fmt.Errorf("no running trader supports trade execution")
}