Documentation
¶
Overview ¶
Package realtime connects to the ProjectX Gateway SignalR hubs: the user hub (accounts, orders, positions, trades) and the market hub (quotes, market trades, market depth).
It implements the ASP.NET Core SignalR JSON hub protocol v1 over WebSockets directly, as described in section 11 of spec/SDK_BEHAVIOR.md: it skips negotiation, sends the token as the access_token query parameter and as a bearer header, pings after 15 seconds of client silence, treats 30 seconds of server silence as a lost connection, reconnects with delays of 0, 1, 2, 5 and 10 seconds and then every 30 seconds (fetching a fresh token for every attempt), and replays successful subscriptions after each reconnect. When the server rejects a token with HTTP 401 during the upgrade, the hub reports it through WithTokenRejected (wired to (*topstepx.Client).InvalidateToken by the ForClient constructors), so the next attempt logs in again instead of reusing the rejected token.
Usage ¶
client, _ := topstepx.NewClient(topstepx.WithCredentials(user, apiKey))
hub := realtime.NewUserHubForClient(client)
if err := hub.Connect(ctx); err != nil {
return err
}
defer hub.Close()
if err := hub.SubscribeOrders(ctx, accountID); err != nil {
return err
}
for ev := range hub.Events() {
switch ev := ev.(type) {
case *realtime.OrderEvent:
fmt.Println(ev.Order.ID, ev.Order.Status)
case *realtime.StateEvent:
fmt.Println("state:", ev.State, ev.Err)
}
}
Events are delivered on one buffered channel together with connection state events. Delivery never blocks the connection: a consumer that falls behind loses events (see WithOverflowPolicy), which Dropped counts and the logger reports. Payloads are decoded leniently; one that cannot be decoded arrives as an *UnknownEvent and the connection is unaffected.
Payload models follow the official realtime guide and are not assumed to match the REST models; every non-identifier field is optional because updates may be partial.
Index ¶
- Constants
- Variables
- type AccountEvent
- type ConnectionState
- type DepthEvent
- type DialError
- type DomType
- type Event
- type GatewayDepth
- type GatewayQuote
- type GatewayTrade
- type GatewayUserAccount
- type GatewayUserOrder
- type GatewayUserPosition
- type GatewayUserTrade
- type HandshakeError
- type HubError
- type MarketHub
- func (h *MarketHub) Close() error
- func (h *MarketHub) Connect(ctx context.Context) error
- func (h *MarketHub) Dropped() uint64
- func (h *MarketHub) Events() <-chan Event
- func (h *MarketHub) Invoke(ctx context.Context, method string, args ...any) (json.RawMessage, error)
- func (h *MarketHub) SubscribeContractMarketDepth(ctx context.Context, contractID string) error
- func (h *MarketHub) SubscribeContractQuotes(ctx context.Context, contractID string) error
- func (h *MarketHub) SubscribeContractTrades(ctx context.Context, contractID string) error
- func (h *MarketHub) UnsubscribeContractMarketDepth(ctx context.Context, contractID string) error
- func (h *MarketHub) UnsubscribeContractQuotes(ctx context.Context, contractID string) error
- func (h *MarketHub) UnsubscribeContractTrades(ctx context.Context, contractID string) error
- type MarketTradesEvent
- type Option
- func WithEventBuffer(n int) Option
- func WithHTTPClient(hc *http.Client) Option
- func WithHandshakeTimeout(d time.Duration) Option
- func WithInvocationTimeout(d time.Duration) Option
- func WithKeepAliveInterval(d time.Duration) Option
- func WithLogger(l *slog.Logger) Option
- func WithOverflowPolicy(p OverflowPolicy) Option
- func WithReadLimit(n int64) Option
- func WithReconnectPolicy(p ReconnectPolicy) Option
- func WithServerTimeout(d time.Duration) Option
- func WithTokenRejected(f func(token string)) Option
- func WithURL(hubURL string) Option
- func WithoutReconnect() Option
- type OrderEvent
- type OverflowPolicy
- type PositionEvent
- type QuoteEvent
- type ReconnectPolicy
- type ServerCloseError
- type StateEvent
- type SubscriptionErrorEvent
- type TokenProvider
- type TradeLogType
- type UnknownEvent
- type UserHub
- func (h *UserHub) Close() error
- func (h *UserHub) Connect(ctx context.Context) error
- func (h *UserHub) Dropped() uint64
- func (h *UserHub) Events() <-chan Event
- func (h *UserHub) Invoke(ctx context.Context, method string, args ...any) (json.RawMessage, error)
- func (h *UserHub) SubscribeAccounts(ctx context.Context) error
- func (h *UserHub) SubscribeOrders(ctx context.Context, accountID int32) error
- func (h *UserHub) SubscribePositions(ctx context.Context, accountID int32) error
- func (h *UserHub) SubscribeTrades(ctx context.Context, accountID int32) error
- func (h *UserHub) UnsubscribeAccounts(ctx context.Context) error
- func (h *UserHub) UnsubscribeOrders(ctx context.Context, accountID int32) error
- func (h *UserHub) UnsubscribePositions(ctx context.Context, accountID int32) error
- func (h *UserHub) UnsubscribeTrades(ctx context.Context, accountID int32) error
- type UserTradeEvent
Examples ¶
Constants ¶
const ( EventGatewayQuote = "GatewayQuote" EventGatewayTrade = "GatewayTrade" EventGatewayDepth = "GatewayDepth" )
Market hub server events.
const ( // DefaultKeepAliveInterval is how long the client may stay silent before // it sends a ping. DefaultKeepAliveInterval = 15 * time.Second // DefaultServerTimeout is how long the server may stay silent before the // connection counts as lost. DefaultServerTimeout = 30 * time.Second // DefaultHandshakeTimeout bounds the WebSocket dial and SignalR handshake. DefaultHandshakeTimeout = 15 * time.Second // DefaultInvocationTimeout bounds each hub method invocation. DefaultInvocationTimeout = 30 * time.Second // DefaultEventBuffer is the capacity of the Events channel. DefaultEventBuffer = 1024 // DefaultReadLimit is the largest accepted WebSocket message, in bytes. DefaultReadLimit = 16 << 20 // UnlimitedAttempts is the ReconnectPolicy.MaxAttempts value for // unlimited reconnect attempts. UnlimitedAttempts = -1 )
Defaults from the cross-SDK contract (section 11).
const ( EventGatewayUserAccount = "GatewayUserAccount" EventGatewayUserOrder = "GatewayUserOrder" EventGatewayUserPosition = "GatewayUserPosition" EventGatewayUserTrade = "GatewayUserTrade" )
User hub server events.
Variables ¶
var ( // ErrNotConnected is returned by invocations while the hub is not // connected, including while it reconnects. They fail fast rather than // wait. ErrNotConnected = errors.New("realtime: not connected") // ErrConnectionLost is returned by invocations pending when the // connection dropped, and wraps the cause reported in StateReconnecting // events. ErrConnectionLost = errors.New("realtime: connection lost") // ErrClosed is returned after Close, and by invocations that Close // interrupted. ErrClosed = errors.New("realtime: hub closed") // ErrAlreadyConnected is returned by Connect on a hub that is connected // or connecting. ErrAlreadyConnected = errors.New("realtime: already connected") // ErrInvocationTimeout is returned when no completion arrives within the // invocation timeout (30 s by default). ErrInvocationTimeout = errors.New("realtime: invocation timed out") // ErrServerTimeout is the connection-loss cause when nothing arrives from // the server within the server timeout (30 s by default). ErrServerTimeout = errors.New("realtime: server timeout: no message received") // rejected with HTTP 401. It is the same value as topstepx.ErrUnauthorized, // so either name matches. ErrUnauthorized = topstepx.ErrUnauthorized )
Sentinel errors, for use with errors.Is.
Functions ¶
This section is empty.
Types ¶
type AccountEvent ¶
type AccountEvent struct {
// Action is the envelope's action code when the payload was wrapped as
// {"action": n, "data": {...}}. Its meaning is undocumented.
Action *int32
// Account is the decoded payload.
Account GatewayUserAccount
// Raw is the payload JSON, including undocumented members.
Raw json.RawMessage
}
AccountEvent is a GatewayUserAccount update.
type ConnectionState ¶
type ConnectionState int
ConnectionState is the kind of a StateEvent.
const ( // StateConnected follows a successful Connect. StateConnected ConnectionState = iota + 1 // StateReconnecting precedes each reconnect attempt. StateReconnecting // StateReconnected follows a successful reconnect and the replay of the // subscription registry. StateReconnected // StateClosed is the last event before the channel closes. StateClosed )
ConnectionState values.
func (ConnectionState) String ¶
func (s ConnectionState) String() string
String returns the state's name.
type DepthEvent ¶
type DepthEvent struct {
// ContractID is the contract the entries belong to.
ContractID string
// Entries are the depth entries, normalized to a list whether the server
// sent one object or an array.
Entries []GatewayDepth
// Raw is the payload JSON.
Raw json.RawMessage
}
DepthEvent carries the market-depth entries of one GatewayDepth message.
type DialError ¶
type DialError struct {
// StatusCode is the HTTP status of a rejected upgrade request, or 0.
StatusCode int
// contains filtered or unexported fields
}
DialError is a failed WebSocket connection attempt. Its message never contains the access token. An upgrade rejected with HTTP 401 matches ErrUnauthorized; the rejected token has then been reported through WithTokenRejected.
type DomType ¶
type DomType int32
DomType is the type of a market-depth entry. Unrecognized values are kept.
const ( DomTypeUnknown DomType = 0 DomTypeAsk DomType = 1 DomTypeBid DomType = 2 DomTypeBestAsk DomType = 3 DomTypeBestBid DomType = 4 DomTypeTrade DomType = 5 DomTypeReset DomType = 6 DomTypeLow DomType = 7 DomTypeHigh DomType = 8 DomTypeNewBestBid DomType = 9 DomTypeNewBestAsk DomType = 10 DomTypeFill DomType = 11 )
DomType values.
type Event ¶
type Event interface {
// contains filtered or unexported methods
}
Event is delivered on a hub's Events channel. Use a type switch on the concrete types: *StateEvent, *AccountEvent, *OrderEvent, *PositionEvent, *UserTradeEvent, *QuoteEvent, *MarketTradesEvent, *DepthEvent, *SubscriptionErrorEvent and *UnknownEvent.
type GatewayDepth ¶
type GatewayDepth struct {
// Timestamp is the entry timestamp.
Timestamp *time.Time `json:"timestamp,omitempty"`
// Type is the depth entry type.
Type *DomType `json:"type,omitempty"`
// Price is the price level.
Price *decimal.Decimal `json:"price,omitempty"`
// Volume is the volume at the level, decoded leniently.
Volume *int64 `json:"volume,omitempty" jsonx:"lenient"`
// CurrentVolume is the current volume at the level, decoded leniently.
CurrentVolume *int64 `json:"currentVolume,omitempty" jsonx:"lenient"`
}
GatewayDepth is one market-depth entry of GatewayDepth events.
func (GatewayDepth) MarshalJSON ¶
func (m GatewayDepth) MarshalJSON() ([]byte, error)
MarshalJSON encodes the entry with decimals as JSON numbers.
func (*GatewayDepth) UnmarshalJSON ¶
func (m *GatewayDepth) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the entry, reading timestamps and volumes leniently.
type GatewayQuote ¶
type GatewayQuote struct {
// Symbol is the symbol identifier.
Symbol string `json:"symbol"`
// SymbolName is the symbol's display name.
SymbolName *string `json:"symbolName,omitempty"`
// LastPrice is the last traded price.
LastPrice *decimal.Decimal `json:"lastPrice,omitempty"`
// BestBid is the best bid price.
BestBid *decimal.Decimal `json:"bestBid,omitempty"`
// BestAsk is the best ask price.
BestAsk *decimal.Decimal `json:"bestAsk,omitempty"`
// Change is the price change of the session.
Change *decimal.Decimal `json:"change,omitempty"`
// ChangePercent is the percentage price change of the session.
ChangePercent *decimal.Decimal `json:"changePercent,omitempty"`
// Open is the session's opening price.
Open *decimal.Decimal `json:"open,omitempty"`
// High is the session's highest price.
High *decimal.Decimal `json:"high,omitempty"`
// Low is the session's lowest price.
Low *decimal.Decimal `json:"low,omitempty"`
// Volume is the session volume, decoded leniently.
Volume *int64 `json:"volume,omitempty" jsonx:"lenient"`
// LastUpdated is when the quote last changed.
LastUpdated *time.Time `json:"lastUpdated,omitempty"`
// Timestamp is the quote timestamp.
Timestamp *time.Time `json:"timestamp,omitempty"`
}
GatewayQuote is the payload of GatewayQuote events. Updates may carry only the fields that changed.
func (GatewayQuote) MarshalJSON ¶
func (m GatewayQuote) MarshalJSON() ([]byte, error)
MarshalJSON encodes the payload with decimals as JSON numbers.
func (*GatewayQuote) UnmarshalJSON ¶
func (m *GatewayQuote) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the payload, reading timestamps and volumes leniently.
type GatewayTrade ¶
type GatewayTrade struct {
// SymbolID is the symbol identifier.
SymbolID string `json:"symbolId"`
// Price is the trade price.
Price *decimal.Decimal `json:"price,omitempty"`
// Timestamp is the trade timestamp.
Timestamp *time.Time `json:"timestamp,omitempty"`
// Type is the aggressor side.
Type *TradeLogType `json:"type,omitempty"`
// Volume is the traded volume, decoded leniently.
Volume *int64 `json:"volume,omitempty" jsonx:"lenient"`
}
GatewayTrade is one market trade of GatewayTrade events.
func (GatewayTrade) MarshalJSON ¶
func (m GatewayTrade) MarshalJSON() ([]byte, error)
MarshalJSON encodes the trade with decimals as JSON numbers.
func (*GatewayTrade) UnmarshalJSON ¶
func (m *GatewayTrade) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the trade, reading timestamps and volumes leniently.
type GatewayUserAccount ¶
type GatewayUserAccount struct {
// ID is the account identifier.
ID int32 `json:"id"`
// Name is the account name.
Name *string `json:"name,omitempty"`
// Balance is the account balance.
Balance *decimal.Decimal `json:"balance,omitempty"`
// CanTrade reports whether the account may trade.
CanTrade *bool `json:"canTrade,omitempty"`
// IsVisible reports whether the account is visible.
IsVisible *bool `json:"isVisible,omitempty"`
// Simulated reports whether the account is simulated.
Simulated *bool `json:"simulated,omitempty"`
}
GatewayUserAccount is the payload of GatewayUserAccount events.
func (GatewayUserAccount) MarshalJSON ¶
func (m GatewayUserAccount) MarshalJSON() ([]byte, error)
MarshalJSON encodes the payload with decimals as JSON numbers.
func (*GatewayUserAccount) UnmarshalJSON ¶
func (m *GatewayUserAccount) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the payload.
type GatewayUserOrder ¶
type GatewayUserOrder struct {
// ID is the order identifier.
ID int64 `json:"id"`
// AccountID is the owning account.
AccountID int32 `json:"accountId"`
// ContractID is the contract identifier.
ContractID string `json:"contractId"`
// SymbolID is the symbol identifier.
SymbolID string `json:"symbolId"`
// CreationTimestamp is when the order was created.
CreationTimestamp *time.Time `json:"creationTimestamp,omitempty"`
// UpdateTimestamp is when the order last changed.
UpdateTimestamp *time.Time `json:"updateTimestamp,omitempty"`
// Status is the order status.
Status *topstepx.OrderStatus `json:"status,omitempty"`
// Type is the order type.
Type *topstepx.OrderType `json:"type,omitempty"`
// Side is the order side.
Side *topstepx.OrderSide `json:"side,omitempty"`
// Size is the order quantity.
Size *int32 `json:"size,omitempty"`
// LimitPrice is the limit price.
LimitPrice *decimal.Decimal `json:"limitPrice,omitempty"`
// StopPrice is the stop price.
StopPrice *decimal.Decimal `json:"stopPrice,omitempty"`
// FillVolume is the filled quantity, decoded leniently.
FillVolume *int32 `json:"fillVolume,omitempty" jsonx:"lenient"`
// FilledPrice is the fill price.
FilledPrice *decimal.Decimal `json:"filledPrice,omitempty"`
// CustomTag is the caller-supplied tag.
CustomTag *string `json:"customTag,omitempty"`
}
GatewayUserOrder is the payload of GatewayUserOrder events.
func (GatewayUserOrder) MarshalJSON ¶
func (m GatewayUserOrder) MarshalJSON() ([]byte, error)
MarshalJSON encodes the payload with decimals as JSON numbers.
func (*GatewayUserOrder) UnmarshalJSON ¶
func (m *GatewayUserOrder) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the payload, reading timestamps and volumes leniently.
type GatewayUserPosition ¶
type GatewayUserPosition struct {
// ID is the position identifier.
ID int32 `json:"id"`
// AccountID is the owning account.
AccountID int32 `json:"accountId"`
// ContractID is the contract identifier.
ContractID string `json:"contractId"`
// CreationTimestamp is when the position was opened.
CreationTimestamp *time.Time `json:"creationTimestamp,omitempty"`
// Type is the position direction.
Type *topstepx.PositionType `json:"type,omitempty"`
// Size is the position quantity.
Size *int32 `json:"size,omitempty"`
// AveragePrice is the average entry price.
AveragePrice *decimal.Decimal `json:"averagePrice,omitempty"`
}
GatewayUserPosition is the payload of GatewayUserPosition events.
func (GatewayUserPosition) MarshalJSON ¶
func (m GatewayUserPosition) MarshalJSON() ([]byte, error)
MarshalJSON encodes the payload with decimals as JSON numbers.
func (*GatewayUserPosition) UnmarshalJSON ¶
func (m *GatewayUserPosition) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the payload, reading timestamps leniently.
type GatewayUserTrade ¶
type GatewayUserTrade struct {
// ID is the trade identifier.
ID int64 `json:"id"`
// AccountID is the owning account.
AccountID int32 `json:"accountId"`
// ContractID is the contract identifier.
ContractID string `json:"contractId"`
// CreationTimestamp is when the trade happened.
CreationTimestamp *time.Time `json:"creationTimestamp,omitempty"`
// Price is the execution price.
Price *decimal.Decimal `json:"price,omitempty"`
// ProfitAndLoss is the realized profit and loss; nil for a half-turn.
ProfitAndLoss *decimal.Decimal `json:"profitAndLoss,omitempty"`
// Fees are the trade fees.
Fees *decimal.Decimal `json:"fees,omitempty"`
// Side is the trade side.
Side *topstepx.OrderSide `json:"side,omitempty"`
// Size is the traded quantity.
Size *int32 `json:"size,omitempty"`
// Voided reports whether the trade was voided.
Voided *bool `json:"voided,omitempty"`
// OrderID is the order that produced the trade.
OrderID int64 `json:"orderId"`
}
GatewayUserTrade is the payload of GatewayUserTrade events.
func (GatewayUserTrade) MarshalJSON ¶
func (m GatewayUserTrade) MarshalJSON() ([]byte, error)
MarshalJSON encodes the payload with decimals as JSON numbers.
func (*GatewayUserTrade) UnmarshalJSON ¶
func (m *GatewayUserTrade) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the payload, reading timestamps leniently.
type HandshakeError ¶
type HandshakeError struct {
// Message is the server's error text or the reason the handshake failed.
Message string
}
HandshakeError is a SignalR handshake that the server rejected or that did not complete.
type HubError ¶
type HubError struct {
// Method is the invoked hub method.
Method string
// Message is the server's error text.
Message string
}
HubError is an invocation the server completed with an error.
type MarketHub ¶
type MarketHub struct {
// contains filtered or unexported fields
}
MarketHub is a connection to the SignalR market hub, which streams quotes, market trades and market depth per contract. It is safe for concurrent use.
Create it with NewMarketHub or NewMarketHubForClient, read Events, call Connect, then subscribe. Subscriptions are replayed after every reconnect.
func NewMarketHub ¶
func NewMarketHub(tokens TokenProvider, opts ...Option) *MarketHub
NewMarketHub returns an unconnected market hub. tokens is called before every connection attempt; pass (*topstepx.Client).Token. The URL defaults to topstepx.DefaultMarketHubURL.
Example ¶
package main
import (
"context"
"log"
topstepx "github.com/CurtisTechSolutions/TopstepX/go"
"github.com/CurtisTechSolutions/TopstepX/go/realtime"
)
func main() {
client, _ := topstepx.NewClient(topstepx.WithCredentials("user", "api-key"))
hub := realtime.NewMarketHub(client.Token,
realtime.WithURL(client.MarketHubURL()),
realtime.WithEventBuffer(4096),
realtime.WithOverflowPolicy(realtime.DropOldest),
)
if err := hub.Connect(context.Background()); err != nil {
log.Fatal(err)
}
defer hub.Close()
_ = hub.SubscribeContractQuotes(context.Background(), "CON.F.US.EP.U25")
}
Output:
func NewMarketHubForClient ¶
NewMarketHubForClient returns an unconnected market hub that takes its token, URL and logger from c and reports tokens rejected with HTTP 401 to c.InvalidateToken, so the next attempt logs in again. Later options override these settings.
func (*MarketHub) Close ¶
Close stops reconnecting, sends a SignalR close message, closes the socket, fails pending invocations with ErrClosed and closes the event channel. It waits for the hub's goroutines to exit and is idempotent.
func (*MarketHub) Connect ¶
Connect opens the connection and performs the SignalR handshake. A failed first connect is not retried automatically; call Connect again.
func (*MarketHub) Dropped ¶
Dropped returns how many events were discarded because the consumer fell behind.
func (*MarketHub) Events ¶
Events returns the event channel. It is buffered, never blocks the connection and is closed after the final StateClosed event.
func (*MarketHub) Invoke ¶
func (h *MarketHub) Invoke(ctx context.Context, method string, args ...any) (json.RawMessage, error)
Invoke calls any hub method and returns its raw result. It is not recorded for replay.
func (*MarketHub) SubscribeContractMarketDepth ¶
SubscribeContractMarketDepth subscribes to GatewayDepth events of the contract.
func (*MarketHub) SubscribeContractQuotes ¶
SubscribeContractQuotes subscribes to GatewayQuote events of the contract.
func (*MarketHub) SubscribeContractTrades ¶
SubscribeContractTrades subscribes to GatewayTrade events of the contract.
func (*MarketHub) UnsubscribeContractMarketDepth ¶
UnsubscribeContractMarketDepth cancels SubscribeContractMarketDepth for the contract.
func (*MarketHub) UnsubscribeContractQuotes ¶
UnsubscribeContractQuotes cancels SubscribeContractQuotes for the contract.
type MarketTradesEvent ¶
type MarketTradesEvent struct {
// ContractID is the contract the trades belong to.
ContractID string
// Trades are the trades, normalized to a list whether the server sent
// one object or an array.
Trades []GatewayTrade
// Raw is the payload JSON.
Raw json.RawMessage
}
MarketTradesEvent carries the market trades of one GatewayTrade message.
type Option ¶
type Option func(*config)
Option configures a hub.
func WithEventBuffer ¶
WithEventBuffer sets the capacity of the Events channel (default DefaultEventBuffer, minimum 1).
func WithHTTPClient ¶
WithHTTPClient sets the HTTP client used for the WebSocket upgrade.
func WithHandshakeTimeout ¶
WithHandshakeTimeout bounds the WebSocket dial and SignalR handshake (default DefaultHandshakeTimeout).
func WithInvocationTimeout ¶
WithInvocationTimeout bounds each hub method invocation (default DefaultInvocationTimeout).
func WithKeepAliveInterval ¶
WithKeepAliveInterval sets how long the client may stay silent before it sends a ping (default DefaultKeepAliveInterval).
func WithLogger ¶
WithLogger sets the logger. The default discards everything. Tokens are never logged.
func WithOverflowPolicy ¶
func WithOverflowPolicy(p OverflowPolicy) Option
WithOverflowPolicy sets which event is lost when the Events buffer is full (default DropOldest). Dropped events are counted by Dropped.
func WithReadLimit ¶
WithReadLimit sets the largest accepted WebSocket message in bytes (default DefaultReadLimit).
func WithReconnectPolicy ¶
func WithReconnectPolicy(p ReconnectPolicy) Option
WithReconnectPolicy sets the reconnect policy (default DefaultReconnectPolicy).
func WithServerTimeout ¶
WithServerTimeout sets how long the server may stay silent before the connection counts as lost (default DefaultServerTimeout).
func WithTokenRejected ¶
WithTokenRejected sets a function that receives the token of a connection attempt whose WebSocket upgrade the server rejected with HTTP 401. It is called before the next attempt, for the initial Connect and for every reconnect, so that the token source can discard the token instead of handing it out again until it expires. Pass (*topstepx.Client).InvalidateToken; NewUserHubForClient and NewMarketHubForClient do so automatically. The function must not block.
func WithoutReconnect ¶
func WithoutReconnect() Option
WithoutReconnect disables automatic reconnection.
type OrderEvent ¶
type OrderEvent struct {
// Action is the envelope's action code, if the payload was wrapped.
Action *int32
// Order is the decoded payload.
Order GatewayUserOrder
// Raw is the payload JSON, including undocumented members.
Raw json.RawMessage
}
OrderEvent is a GatewayUserOrder update.
type OverflowPolicy ¶
type OverflowPolicy int
OverflowPolicy decides which event is lost when the consumer falls behind and the Events buffer is full. Delivery never blocks the connection.
const ( // DropOldest evicts the oldest buffered event to make room (default). DropOldest OverflowPolicy = iota // DropNewest discards the incoming data event. State events are always // enqueued, evicting the oldest buffered event if needed. DropNewest )
OverflowPolicy values.
type PositionEvent ¶
type PositionEvent struct {
// Action is the envelope's action code, if the payload was wrapped.
Action *int32
// Position is the decoded payload.
Position GatewayUserPosition
// Raw is the payload JSON, including undocumented members.
Raw json.RawMessage
}
PositionEvent is a GatewayUserPosition update.
type QuoteEvent ¶
type QuoteEvent struct {
// ContractID is the contract the quote belongs to.
ContractID string
// Quote is the decoded payload. Updates may carry only changed fields.
Quote GatewayQuote
// Raw is the payload JSON, including undocumented members.
Raw json.RawMessage
}
QuoteEvent is a GatewayQuote update for a contract.
type ReconnectPolicy ¶
type ReconnectPolicy struct {
// Delays are the waits before successive attempts; the last one repeats
// for later attempts. Empty means the default delays.
Delays []time.Duration
// MaxAttempts caps the attempts per outage. UnlimitedAttempts (any
// negative value) removes the cap; zero disables reconnection.
MaxAttempts int
}
ReconnectPolicy controls automatic reconnection after a lost connection or a server close that allows reconnecting.
func DefaultReconnectPolicy ¶
func DefaultReconnectPolicy() ReconnectPolicy
DefaultReconnectPolicy returns the contract policy: delays of 0, 1, 2, 5 and 10 seconds, then every 30 seconds, with unlimited attempts.
type ServerCloseError ¶
type ServerCloseError struct {
// Message is the server's error text, empty when none was sent.
Message string
// AllowReconnect is the server's allowReconnect flag.
AllowReconnect bool
}
ServerCloseError is the cause reported when the server sends a SignalR close message. The hub reconnects only when AllowReconnect is true.
func (*ServerCloseError) Error ¶
func (e *ServerCloseError) Error() string
Error describes the close.
type StateEvent ¶
type StateEvent struct {
// State is the new state.
State ConnectionState
// Attempt is the 1-based reconnect attempt for StateReconnecting and
// StateReconnected.
Attempt int
// Err is the cause: for StateReconnecting, why the connection was lost
// or why the previous attempt failed; for StateClosed, why the
// connection ended, or nil after Close.
Err error
}
StateEvent reports a connection state change. A state event is always enqueued, even into a full buffer (the oldest buffered event is evicted); like any buffered event it can later be evicted under DropOldest when the consumer falls behind. The channel closing always follows StateClosed.
type SubscriptionErrorEvent ¶
type SubscriptionErrorEvent struct {
// Method is the subscribe method, such as "SubscribeOrders".
Method string
// Arguments are the method arguments.
Arguments []any
// Err is the failure.
Err error
}
SubscriptionErrorEvent reports a subscription that failed to be replayed after a reconnect. The subscription stays registered and is replayed again after the next reconnect.
type TokenProvider ¶
TokenProvider returns the access token for a connection attempt. It is called before every attempt, so each reconnect uses a fresh token. Pass (*topstepx.Client).Token.
type TradeLogType ¶
type TradeLogType int32
TradeLogType is the aggressor side of a market trade. Unrecognized values are kept.
const ( TradeLogTypeBuy TradeLogType = 0 TradeLogTypeSell TradeLogType = 1 )
TradeLogType values.
func (TradeLogType) String ¶
func (v TradeLogType) String() string
String returns the value's name, or "Unrecognized(<n>)".
type UnknownEvent ¶
type UnknownEvent struct {
// Target is the server event name.
Target string
// Arguments are the raw invocation arguments.
Arguments []json.RawMessage
// Err is the decode error, or nil for an unknown target.
Err error
}
UnknownEvent is a server invocation with an unknown target, or one whose payload failed to decode. The connection is unaffected.
type UserHub ¶
type UserHub struct {
// contains filtered or unexported fields
}
UserHub is a connection to the SignalR user hub, which streams account, order, position and trade updates. It is safe for concurrent use.
Create it with NewUserHub or NewUserHubForClient, read Events, call Connect, then subscribe. Subscriptions are replayed after every reconnect.
func NewUserHub ¶
func NewUserHub(tokens TokenProvider, opts ...Option) *UserHub
NewUserHub returns an unconnected user hub. tokens is called before every connection attempt; pass (*topstepx.Client).Token. The URL defaults to topstepx.DefaultUserHubURL.
func NewUserHubForClient ¶
NewUserHubForClient returns an unconnected user hub that takes its token, URL and logger from c and reports tokens rejected with HTTP 401 to c.InvalidateToken, so the next attempt logs in again. Later options override these settings.
Example ¶
package main
import (
"context"
"fmt"
"log"
topstepx "github.com/CurtisTechSolutions/TopstepX/go"
"github.com/CurtisTechSolutions/TopstepX/go/realtime"
)
func main() {
client, err := topstepx.NewClient(topstepx.WithCredentials("user", "api-key"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
hub := realtime.NewUserHubForClient(client)
if err := hub.Connect(ctx); err != nil {
log.Fatal(err)
}
defer hub.Close()
if err := hub.SubscribeOrders(ctx, 123); err != nil {
log.Fatal(err)
}
for ev := range hub.Events() {
switch ev := ev.(type) {
case *realtime.OrderEvent:
fmt.Println("order", ev.Order.ID, ev.Order.Status)
case *realtime.StateEvent:
fmt.Println("state", ev.State, ev.Err)
}
}
}
Output:
func (*UserHub) Close ¶
Close stops reconnecting, sends a SignalR close message, closes the socket, fails pending invocations with ErrClosed and closes the event channel. It waits for the hub's goroutines to exit and is idempotent.
func (*UserHub) Connect ¶
Connect opens the connection and performs the SignalR handshake. A failed first connect is not retried automatically; call Connect again.
func (*UserHub) Dropped ¶
Dropped returns how many events were discarded because the consumer fell behind.
func (*UserHub) Events ¶
Events returns the event channel. It is buffered, never blocks the connection and is closed after the final StateClosed event.
func (*UserHub) Invoke ¶
Invoke calls any hub method and returns its raw result. It is not recorded for replay.
func (*UserHub) SubscribeAccounts ¶
SubscribeAccounts subscribes to GatewayUserAccount events.
func (*UserHub) SubscribeOrders ¶
SubscribeOrders subscribes to GatewayUserOrder events of the account.
func (*UserHub) SubscribePositions ¶
SubscribePositions subscribes to GatewayUserPosition events of the account.
func (*UserHub) SubscribeTrades ¶
SubscribeTrades subscribes to GatewayUserTrade events of the account.
func (*UserHub) UnsubscribeAccounts ¶
UnsubscribeAccounts cancels SubscribeAccounts.
func (*UserHub) UnsubscribeOrders ¶
UnsubscribeOrders cancels SubscribeOrders for the account.
func (*UserHub) UnsubscribePositions ¶
UnsubscribePositions cancels SubscribePositions for the account.
type UserTradeEvent ¶
type UserTradeEvent struct {
// Action is the envelope's action code, if the payload was wrapped.
Action *int32
// Trade is the decoded payload.
Trade GatewayUserTrade
// Raw is the payload JSON, including undocumented members.
Raw json.RawMessage
}
UserTradeEvent is a GatewayUserTrade update.