mirror of
https://github.com/luxfi/broker.git
synced 2026-09-24 00:25:18 +00:00
C1: queryCountryInfos endpoint typo (-1 suffix removed) C2: ValidateTIF wired into CreateOrder (was dead code) C3: GetFixedIncomeQuote returns honest error (Moment ToS required) C4: Agreement signing uses real client IP (was 0.0.0.0) C5: Overnight trading uses America/New_York timezone (was UTC-4) H1: Background asset sync goroutine (1-hour ticker) H2: ACATS disclosure API endpoint H3: Crypto $200K aggregate limit on split plans
195 lines
9.3 KiB
Go
195 lines
9.3 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/luxfi/broker/pkg/types"
|
|
)
|
|
|
|
// TradingExtended is an optional interface for providers that support
|
|
// advanced trading operations beyond the base Provider interface.
|
|
type TradingExtended interface {
|
|
ReplaceOrder(ctx context.Context, accountID, orderID string, req *types.ReplaceOrderRequest) (*types.Order, error)
|
|
CancelAllOrders(ctx context.Context, accountID string) error
|
|
GetPosition(ctx context.Context, accountID, symbol string) (*types.Position, error)
|
|
ClosePosition(ctx context.Context, accountID, symbol string, qty *float64) (*types.Order, error)
|
|
CloseAllPositions(ctx context.Context, accountID string) ([]*types.Order, error)
|
|
ListOrdersFiltered(ctx context.Context, accountID string, params *types.ListOrdersParams) ([]*types.Order, error)
|
|
}
|
|
|
|
// AccountManager is an optional interface for account lifecycle management.
|
|
type AccountManager interface {
|
|
UpdateAccount(ctx context.Context, accountID string, req *types.UpdateAccountRequest) (*types.Account, error)
|
|
CloseAccount(ctx context.Context, accountID string) error
|
|
GetAccountActivities(ctx context.Context, accountID string, params *types.ActivityParams) ([]*types.Activity, error)
|
|
}
|
|
|
|
// DocumentManager is an optional interface for document upload/retrieval.
|
|
type DocumentManager interface {
|
|
UploadDocument(ctx context.Context, accountID string, doc *types.DocumentUpload) (*types.Document, error)
|
|
ListDocuments(ctx context.Context, accountID string, params *types.DocumentParams) ([]*types.Document, error)
|
|
GetDocument(ctx context.Context, accountID, documentID string) (*types.Document, error)
|
|
DownloadDocument(ctx context.Context, accountID, documentID string) ([]byte, string, error)
|
|
}
|
|
|
|
// JournalManager is an optional interface for inter-account journal transfers.
|
|
type JournalManager interface {
|
|
CreateJournal(ctx context.Context, req *types.CreateJournalRequest) (*types.Journal, error)
|
|
ListJournals(ctx context.Context, params *types.JournalParams) ([]*types.Journal, error)
|
|
GetJournal(ctx context.Context, journalID string) (*types.Journal, error)
|
|
DeleteJournal(ctx context.Context, journalID string) error
|
|
CreateBatchJournal(ctx context.Context, req *types.BatchJournalRequest) ([]*types.Journal, error)
|
|
ReverseBatchJournal(ctx context.Context, req *types.ReverseBatchJournalRequest) ([]*types.Journal, error)
|
|
}
|
|
|
|
// TransferExtended is an optional interface for extended transfer operations.
|
|
type TransferExtended interface {
|
|
CancelTransfer(ctx context.Context, accountID, transferID string) error
|
|
DeleteACHRelationship(ctx context.Context, accountID, achID string) error
|
|
CreateRecipientBank(ctx context.Context, accountID string, req *types.CreateBankRequest) (*types.RecipientBank, error)
|
|
ListRecipientBanks(ctx context.Context, accountID string) ([]*types.RecipientBank, error)
|
|
DeleteRecipientBank(ctx context.Context, accountID, bankID string) error
|
|
}
|
|
|
|
// ACATSManager is an optional interface for ACATS (Automated Customer Account Transfer Service) operations.
|
|
type ACATSManager interface {
|
|
CreateACATSTransfer(ctx context.Context, accountID string, req *types.CreateACATSTransferRequest) (*types.ACATSTransfer, error)
|
|
GetACATSTransfer(ctx context.Context, accountID, transferID string) (*types.ACATSTransfer, error)
|
|
ListACATSTransfers(ctx context.Context, accountID string) ([]*types.ACATSTransfer, error)
|
|
CancelACATSTransfer(ctx context.Context, accountID, transferID string) error
|
|
}
|
|
|
|
// FixedIncomeTrader is an optional interface for fixed income order execution,
|
|
// positions, and asset listing (corporate bonds, treasuries).
|
|
type FixedIncomeTrader interface {
|
|
ListCorporateBonds(ctx context.Context) ([]*types.Asset, error)
|
|
ListTreasuryBonds(ctx context.Context) ([]*types.Asset, error)
|
|
CreateFixedIncomeOrder(ctx context.Context, accountID string, req *types.CreateOrderRequest) (*types.Order, error)
|
|
GetFixedIncomeOrder(ctx context.Context, accountID, orderID string) (*types.Order, error)
|
|
CancelFixedIncomeOrder(ctx context.Context, accountID, orderID string) error
|
|
GetFixedIncomeActivities(ctx context.Context, accountID string) ([]*types.Activity, error)
|
|
GetFixedIncomePositions(ctx context.Context, accountID string) ([]*types.Position, error)
|
|
CloseFixedIncomePosition(ctx context.Context, accountID, symbol string, qty *float64) (*types.Order, error)
|
|
}
|
|
|
|
// CryptoDataProvider is an optional interface for crypto-specific market data.
|
|
type CryptoDataProvider interface {
|
|
GetCryptoBars(ctx context.Context, req *types.CryptoBarsRequest) (*types.BarsResponse, error)
|
|
GetCryptoQuotes(ctx context.Context, req *types.CryptoQuotesRequest) (*types.QuotesResponse, error)
|
|
GetCryptoTrades(ctx context.Context, req *types.CryptoTradesRequest) (*types.TradesResponse, error)
|
|
GetCryptoSnapshots(ctx context.Context, symbols []string) (map[string]*types.CryptoSnapshot, error)
|
|
}
|
|
|
|
// EventStreamer is an optional interface for server-sent event streaming.
|
|
type EventStreamer interface {
|
|
StreamTradeEvents(ctx context.Context, since string) (<-chan *types.TradeEvent, error)
|
|
StreamAccountEvents(ctx context.Context, since string) (<-chan *types.AccountEvent, error)
|
|
StreamTransferEvents(ctx context.Context, since string) (<-chan *types.TransferEvent, error)
|
|
StreamJournalEvents(ctx context.Context, since string) (<-chan *types.JournalEvent, error)
|
|
}
|
|
|
|
// PortfolioAnalyzer is an optional interface for portfolio history.
|
|
type PortfolioAnalyzer interface {
|
|
GetPortfolioHistory(ctx context.Context, accountID string, params *types.HistoryParams) (*types.PortfolioHistory, error)
|
|
}
|
|
|
|
// WatchlistManager is an optional interface for watchlist operations.
|
|
type WatchlistManager interface {
|
|
CreateWatchlist(ctx context.Context, accountID string, req *types.CreateWatchlistRequest) (*types.Watchlist, error)
|
|
ListWatchlists(ctx context.Context, accountID string) ([]*types.Watchlist, error)
|
|
GetWatchlist(ctx context.Context, accountID, watchlistID string) (*types.Watchlist, error)
|
|
UpdateWatchlist(ctx context.Context, accountID, watchlistID string, req *types.UpdateWatchlistRequest) (*types.Watchlist, error)
|
|
DeleteWatchlist(ctx context.Context, accountID, watchlistID string) error
|
|
AddWatchlistAsset(ctx context.Context, accountID, watchlistID, symbol string) (*types.Watchlist, error)
|
|
RemoveWatchlistAsset(ctx context.Context, accountID, watchlistID, symbol string) error
|
|
}
|
|
|
|
// Provider is the unified interface every broker backend must implement.
|
|
type Provider interface {
|
|
// Name returns the provider identifier (e.g. "alpaca", "ibkr").
|
|
Name() string
|
|
|
|
// Accounts
|
|
CreateAccount(ctx context.Context, req *types.CreateAccountRequest) (*types.Account, error)
|
|
GetAccount(ctx context.Context, providerAccountID string) (*types.Account, error)
|
|
ListAccounts(ctx context.Context) ([]*types.Account, error)
|
|
|
|
// Portfolio & Positions
|
|
GetPortfolio(ctx context.Context, providerAccountID string) (*types.Portfolio, error)
|
|
|
|
// Orders
|
|
CreateOrder(ctx context.Context, providerAccountID string, req *types.CreateOrderRequest) (*types.Order, error)
|
|
ListOrders(ctx context.Context, providerAccountID string) ([]*types.Order, error)
|
|
GetOrder(ctx context.Context, providerAccountID, providerOrderID string) (*types.Order, error)
|
|
CancelOrder(ctx context.Context, providerAccountID, providerOrderID string) error
|
|
|
|
// Transfers
|
|
CreateTransfer(ctx context.Context, providerAccountID string, req *types.CreateTransferRequest) (*types.Transfer, error)
|
|
ListTransfers(ctx context.Context, providerAccountID string) ([]*types.Transfer, error)
|
|
|
|
// Bank Relationships
|
|
CreateBankRelationship(ctx context.Context, providerAccountID string, ownerName, accountType, accountNumber, routingNumber string) (*types.BankRelationship, error)
|
|
ListBankRelationships(ctx context.Context, providerAccountID string) ([]*types.BankRelationship, error)
|
|
|
|
// Assets
|
|
ListAssets(ctx context.Context, class string) ([]*types.Asset, error)
|
|
GetAsset(ctx context.Context, symbolOrID string) (*types.Asset, error)
|
|
|
|
// Market Data
|
|
GetSnapshot(ctx context.Context, symbol string) (*types.MarketSnapshot, error)
|
|
GetSnapshots(ctx context.Context, symbols []string) (map[string]*types.MarketSnapshot, error)
|
|
GetBars(ctx context.Context, symbol, timeframe, start, end string, limit int) ([]*types.Bar, error)
|
|
GetLatestTrades(ctx context.Context, symbols []string) (map[string]*types.Trade, error)
|
|
GetLatestQuotes(ctx context.Context, symbols []string) (map[string]*types.Quote, error)
|
|
GetClock(ctx context.Context) (*types.MarketClock, error)
|
|
GetCalendar(ctx context.Context, start, end string) ([]*types.MarketCalendarDay, error)
|
|
}
|
|
|
|
// Registry holds all registered providers.
|
|
type Registry struct {
|
|
mu sync.RWMutex
|
|
providers map[string]Provider
|
|
}
|
|
|
|
func NewRegistry() *Registry {
|
|
return &Registry{providers: make(map[string]Provider)}
|
|
}
|
|
|
|
func (r *Registry) Register(p Provider) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.providers[p.Name()] = p
|
|
}
|
|
|
|
func (r *Registry) Get(name string) (Provider, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
p, ok := r.providers[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("provider %q not registered", name)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func (r *Registry) Unregister(name string) bool {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if _, ok := r.providers[name]; !ok {
|
|
return false
|
|
}
|
|
delete(r.providers, name)
|
|
return true
|
|
}
|
|
|
|
func (r *Registry) List() []string {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
names := make([]string, 0, len(r.providers))
|
|
for n := range r.providers {
|
|
names = append(names, n)
|
|
}
|
|
return names
|
|
}
|