Files
zeekay f897cd1f9c feat: complete Alpaca Broker API — 100% endpoint coverage
Security fixes:
- Fix auth bypass: empty API key now returns 401 (unless BROKER_DEV_MODE=true)
- Fix risk engine recording $0 for all orders (now calculates qty*price)
- Fix hardcoded compliance fields in CreateAccount (pass-through from request)

P0 Trading Core:
- ReplaceOrder (PATCH), CancelAllOrders, GetPosition, ClosePosition, CloseAllPositions
- ListOrdersFiltered with status/limit/after/until/direction/nested params
- Extended CreateOrderRequest: trailing stops, bracket orders, extended hours

P1-P5 Complete API via composable interfaces:
- AccountManager: UpdateAccount, CloseAccount, GetAccountActivities
- DocumentManager: Upload, List, Get, Download documents
- JournalManager: Create, List, Get, Delete, Batch, ReverseBatch journals
- TransferExtended: CancelTransfer, DeleteACH, recipient bank CRUD
- CryptoDataProvider: GetCryptoBars/Quotes/Trades/Snapshots
- EventStreamer: SSE streaming for trade/account/transfer/journal events
- PortfolioAnalyzer: GetPortfolioHistory
- WatchlistManager: Full CRUD with asset management

Architecture: 8 optional capability interfaces on single Provider abstraction.
Providers implement what they support; router detects via type assertion.

Market data fixes:
- Crypto symbols now route to /v1beta3/crypto/us/ (not /v2/stocks/)
- GetBars pagination via next_page_token loop
2025-10-22 10:40:22 -07:00

60 lines
1.4 KiB
Go

package alpaca
import (
"context"
"encoding/json"
"net/http"
"github.com/luxfi/broker/pkg/types"
)
// --- PortfolioAnalyzer implementation ---
func (p *Provider) GetPortfolioHistory(ctx context.Context, accountID string, params *types.HistoryParams) (*types.PortfolioHistory, error) {
path := "/v1/trading/accounts/" + accountID + "/account/portfolio/history"
sep := "?"
if params != nil {
if params.Period != "" {
path += sep + "period=" + params.Period
sep = "&"
}
if params.Timeframe != "" {
path += sep + "timeframe=" + params.Timeframe
sep = "&"
}
if params.DateEnd != "" {
path += sep + "date_end=" + params.DateEnd
sep = "&"
}
if params.ExtendedHours {
path += sep + "extended_hours=true"
}
}
data, _, err := p.do(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, err
}
var raw struct {
Timestamp []int64 `json:"timestamp"`
Equity []float64 `json:"equity"`
ProfitLoss []float64 `json:"profit_loss"`
ProfitLossPct []float64 `json:"profit_loss_pct"`
BaseValue float64 `json:"base_value"`
Timeframe string `json:"timeframe"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
return &types.PortfolioHistory{
Timestamp: raw.Timestamp,
Equity: raw.Equity,
ProfitLoss: raw.ProfitLoss,
ProfitLossPct: raw.ProfitLossPct,
BaseValue: raw.BaseValue,
Timeframe: raw.Timeframe,
}, nil
}