types

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package types defines the core domain values shared across the order book and matching engine: orders, trades, and the small set of errors they raise.

Prices and quantities are exact decimals (shopspring/decimal) — never floats — so that money arithmetic is precise. This is the single most important correctness decision in the project; see docs/SPEC.md §6.1.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidPrice       = errors.New("invalid price: must be positive")
	ErrInvalidQuantity    = errors.New("invalid quantity: must be positive")
	ErrInvalidSide        = errors.New("invalid order side")
	ErrInvalidOrderType   = errors.New("invalid order type")
	ErrInvalidTimeInForce = errors.New("invalid time in force")
)

Validation errors raised when constructing orders.

View Source
var (
	ErrFillExceedsRemaining    = errors.New("fill quantity exceeds remaining quantity")
	ErrOrderNotFound           = errors.New("order not found")
	ErrOrderNotActive          = errors.New("order is not active")
	ErrOrderBookFull           = errors.New("order book has reached maximum capacity")
	ErrMarketOrderNoLiquidity  = errors.New("market order cannot be filled: no liquidity")
	ErrFOKCannotFill           = errors.New("FOK order cannot be fully filled")
	ErrSelfTradeNotAllowed     = errors.New("self-trade not allowed")
	ErrNilOrder                = errors.New("order must not be nil")
	ErrInvalidStopPrice        = errors.New("invalid stop price: must be positive")
	ErrPostOnlyWouldCross      = errors.New("post-only order would cross the spread")
	ErrInvalidDisplayQuantity  = errors.New("display quantity must be positive and <= total quantity")
	ErrInvalidPegReference     = errors.New("invalid peg reference")
	ErrPegReferenceUnavailable = errors.New("peg reference price is unavailable")
	ErrTradingHalted           = errors.New("trading is halted")
	ErrPriceOutsideBand        = errors.New("price is outside the allowed band")
)

Lifecycle / matching errors.

Functions

This section is empty.

Types

type IcebergOrder

type IcebergOrder struct {
	Order      *Order          // the currently displayed slice (Quantity == a display chunk)
	DisplayQty decimal.Decimal // size shown per slice
	Hidden     decimal.Decimal // quantity not yet displayed
}

IcebergOrder shows only a small slice of a large order to the book at a time. The visible slice (Order) rests like an ordinary limit; when it is fully consumed the order refills the next slice from its hidden reserve, until the total quantity is worked off. This is how large participants avoid signalling their full size (docs/SPEC.md §5.1).

func NewIcebergOrder

func NewIcebergOrder(order *Order, displayQty decimal.Decimal) (*IcebergOrder, error)

NewIcebergOrder wraps order (whose Quantity is the TOTAL size) so that only displayQty is visible at a time. displayQty must be positive and no larger than the total.

func (*IcebergOrder) IsFullyFilled

func (ib *IcebergOrder) IsFullyFilled() bool

IsFullyFilled reports whether both the hidden reserve and the visible slice are exhausted.

func (*IcebergOrder) Refill

func (ib *IcebergOrder) Refill() bool

Refill loads the next slice into Order (resetting its fill state to the new chunk size) and returns true. It returns false when nothing is hidden.

func (*IcebergOrder) TotalRemaining

func (ib *IcebergOrder) TotalRemaining() decimal.Decimal

TotalRemaining is the hidden reserve plus the unfilled part of the visible slice.

type OCOOrder

type OCOOrder struct {
	Primary *Order     // e.g. take-profit limit
	Stop    *StopOrder // e.g. stop-loss
}

OCOOrder links two orders so that completing one cancels the other. The canonical use is a bracket exit for an open position: a take-profit limit (Primary) paired with a stop-loss (Stop). Whichever fires first, the engine cancels its counterpart (docs/SPEC.md §5.1).

func NewOCOOrder

func NewOCOOrder(primary *Order, stop *StopOrder) (*OCOOrder, error)

NewOCOOrder pairs a primary order with a stop order. Both must be non-nil.

type Order

type Order struct {
	ID           string          `json:"id"`
	UserID       string          `json:"user_id"`
	Symbol       string          `json:"symbol"`
	Side         Side            `json:"side"`
	Type         OrderType       `json:"type"`
	Price        decimal.Decimal `json:"price"`
	Quantity     decimal.Decimal `json:"quantity"`
	FilledQty    decimal.Decimal `json:"filled_qty"`
	RemainingQty decimal.Decimal `json:"remaining_qty"`
	TimeInForce  TimeInForce     `json:"time_in_force"`
	PostOnly     bool            `json:"post_only,omitempty"` // maker-only: reject if it would cross
	Status       OrderStatus     `json:"status"`
	CreatedAt    time.Time       `json:"created_at"`
	UpdatedAt    time.Time       `json:"updated_at"`
	SequenceNum  uint64          `json:"sequence_num"`
}

Order is a single instruction to buy or sell a quantity of a symbol.

RemainingQty is the authoritative "how much is still live" figure the matching engine reads and mutates; FilledQty + RemainingQty always equals Quantity.

func NewOrder

func NewOrder(userID, symbol string, side Side, orderType OrderType, price, quantity decimal.Decimal, tif TimeInForce) (*Order, error)

NewOrder constructs a validated order with a time-ordered UUIDv7 id.

Quantity must be positive. Limit orders require a positive price; market orders ignore price (it is forced to zero, since a market order takes whatever the book offers).

func (*Order) AsPostOnly

func (o *Order) AsPostOnly() *Order

AsPostOnly marks the order maker-only and returns it (for fluent construction). A post-only order is rejected if it would cross the spread instead of resting.

func (*Order) Cancel

func (o *Order) Cancel() error

Cancel marks the order cancelled. Filled or already-cancelled orders cannot be cancelled.

func (*Order) Fill

func (o *Order) Fill(qty decimal.Decimal) error

Fill applies a fill of qty to the order, advancing FilledQty/RemainingQty and Status. It returns an error if qty is non-positive or exceeds RemainingQty.

func (*Order) Fresh

func (o *Order) Fresh() *Order

Fresh returns a copy of the order reset to its initial, unfilled state (FilledQty=0, RemainingQty=Quantity, Status=NEW, SequenceNum=0) while keeping its identity and parameters (ID, user, symbol, side, type, price, quantity, TIF). It exists for deterministic replay and simulation, where the same order must be re-submitted to a fresh engine without carrying prior fill state. The original order is not modified.

func (*Order) IsActive

func (o *Order) IsActive() bool

IsActive reports whether the order can still be matched or cancelled.

func (*Order) IsFilled

func (o *Order) IsFilled() bool

IsFilled reports whether the order has no remaining quantity.

func (*Order) NotionalValue

func (o *Order) NotionalValue() decimal.Decimal

NotionalValue is Price × Quantity (zero for market orders, which have no price).

func (*Order) RemainingValue

func (o *Order) RemainingValue() decimal.Decimal

RemainingValue is Price × RemainingQty.

type OrderStatus

type OrderStatus string

OrderStatus is the lifecycle state of an order.

const (
	OrderStatusNew             OrderStatus = "NEW"
	OrderStatusPartiallyFilled OrderStatus = "PARTIALLY_FILLED"
	OrderStatusFilled          OrderStatus = "FILLED"
	OrderStatusCancelled       OrderStatus = "CANCELLED"
	OrderStatusRejected        OrderStatus = "REJECTED"
	OrderStatusPendingTrigger  OrderStatus = "PENDING_TRIGGER" // resting stop, not yet fired
)

type OrderType

type OrderType string

OrderType is how an order interacts with the book.

const (
	// OrderTypeLimit rests at a price (or better) until filled or cancelled.
	OrderTypeLimit OrderType = "LIMIT"
	// OrderTypeMarket takes liquidity immediately at the best available prices.
	OrderTypeMarket OrderType = "MARKET"
)

type PegReference

type PegReference string

PegReference is the market price a pegged order tracks.

const (
	PegToBid PegReference = "BID" // best bid
	PegToAsk PegReference = "ASK" // best ask
	PegToMid PegReference = "MID" // mid price
)

type PeggedOrder

type PeggedOrder struct {
	Order  *Order
	Ref    PegReference
	Offset decimal.Decimal
}

PeggedOrder is a limit order whose price is derived from a market reference plus a signed offset at the moment it is submitted (price = reference + Offset). This is a static peg — it resolves once on entry; continuously re-pegging as the book moves is a future extension (docs/SPEC.md §5.1).

func NewPeggedOrder

func NewPeggedOrder(order *Order, ref PegReference, offset decimal.Decimal) (*PeggedOrder, error)

NewPeggedOrder wraps a limit order with a peg reference and offset.

type Side

type Side string

Side is the direction of an order.

const (
	SideBuy  Side = "BUY"
	SideSell Side = "SELL"
)

type StopOrder

type StopOrder struct {
	Order     *Order
	StopPrice decimal.Decimal
	// contains filtered or unexported fields
}

