Skip to content

Instantly share code, notes, and snippets.

@heri16
Last active September 9, 2021 08:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heri16/5d8c34971e3075df5819b4a0676019b1 to your computer and use it in GitHub Desktop.
Save heri16/5d8c34971e3075df5819b4a0676019b1 to your computer and use it in GitHub Desktop.
Nasdaq OUCH_4.2 golang
package main
import (
"bytes"
"encoding"
"encoding/binary"
"io"
"fmt"
"stockbit.com/proto/ouch42"
"stockbit.com/proto/ouch42/token"
"stockbit.com/proto/ouch42/stock"
)
func main() {
// See: https://www.nasdaqtrader.com/content/technicalsupport/specifications/tradingproducts/ouch4.2.pdf
// Spec: Prices are integer fields with 6 whole number places followed by 4 decimal digits.
orderToken := token.New("12345678901234")
// Enter Order
order := ouch42.MsgEnterOrder{
OrderToken: orderToken,
BuySellIndicator: ouch42.BUY_SELL_INDICATOR_BUY,
Shares: 2000,
Stock: stock.New("BUKA"),
Price: 15000, // 1.5,
}
order.Defaults()
fmt.Printf("Struct: %+v\n", order)
bytes1, err := order.MarshalBinary()
if err != nil { panic(err) }
fmt.Println("Bytes:", bytes1)
fmt.Println("Length:", len(bytes1))
enterOrder := ouch42.MsgEnterOrder{}
enterOrder.UnmarshalBinary(bytes1)
fmt.Printf("Equals?: %t\n", enterOrder == order)
// Cancel Order
order2 := ouch42.MsgCancelOrder{ OrderToken: enterOrder.OrderToken }
fmt.Printf("Struct: %+v\n", order2)
bytes2, err := order2.MarshalBinary()
if err != nil { panic(err) }
fmt.Println("Bytes:", bytes2)
fmt.Println("Length:", len(bytes2))
cancelOrder := ouch42.MsgCancelOrder{}
cancelOrder.UnmarshalBinary(bytes2)
fmt.Printf("Equals?: %t\n", cancelOrder == order2)
// Check BinaryMarshaler Interface
orders := []encoding.BinaryMarshaler{}
orders = append(orders, &enterOrder)
orders = append(orders, &cancelOrder)
var buf bytes.Buffer
for _, v := range(orders) {
data, err := v.MarshalBinary()
if err != nil { panic(err) }
// Write header
var dataHeader [2]byte
dataLen := uint16(len(data))
binary.BigEndian.PutUint16(dataHeader[0:2], dataLen)
buf.Write(dataHeader[:])
// Write datum
buf.Write(data)
}
fmt.Println("Marshalled Bytes (length-prefixed):", buf)
// Check BinaryUnmarshaler Interface
dataHeader := make([]byte, 2)
for {
// Read header
_, err := buf.Read(dataHeader)
if err == io.EOF { break }
if err != nil { panic(err) }
dataLen := binary.BigEndian.Uint16(dataHeader[0:2]);
// Read datum
data := make([]byte, dataLen)
_, err = buf.Read(data)
if err == io.EOF { break }
if err != nil { panic(err) }
// Unmarshal data
switch msgType := data[0]; msgType {
case ouch42.MESSAGE_TYPE_ENTER_ORDER:
msg := ouch42.MsgEnterOrder{}
msg.UnmarshalBinary(data)
fmt.Printf("Unmarshalled Struct: %+v\n", msg)
case ouch42.MESSAGE_TYPE_CANCEL_ORDER:
msg := ouch42.MsgCancelOrder{}
msg.UnmarshalBinary(data)
fmt.Printf("Unmarshalled Struct: %+v\n", msg)
default:
fmt.Printf("Cannot Unmarshal Type: %c\n", msgType)
}
}
}
-- go.mod --
module stockbit.com/proto
-- ouch42/ouch42.go --
package ouch42
import (
"fmt"
"bytes"
"encoding/binary"
)
const (
// See: https://github.com/libtrading/libtrading/blob/master/include/libtrading/proto/ouch42_message.h
MESSAGE_TYPE_ENTER_ORDER = 'O'
MESSAGE_TYPE_REPLACE_ORDER = 'U'
MESSAGE_TYPE_CANCEL_ORDER = 'X'
MESSAGE_TYPE_MODIFY_ORDER = 'M'
MESSAGE_TYPE_SYSTEM_EVENT = 'S'
MESSAGE_TYPE_ACCEPTED = 'A'
MESSAGE_TYPE_REPLACED = 'U'
MESSAGE_TYPE_CANCELED = 'C'
MESSAGE_TYPE_AIQ_CANCELED = 'D'
MESSAGE_TYPE_EXECUTED = 'E'
MESSAGE_TYPE_BROKEN_TRADE = 'B'
MESSAGE_TYPE_REJECTED = 'J'
MESSAGE_TYPE_CANCEL_PENDING = 'P'
MESSAGE_TYPE_CANCEL_REJECT = 'I'
MESSAGE_TYPE_ORDER_PRIO_UPDATE = 'T'
MESSAGE_TYPE_ORDER_MODIFIED = 'M'
)
const (
// See: https://github.com/Open-Markets-Initiative/c-structs/blob/master/Nasdaq/Nasdaq.Equities.Orders.Ouch.v4.2.h
/*
* Buy Sell Indicator Values
*/
BUY_SELL_INDICATOR_BUY = 'B'
BUY_SELL_INDICATOR_SELL = 'S'
BUY_SELL_INDICATOR_SELL_SHORT = 'T'
BUY_SELL_INDICATOR_SELL_SHORT_EXEMPT = 'E'
/*
* Display Values
*/
DISPLAY_ATTRIBUTABLE_PRICE = 'A'
DISPLAY_ANONYMOUS_PRICE = 'Y'
DISPLAY_NON_DISPLAY = 'N'
DISPLAY_POST_ONLY = 'P'
DISPLAY_IMBALANCE_ONLY = 'I'
DISPLAY_MID_POINT = 'M'
DISPLAY_MID_POINT_POST_ONLY = 'W'
DISPLAY_POST_ONLY_AND_ATTRIBUTABLE = 'L'
DISPLAY_RETAIL_ORDER_TYPE_1 = 'O'
DISPLAY_RETAIL_ORDER_TYPE_2 = 'T'
DISPLAY_RETAIL_PRICE = 'Q'
DISPLAY_MID_POINT_TRADE_NOW = 'm'
DISPLAY_NON_DISPLAY_AND_MID_POINT_TRADE_NOW = 'n'
/*
* Capacity Values
*/
CAPACITY_OTHER = 'O'
CAPACITY_AGENCY = 'A'
CAPACITY_PRINCIPAL = 'P'
CAPACITY_RISKLESS = 'R'
/*
* Intermarket Sweep Eligibility Values
*/
INTERMARKET_SWEEP_ELIGIBILITY_ELIGIBLE = 'Y'
INTERMARKET_SWEEP_ELIGIBILITY_NOT_ELIGIBLE = 'N'
INTERMARKET_SWEEP_ELIGIBILITY_TRADEAT = 'y'
/*
* Cross Type Values
*/
CROSS_TYPE_NO_CROSS = 'N'
CROSS_TYPE_OPENING = 'O'
CROSS_TYPE_CLOSING = 'C'
CROSS_TYPE_HALT_IPO_CROSS = 'H'
CROSS_TYPE_SUPPLEMENTAL = 'S'
CROSS_TYPE_RETAIL = 'R'
CROSS_TYPE_EXTENDED = 'E'
)
type char = byte
type u8 = uint8
type u32 = uint32
type Token [14]char
type Stock [8]char
type Firm [4]char
func (v Token) String() string {
return string(bytes.TrimSpace(v[:]))
}
func (v Stock) String() string {
return string(bytes.TrimSpace(v[:]))
}
func (v Firm) String() string {
return string(bytes.TrimSpace(v[:]))
}
type MsgEnterOrder struct {
// See: https://github.com/libtrading/libtrading/blob/master/include/libtrading/proto/ouch42_message.h
// MessageType char
OrderToken Token
BuySellIndicator char
Shares u32
Stock Stock
Price u32
TimeInForce u32
Firm Firm
Display char
Capacity char
IntermarketSweepEligibility char
MinimumQuantity u32
CrossType char
}
func (m* MsgEnterOrder) MarshalBinary() ([]byte, error) {
// Arrays are faster for serialization
// See: https://stackoverflow.com/questions/58775651/which-is-the-go-way-putuint32-or-use-operator-directly
// Spec: All integer fields are unsigned big-endian (network byte order) binary encoded numbers.
var shares [4]byte
binary.BigEndian.PutUint32(shares[:], m.Shares)
var price [4]byte
binary.BigEndian.PutUint32(price[:], m.Price)
var timeInForce [4]byte
binary.BigEndian.PutUint32(timeInForce[:], m.TimeInForce)
var minimumQuantity [4]byte
binary.BigEndian.PutUint32(minimumQuantity[:], m.MinimumQuantity)
stock := &m.Stock
firm := &m.Firm
data := [...]byte{
MESSAGE_TYPE_ENTER_ORDER, // MessageType
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // OrderToken
m.BuySellIndicator,
shares[0], shares[1], shares[2], shares[3],
stock[0], stock[1], stock[2], stock[3], stock[4], stock[5], stock[6], stock[7],
price[0], price[1], price[2], price[3],
timeInForce[0], timeInForce[1], timeInForce[2], timeInForce[3],
firm[0], firm[1], firm[2], firm[3],
m.Display,
m.Capacity,
m.IntermarketSweepEligibility,
minimumQuantity[0], minimumQuantity[1], minimumQuantity[2], minimumQuantity[3],
m.CrossType,
}
copy(data[1:15], m.OrderToken[:])
// data[15] = m.BuySellIndicator
// binary.BigEndian.PutUint32(data[16:20], m.Shares)
// copy(data[20:28], m.Stock[:])
// binary.BigEndian.PutUint32(data[28:32], m.Price)
// binary.BigEndian.PutUint32(data[32:36], m.TimeInForce)
// copy(data[36:40], m.Firm[:])
// data[40] = m.Display
// data[41] = m.Capacity
// data[42] = m.IntermarketSweepEligibility
// binary.BigEndian.PutUint32(data[43:47], m.MinimumQuantity)
// data[47] = m.CrossType
// Slices are faster to pass around
return data[:], nil
}
func (m* MsgEnterOrder) UnmarshalBinary(data []byte) error {
copy(m.OrderToken[:], data[1:15])
m.BuySellIndicator = data[15]
m.Shares = binary.BigEndian.Uint32(data[16:20])
copy(m.Stock[:], data[20:28])
m.Price = binary.BigEndian.Uint32(data[28:32])
m.TimeInForce = binary.BigEndian.Uint32(data[32:36])
copy(m.Firm[:], data[36:40])
m.Display = data[40]
m.Capacity = data[41]
m.IntermarketSweepEligibility = data[42]
m.MinimumQuantity = binary.BigEndian.Uint32(data[43:47])
m.CrossType = data[47]
return nil
}
func (m* MsgEnterOrder) Defaults() {
if m.TimeInForce == 0 {
m.TimeInForce = 99999 // Indicates that the order should live until the end of the trading day
}
if (m.Firm == Firm{}) {
m.Firm = Firm{32, 32, 32, 32}
}
if m.Display == 0 {
m.Display = DISPLAY_RETAIL_ORDER_TYPE_1
}
if m.Capacity == 0 {
m.Capacity = CAPACITY_OTHER
}
if m.IntermarketSweepEligibility == 0 {
m.IntermarketSweepEligibility = INTERMARKET_SWEEP_ELIGIBILITY_NOT_ELIGIBLE
}
if m.CrossType == 0 {
m.CrossType = CROSS_TYPE_NO_CROSS
}
}
func (m MsgEnterOrder) String() string {
return fmt.Sprintf(
`MsgEnterOrder{OrderToken:"%s" BuySellIndicator:%c Shares:%d Stock:"%s" Price:%d TimeInForce:%d Firm:"%s" Display:%c Capacity:%c IntermarketSweepEligibility:%c MinimumQuantity:%d CrossType:%c}`,
m.OrderToken,
m.BuySellIndicator,
m.Shares,
m.Stock,
m.Price,
m.TimeInForce,
m.Firm,
m.Display,
m.Capacity,
m.IntermarketSweepEligibility,
m.MinimumQuantity,
m.CrossType,
)
}
type MsgCancelOrder struct {
// See: https://github.com/libtrading/libtrading/blob/master/include/libtrading/proto/ouch42_message.h
// MessageType char
OrderToken Token
Shares u32
}
func (m* MsgCancelOrder) MarshalBinary() ([]byte, error) {
// Arrays are faster for serialization
// See: https://stackoverflow.com/questions/58775651/which-is-the-go-way-putuint32-or-use-operator-directly
data := [...]byte{
MESSAGE_TYPE_CANCEL_ORDER, // MessageType
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // OrderToken
0, 0, 0, 0, // Shares
}
copy(data[1:15], m.OrderToken[:])
binary.BigEndian.PutUint32(data[15:19], m.Shares)
// Slices are faster to pass around
return data[:], nil
}
func (m* MsgCancelOrder) UnmarshalBinary(data []byte) error {
copy(m.OrderToken[:], data[1:15])
m.Shares = binary.BigEndian.Uint32(data[15:19])
return nil
}
func (m MsgCancelOrder) String() string {
return fmt.Sprintf(
`MsgCancelOrder{OrderToken:"%s" Shares:%d}`,
m.OrderToken,
m.Shares,
)
}
-- ouch42/token/token.go --
package token
import "stockbit.com/proto/ouch42"
func New(val string) (v ouch42.Token) {
// Spec: Alpha fields are left-justified and padded on the right with spaces
bp := copy(v[:], val)
for bp < len(v) {
v[bp] = 32
bp++
}
return v
}
-- ouch42/stock/stock.go --
package stock
import "stockbit.com/proto/ouch42"
func New(val string) (v ouch42.Stock) {
// Spec: Alpha fields are left-justified and padded on the right with spaces
bp := copy(v[:], val)
for bp < len(v) {
v[bp] = 32
bp++
}
return v
}
-- ouch42/firm/firm.go --
package firm
import "stockbit.com/proto/ouch42"
func New(val string) (v ouch42.Firm) {
// Spec: Alpha fields are left-justified and padded on the right with spaces
bp := copy(v[:], val)
for bp < len(v) {
v[bp] = 32
bp++
}
return v
}
@heri16
Copy link
Author

heri16 commented Aug 18, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment