Documentation
¶
Overview ¶
Package godex is a trading integration layer for perpetual DEXes. It owns authenticated order placement, cancellation, account-state observation, and venue-specific signing behind a small, safety-oriented contract. Strategy and risk logic depend only on the normalized types and events in this package; venue adapters live in subpackages (lighter, ...).
This is not a generic exchange SDK: the contract intentionally supports only what a post-only maker / IOC taker strategy needs.
Index ¶
- Constants
- Variables
- func ComputeMarginUsage(total, available string) (decimal.Decimal, error)
- func QuantizeReduceOnlySize(size, step decimal.Decimal) (decimal.Decimal, error)
- func QuantizeSize(size, step, minSize decimal.Decimal) (decimal.Decimal, error)
- func RoundPriceToTick(price, tick decimal.Decimal, side Side) (decimal.Decimal, error)
- func SizeForNotional(notional, price, step decimal.Decimal) (decimal.Decimal, error)
- type AccountEvent
- type AckStatus
- type BookLevel
- type BookSnapshotEvent
- type ConnectedEvent
- type DisconnectedEvent
- type ExecutionMetadata
- type FillEvent
- type FundingRate
- type MarginEvent
- type MarketConnectedEvent
- type MarketDataClient
- type MarketDisconnectedEvent
- type MarketEvent
- type MarketStats
- type MarketStream
- type NewOrder
- type OrderAck
- type OrderBook
- type OrderID
- type OrderIntent
- type OrderRejectedEvent
- type Position
- type PositionEvent
- type ReconnectConfig
- type Side
- type Symbol
- type VenueExecutor
- type VenueID
Constants ¶
const DefaultAccountEventBuffer = 1024
DefaultAccountEventBuffer is the AccountEvents channel capacity. It absorbs transient consumer stalls; when it fills, producers block instead of dropping (a dropped fill would silently corrupt position state), which eventually stalls the WebSocket read loop and surfaces loudly as a venue idle-disconnect plus reconnect.
const DefaultMarketEventBuffer = 1024
DefaultMarketEventBuffer is the MarketStream Events channel capacity. Like the account stream, when it fills the adapter blocks rather than dropping: a consumer acting on a silently stale book would quote against prices that no longer exist. The stall eventually surfaces as a venue idle-disconnect plus reconnect.
const FundingRateScale = 8
FundingRateScale is the decimal scale of normalized funding rates. Venues report rates at unpredictable native precision (dYdX has been observed sending 20 fractional digits); adapters round half away from zero to this scale so rates from different venues subtract cleanly.
const MarginUsageScale = 4
MarginUsageScale is the decimal scale of normalized margin usage ratios (0.6200 = 62%).
const ReasonCanceledByRequest = "canceled by request"
ReasonCanceledByRequest is the reason an adapter reports for an order that ended by a cancel the caller asked for. Every other reason is the venue's own wording, passed through; this one is the adapter's, so such a cancel reads the same on every venue.
It is reported when the venue says the order ended, not when it accepts the cancel — accepting one says the request was valid, not that it applied. A cancel accepted in the same instant the order filled applied to nothing, and that order is reported as filled, never under this reason.
It follows that an order whose end the venue never reports is never reported here either. Where an adapter can ask outright it does, and each reconnect re-checks every order still believed live. Lighter is the exception: its account stream reports only post-only cancellations and it has no order-status query, so a caller's cancel of a resting order there produces no event at all. See the lighter package comment.
const USDNotionalScale = 2
USDNotionalScale is the decimal scale of normalized USD notionals in market statistics (open interest, volume). Statistics are reference values, not order inputs, so cent precision is enough.
Variables ¶
var ( // ErrNotConnected is returned when an operation requires a connected // executor. ErrNotConnected = errors.New("godex: executor not connected") // ErrClosed is returned after Close. ErrClosed = errors.New("godex: executor closed") // ErrUnknownOrder is returned by CancelOrder for an ID the executor is // not tracking. ErrUnknownOrder = errors.New("godex: unknown order id") // ErrTxOutcomeUnknown reports that a submission's outcome could not be // determined (e.g. timeout). The executor latches this fault and blocks // further submissions until it reconciles with venue state; callers must // never blindly retry. ErrTxOutcomeUnknown = errors.New("godex: transaction outcome unknown") )
Sentinel errors shared by all venue adapters.
Functions ¶
func ComputeMarginUsage ¶
ComputeMarginUsage returns (total - available) / total at MarginUsageScale. Venue adapters map their native wire fields onto (total, available) — e.g. equity/freeCollateral or collateral/availableBalance. Zero total (an unfunded account) is zero usage.
func QuantizeReduceOnlySize ¶
QuantizeReduceOnlySize ceils size to a multiple of step. Reduce-only orders cannot flip the position, so dust may be ceiled up to fully close.
func QuantizeSize ¶
QuantizeSize floors size to a multiple of step. If the result is zero or below minSize, it returns an error rather than silently rounding up.
func RoundPriceToTick ¶
RoundPriceToTick rounds price to a multiple of tick: buy floors, sell ceils. The result carries tick's scale.
Types ¶
type AccountEvent ¶
type AccountEvent interface {
// contains filtered or unexported methods
}
AccountEvent is the sealed union of account-stream events. Consumers type-switch over the concrete types below; treat unknown variants in the default branch as a programming error (fail fast), mirroring strict discriminator validation.
type AckStatus ¶
type AckStatus string
AckStatus is the submission outcome reported by OrderAck.
const ( // AckSubmitted means the venue accepted the submission. It does not mean // the order filled. AckSubmitted AckStatus = "submitted" // AckRejected means the venue (or the adapter's pre-check) rejected the // order — e.g. a post-only order that would cross. A normal-path outcome. AckRejected AckStatus = "rejected" )
Ack statuses.
type BookSnapshotEvent ¶ added in v0.4.0
type BookSnapshotEvent struct {
Book OrderBook
}
BookSnapshotEvent carries a normalized full book snapshot. Adapters rebuild the book internally from the venue's snapshot/delta wire protocol; the difference never leaks to consumers.
type ConnectedEvent ¶
type ConnectedEvent struct {
VenueID VenueID
}
ConnectedEvent reports that the account stream is up and the initial (or post-reconnect) snapshot follows.
type DisconnectedEvent ¶
type DisconnectedEvent struct {
VenueID VenueID
}
DisconnectedEvent reports that the account stream is down; state events pause until the next ConnectedEvent.
type ExecutionMetadata ¶
type ExecutionMetadata struct {
// SizeStep is the venue's order size increment.
SizeStep decimal.Decimal
// MaintenanceMarginFraction is normalized to a decimal ratio. Venues
// define it differently (decimal vs 1/10000 integer); each adapter
// converts to a plain ratio.
MaintenanceMarginFraction decimal.Decimal
}
ExecutionMetadata is venue market metadata resolved during Connect.
type FillEvent ¶
type FillEvent struct {
OrderID OrderID
Side Side
Price decimal.Decimal
Size decimal.Decimal
Time time.Time
}
FillEvent reports an execution from the authenticated account stream — the only source of truth for fills.
type FundingRate ¶ added in v0.4.0
type FundingRate struct {
VenueID VenueID
Symbol Symbol
// Rate is the funding rate per interval at FundingRateScale, signed the
// way perp venues quote it: positive means longs pay shorts.
Rate decimal.Decimal
// IntervalHours is the venue's funding interval (1 for hourly venues).
IntervalHours int
// NextFundingTime is the next application time, nil when the venue's API
// does not report one.
NextFundingTime *time.Time
}
FundingRate is a venue's current funding rate observation for one market.
type MarginEvent ¶
type MarginEvent struct {
// UsageRatio is at MarginUsageScale; see ComputeMarginUsage.
UsageRatio decimal.Decimal
EquityUSD decimal.Decimal
Time time.Time
}
MarginEvent reports account margin state.
type MarketConnectedEvent ¶ added in v0.4.0
type MarketConnectedEvent struct {
VenueID VenueID
}
MarketConnectedEvent reports that the market stream is up and subscribed.
type MarketDataClient ¶ added in v0.4.0
type MarketDataClient interface {
// VenueID identifies the venue this client queries.
VenueID() VenueID
// FundingRate returns the venue's current funding rate for the
// configured market.
FundingRate(ctx context.Context) (FundingRate, error)
// MarketStats returns venue statistics for the configured market.
MarketStats(ctx context.Context) (MarketStats, error)
}
MarketDataClient is the normalized polled market-data contract (REST). Like executors, one client serves one market. Methods are safe for concurrent use.
type MarketDisconnectedEvent ¶ added in v0.4.0
type MarketDisconnectedEvent struct {
VenueID VenueID
}
MarketDisconnectedEvent reports that the market stream is down. Book snapshots pause until the next MarketConnectedEvent; consumers must treat the last snapshot as stale, not current.
type MarketEvent ¶ added in v0.4.0
type MarketEvent interface {
// contains filtered or unexported methods
}
MarketEvent is the sealed union of market-stream events. Consumers type-switch over the concrete types below; treat unknown variants in the default branch as a programming error (fail fast).
type MarketStats ¶ added in v0.4.0
type MarketStats struct {
VenueID VenueID
Symbol Symbol
// OpenInterestUSD is the open interest at USDNotionalScale. Venues that
// report OI in base-asset units are converted with the venue's own
// reference price, rounding once at the product.
OpenInterestUSD decimal.Decimal
// Volume24hUSD is the 24-hour volume at USDNotionalScale.
Volume24hUSD decimal.Decimal
}
MarketStats are venue market statistics. Reference values only — never order inputs.
type MarketStream ¶ added in v0.4.0
type MarketStream interface {
// VenueID identifies the venue this stream observes.
VenueID() VenueID
// Start dials the venue and subscribes. A first-connect failure is
// returned and the reconnect loop is not entered (fail fast).
Start(ctx context.Context) error
// Events returns the stream's single event channel. The channel is
// buffered (DefaultMarketEventBuffer); when it fills the adapter blocks
// rather than dropping. Consume promptly. The channel is closed only
// after Close completes.
Events() <-chan MarketEvent
// Close tears the stream down and closes the event channel. Close is
// terminal; observing again means constructing a new stream.
Close() error
}
MarketStream is the normalized market-data streaming contract. Like executors, one stream serves one market: N markets are N streams.
Design invariants:
- A crossed book is never emitted. Sequence gaps, duplicate sequence numbers, and unparseable payloads abort the connection instead of being guessed at (fail fast); the stream reconnects and resubscribes.
- Snapshot/delta reassembly is internal; consumers always receive full snapshots.
Unlike VenueExecutor.Close, a MarketStream keeps itself alive across connection drops between Start and Close: drops emit MarketDisconnectedEvent, reconnects emit MarketConnectedEvent and resubscribe.
Event ordering contract (Events):
- MarketConnectedEvent and MarketDisconnectedEvent alternate, including across internal reconnects.
- BookSnapshotEvent is emitted only between a MarketConnectedEvent and the following MarketDisconnectedEvent.
type NewOrder ¶
type NewOrder struct {
Symbol Symbol
Side Side
// Price is the desired price before rounding; the executor rounds it to
// the venue tick (buy floor / sell ceil).
Price decimal.Decimal
// Size is the desired size before rounding; the executor quantizes it to
// the venue step.
Size decimal.Decimal
Intent OrderIntent
ReduceOnly bool
}
NewOrder is a normalized order intent.
type OrderBook ¶ added in v0.4.0
type OrderBook struct {
VenueID VenueID
Symbol Symbol
Bids []BookLevel
Asks []BookLevel
// ReceivedAt is the local receive time of the update that produced this
// snapshot. Consumers use it for staleness decisions.
ReceivedAt time.Time
}
OrderBook is a normalized full order-book snapshot. Bids are sorted best (highest) first, asks best (lowest) first. A book is never emitted crossed; see MarketStream.
type OrderID ¶
type OrderID string
OrderID is an executor-scoped order identifier. The mapping to venue-native IDs is kept inside each adapter.
type OrderIntent ¶
type OrderIntent string
OrderIntent is the normalized execution intent. There is no GTC: the contract supports exactly what a maker/taker strategy needs.
const ( // IntentPostOnly quotes maker-only; an order that would cross the book // is rejected by the venue (a normal-path outcome). IntentPostOnly OrderIntent = "post_only" // IntentIOC executes immediately up to the price cap; any remainder is // canceled. IntentIOC OrderIntent = "ioc" )
Order intents.
type OrderRejectedEvent ¶
OrderRejectedEvent reports that an order is finished without having filled in full — a post-only order that would cross, an IOC remainder the venue cancelled, a short-term order that reached its expiry block. A normal-path event, not an error.
It means no further fills are coming for this order. It does not mean the order did nothing: an IOC that filled part of its size and had the rest cancelled produces both a FillEvent and this. Consumers must treat it as closing the order, not as voiding it.
It can also arrive before a fill it accounts for, when the venue reports the removal in an earlier message than the execution. Adapters emit fills first within a single venue message, but they do not reorder across messages — buffering the account stream to tidy this would cost latency on the one signal that must not have any. Attribute fills by OrderID rather than assuming a rejection is the last word on an order.
type Position ¶
type Position struct {
VenueID VenueID
Symbol Symbol
// Size is signed: long positive, short negative, zero flat.
Size decimal.Decimal
EntryPrice decimal.Decimal
UnrealizedPnL decimal.Decimal
// Time is the venue observation timestamp.
Time time.Time
}
Position is a venue position observation.
type PositionEvent ¶
type PositionEvent struct {
Position Position
}
PositionEvent reports a position observation.
type ReconnectConfig ¶
type ReconnectConfig struct {
// InitialDelay is the backoff delay after an unexpected drop; it resets
// on every successful open.
InitialDelay time.Duration
// MaxDelay caps the exponential backoff.
MaxDelay time.Duration
// Multiplier scales the delay after each failed reconnect attempt.
Multiplier float64
// IdleTimeout is the maximum inbound silence (messages, pings, pongs)
// before a connection is treated as half-open (e.g. a sleep/wake or
// network partition where no close frame arrives) and force-reconnected.
IdleTimeout time.Duration
}
ReconnectConfig tunes WebSocket reconnect behavior shared by all venue adapters: exponential backoff plus half-open detection.
func DefaultReconnectConfig ¶
func DefaultReconnectConfig() ReconnectConfig
DefaultReconnectConfig returns the reference tuning used in production.
func (ReconnectConfig) IsZero ¶
func (c ReconnectConfig) IsZero() bool
IsZero reports whether c is the zero value.
func (ReconnectConfig) Validate ¶
func (c ReconnectConfig) Validate() error
Validate rejects partially specified configs. Use the zero value (adapters substitute DefaultReconnectConfig) or specify every field.
type Symbol ¶
type Symbol string
Symbol is the normalized instrument label, e.g. "SOL-PERP". The symbol universe is application configuration, not library contract; adapters map it to venue-native market identifiers.
type VenueExecutor ¶
type VenueExecutor interface {
// VenueID identifies the venue this executor trades on.
VenueID() VenueID
// Connect loads venue market metadata, validates credentials, starts the
// authenticated account stream, and emits a verified initial snapshot
// (Connected, Position, Margin) before returning. Unsupported positions
// or incomplete account state fail Connect.
Connect(ctx context.Context) (ExecutionMetadata, error)
// PlaceOrder rounds, signs, and submits the order.
//
// ctx cancellation is honored only until the transaction is dispatched;
// once submission has started, PlaceOrder waits for the venue outcome
// under the adapter's own request timeout — canceling mid-flight would
// leave the submission ambiguous or orphan a live order.
//
// If a submission outcome is unknown, the adapter latches a fault: the
// affected transaction is never retried and subsequent submissions fail
// with ErrTxOutcomeUnknown until the adapter reconciles with venue state.
PlaceOrder(ctx context.Context, order NewOrder) (OrderAck, error)
// CancelOrder cancels a previously placed order by its executor-scoped
// ID. Returns ErrUnknownOrder for IDs the executor is not tracking.
CancelOrder(ctx context.Context, id OrderID) error
// AccountEvents returns the executor's single account-event stream. The
// channel is buffered (DefaultAccountEventBuffer); when it fills the
// adapter blocks rather than dropping — a dropped fill would silently
// corrupt position state. Consume promptly. The channel is closed only
// after Close completes, so range termination means the executor is
// terminal.
AccountEvents() <-chan AccountEvent
// Close tears the executor down: a final DisconnectedEvent is emitted
// (if connected), then the event channel is closed. Close is terminal;
// reconnecting means constructing a new executor.
Close() error
}
VenueExecutor is the normalized execution contract every venue adapter implements.
Design invariants:
- Maker orders are always post-only. A taker-crossing rejection is a normal-path outcome — PlaceOrder returns AckRejected (plus an OrderRejectedEvent); it is never an error.
- REST and WebSocket payloads are validated strictly. Unexpected shapes abort the connection instead of being guessed at (fail fast).
- Authenticated account-stream fills are the only source of truth for executions. Adapters never infer fills or positions from book state.
- Price tick and size step rounding are the adapter's responsibility.
Concurrency: Connect must return before any other method is called — it resolves the market metadata and signer the other methods read, and nothing else establishes that ordering. Afterwards PlaceOrder, CancelOrder, and AccountEvents are safe to use concurrently, and Close is terminal.
Event ordering contract (AccountEvents):
- ConnectedEvent and DisconnectedEvent alternate, including across internal reconnects.
- Other events are emitted only between a ConnectedEvent and the following DisconnectedEvent.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
godex-smoke
command
godex-smoke runs the adoption-gate smoke test against a live venue (normally testnet) with real credentials taken from the environment.
|
godex-smoke runs the adoption-gate smoke test against a live venue (normally testnet) with real credentials taken from the environment. |
|
Package decimal provides an immutable fixed-point decimal: value = mantissa / 10^scale.
|
Package decimal provides an immutable fixed-point decimal: value = mantissa / 10^scale. |
|
Package dydx implements godex.VenueExecutor for dYdX v4.
|
Package dydx implements godex.VenueExecutor for dYdX v4. |
|
internal/pb
Package pb holds the generated protobuf types the dydx adapter needs to build, sign, and broadcast dYdX v4 (Cosmos SDK) transactions.
|
Package pb holds the generated protobuf types the dydx adapter needs to build, sign, and broadcast dYdX v4 (Cosmos SDK) transactions. |
|
Package hyperliquid implements godex.VenueExecutor for Hyperliquid.
|
Package hyperliquid implements godex.VenueExecutor for Hyperliquid. |
|
internal
|
|
|
book
Package book implements shared order-book reassembly for snapshot+delta market-data feeds.
|
Package book implements shared order-book reassembly for snapshot+delta market-data feeds. |
|
dedupe
Package dedupe bounds the "have I already reported this?" state an adapter needs to keep an at-most-once event contract.
|
Package dedupe bounds the "have I already reported this?" state an adapter needs to keep an at-most-once event contract. |
|
ws
Package ws implements the shared WebSocket connection lifecycle used by venue adapters: exponential-backoff reconnect and half-open detection.
|
Package ws implements the shared WebSocket connection lifecycle used by venue adapters: exponential-backoff reconnect and half-open detection. |
|
Package lighter implements godex.VenueExecutor for Lighter (zkLighter).
|
Package lighter implements godex.VenueExecutor for Lighter (zkLighter). |
|
Package smoketest runs the venue-agnostic adoption-gate scenario against a live VenueExecutor (normally on testnet).
|
Package smoketest runs the venue-agnostic adoption-gate scenario against a live VenueExecutor (normally on testnet). |