StopOrder is a trigger wrapper around an ordinary order. While resting it sits off-book; when the market price reaches StopPrice the underlying Order (a market order for a stop, or a limit order for a stop-limit) is released to the matching engine.

Direction follows the underlying side: a BUY stop triggers when the market rises to the stop (price ≥ StopPrice); a SELL stop triggers when the market falls to it (price ≤ StopPrice). A sell stop below the market is the classic stop-loss for a long position.

func NewStopOrder

func NewStopOrder(order *Order, stopPrice decimal.Decimal) (*StopOrder, error)

NewStopOrder wraps order with a positive stop price.

func (*StopOrder) IsTriggered

func (s *StopOrder) IsTriggered() bool

IsTriggered reports whether the stop has fired.

func (*StopOrder) ShouldTrigger

func (s *StopOrder) ShouldTrigger(marketPrice decimal.Decimal) bool

ShouldTrigger reports whether marketPrice has reached the stop (and it has not already fired).

func (*StopOrder) Trigger

func (s *StopOrder) Trigger()

Trigger marks the stop as fired and resets the underlying order to NEW so the engine can process it.

type TimeInForce

type TimeInForce string

TimeInForce controls how long an order remains active.

const (
	// TIFGoodTillCancel rests until explicitly cancelled.
	TIFGoodTillCancel TimeInForce = "GTC"
	// TIFImmediateOrCancel fills what it can immediately, cancels the rest.
	TIFImmediateOrCancel TimeInForce = "IOC"
	// TIFFillOrKill fills the entire order immediately or cancels all of it.
	TIFFillOrKill TimeInForce = "FOK"
)

type Trade

type Trade struct {
	ID           string          `json:"id"`
	Symbol       string          `json:"symbol"`
	Price        decimal.Decimal `json:"price"`
	Quantity     decimal.Decimal `json:"quantity"`
	BuyOrderID   string          `json:"buy_order_id"`
	SellOrderID  string          `json:"sell_order_id"`
	BuyerUserID  string          `json:"buyer_user_id"`
	SellerUserID string          `json:"seller_user_id"`
	MakerOrderID string          `json:"maker_order_id"`
	TakerOrderID string          `json:"taker_order_id"`
	TakerSide    Side            `json:"taker_side"`
	CreatedAt    time.Time       `json:"created_at"`
	SequenceNum  uint64          `json:"sequence_num"`
}

Trade is a single execution between a resting maker order and an incoming taker order. It always prints at the maker's (resting) price.

func NewTrade

func NewTrade(symbol string, price, quantity decimal.Decimal, buyOrder, sellOrder *Order, takerSide Side) *Trade

NewTrade builds a trade from the matched buy/sell orders. takerSide identifies which side crossed the spread, from which maker/taker order ids are derived.

func (*Trade) IsSelfTrade

func (t *Trade) IsSelfTrade() bool

IsSelfTrade reports whether both sides belong to the same user.

func (*Trade) NotionalValue

func (t *Trade) NotionalValue() decimal.Decimal

NotionalValue is Price × Quantity of the trade.

type TrailingStop

type TrailingStop struct {
	Order *Order
	Trail decimal.Decimal
	// contains filtered or unexported fields
}

TrailingStop is a stop whose trigger price follows the market by a fixed Trail distance, ratcheting in the favourable direction and never retreating. A SELL trailing stop (protecting a long) trails Trail below the highest price seen and fires when price falls to it; a BUY trailing stop (protecting a short) trails Trail above the lowest price seen (docs/SPEC.md §5.1).

func NewTrailingStop

func NewTrailingStop(order *Order, trail decimal.Decimal) (*TrailingStop, error)

NewTrailingStop wraps order with a positive trail distance.

func (*TrailingStop) IsTriggered

func (ts *TrailingStop) IsTriggered() bool

IsTriggered reports whether the trailing stop has fired.

func (*TrailingStop) Observe

func (ts *TrailingStop) Observe(marketPrice decimal.Decimal)

Observe updates the trailing extreme and stop price from a new market price. The extreme only moves favourably, so the stop only ratchets.

func (*TrailingStop) ShouldTrigger

func (ts *TrailingStop) ShouldTrigger(marketPrice decimal.Decimal) bool

ShouldTrigger reports whether marketPrice has reached the trailed stop.

func (*TrailingStop) StopPrice

func (ts *TrailingStop) StopPrice() decimal.Decimal

StopPrice returns the current trailed trigger price.

func (*TrailingStop) Trigger

func (ts *TrailingStop) Trigger()

Trigger fires the trailing stop, releasing its underlying order.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL