Documentation
¶
Overview ¶
Package fastfix implements the FAST (FIX Adapted for STreaming) 1.1 wire protocol in pure Go, with no dependencies outside the standard library.
FAST is the binary encoding used by most exchange market-data feeds: a presence map plus field operators (constant, copy, default, delta, increment, tail) let the encoder omit anything the decoder can reconstruct from its dictionary, so a typical update costs a handful of bytes.
Usage ¶
Load a template definition XML into a Registry, then decode packets:
f, err := os.Open("templates.xml")
reg, err := fastfix.LoadTemplates(f)
dec := fastfix.NewDecoder(reg)
for pkt := range packets {
dec.Ctx.Reset() // see "Context resets" below
msgs, err := dec.DecodePacket(pkt)
for _, m := range msgs {
price, ok := m.Int("MDEntryPx")
...
}
}
Context resets ¶
This is the single most common source of wrong values. A Decoder's Context holds the operator dictionary that delta, copy, increment and tail operators build on. Whether that dictionary spans the whole stream or is rebuilt per packet is a property of the feed, not of FAST.
UDP market-data feeds typically encode each datagram independently, so the caller must call Ctx.Reset() at every packet boundary. Skip it and the first packet decodes correctly while every later one accumulates stale deltas — values drift instead of failing loudly. TCP session channels usually do want the dictionary carried across messages; there, do not reset. Check the feed's specification.
Templates ¶
LoadTemplates accepts both flattened template files and reference-composed ones that assemble messages from shared <templateRef> blocks. Templates with no numeric id are treated as reference-only building blocks: available for inlining, never registered as decodable messages. <typeRef> is a FIX type annotation with no wire meaning and is ignored.
Feeds commonly reference a reset template by id without declaring it in the XML; register it with Registry.RegisterReset.
Encoding ¶
Encode covers client-initiated session messages (Logon, MarketDataRequest, Logout, ...). It is deliberately lazy: every operator-bearing field sets its presence-map bit and transmits explicitly rather than exploiting the dictionary. That is valid FAST 1.1 and keeps decoder state consistent, but it is not meant for encoding a high-rate feed.
Scope ¶
Decoding covers scalars, groups and sequences across all six operators. Not implemented: encoding of groups and of the delta operator, and string (as opposed to byteVector) delta. These return an error rather than producing a wrong encoding.
Example ¶
Encode a session message and decode it back.
package main
import (
"fmt"
"strings"
"github.com/bandari-abhilash/fastfix"
)
const templatesXML = `<?xml version="1.0" encoding="UTF-8"?>
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
<template name="Header">
<string name="MessageType"/>
<string name="SendingTime"><tail/></string>
</template>
<template name="Logon" id="1">
<templateRef name="Header"/>
<uInt32 name="HeartBtInt"/>
<string name="Username"/>
<string name="AppID" presence="optional"/>
</template>
</templates>`
// Encode a session message and decode it back.
func main() {
reg, err := fastfix.LoadTemplates(strings.NewReader(templatesXML))
if err != nil {
panic(err)
}
packet, err := fastfix.Encode(reg.Get(1), map[string]any{
"MessageType": "A",
"SendingTime": "20260716-09:15:00.000000",
"HeartBtInt": 30,
"Username": "USER01",
})
if err != nil {
panic(err)
}
fmt.Printf("%d bytes on the wire\n", len(packet))
dec := fastfix.NewDecoder(reg)
msgs, err := dec.DecodePacket(packet)
if err != nil {
panic(err)
}
m := msgs[0]
hb, _ := m.Int("HeartBtInt")
fmt.Printf("%s user=%s heartbeat=%ds appID-present=%v\n",
m.Template.Name, m.ValueString("Username"), hb, m.Has("AppID"))
}
Output: 35 bytes on the wire Logon user=USER01 heartbeat=30s appID-present=false
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrEOF = errors.New("fast: end of packet")
ErrEOF is returned when the packet has no further bytes.
Functions ¶
Types ¶
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context carries decoder state across messages: the operator dictionary (global scope, keyed by field name, matching OpenFAST's default dictionary setup) and the last template id.
func NewContext ¶
func NewContext() *Context
func (*Context) Reset ¶
func (c *Context) Reset()
Reset clears the dictionaries and template state.
Many UDP market-data feeds encode every datagram against a fresh context rather than a stream-wide one. Against such a feed the caller MUST call Reset() at each packet boundary, otherwise delta and copy operators keep accumulating across packets and every value after the first is wrong. See TestPerPacketContextReset.
type Decimal ¶
Decimal is a FAST decimal value (mantissa * 10^exponent).
type Decoder ¶
type Decoder struct {
Reg *Registry
Ctx *Context
// ResetOnResetTemplate controls whether a reset template registered via
// Registry.RegisterReset clears the dictionaries MID-packet. It defaults
// to false: feeds commonly carry a reset template in the stream without
// intending a dictionary reset, and per-packet reset is the caller's job
// via Ctx.Reset().
ResetOnResetTemplate bool
}
Decoder decodes FAST messages against a template registry.
func NewDecoder ¶
func (*Decoder) DecodeNext ¶
DecodeNext decodes ONE message from the front of a (possibly incomplete) byte stream. Returns (msg, bytesConsumed, nil) on success, (nil, 0, nil) when the buffer holds only a partial message (feed more bytes and retry), or an error for malformed data. Context state is snapshotted and restored on a partial decode so a retry with more bytes decodes identically.
type Field ¶
type Field struct {
Name string
Kind Kind
Optional bool
Operator Op
HasInitial bool
InitInt int64
InitDec Decimal
InitStr string
Length *Field // sequences: the length field
Fields []*Field // groups/sequences: members
}
Field is one node of a template.
type GroupVal ¶
type GroupVal struct {
// contains filtered or unexported fields
}
GroupVal holds decoded field values by name.
func NewGroupValFrom ¶
NewGroupValFrom builds a GroupVal from decoded values; used by consumer tests to exercise parsing logic without a wire round-trip.
func (*GroupVal) Int ¶
Int mirrors OpenFAST's GetInt/GetLong: ok=false when the field is absent, and ok=false when the value is a Decimal with a fractional part (OpenFAST raises there rather than truncating). Callers that must not silently round should treat ok=false as "skip this entry".
func (*GroupVal) ValueString ¶
ValueString renders a field as text, returning "" for a null/absent field rather than reporting an error.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds templates by id.
func LoadTemplates ¶
LoadTemplates parses a FAST 1.1 template definition XML.
Two template-composition styles are supported:
- Flattened files, where every template inlines its own fields and sequences.
- Reference-composed files, which build messages from shared <templateRef> blocks (headers, trailers, sequence element templates) and carry <typeRef> annotations.
A <templateRef name="X"/> is expanded in place by inlining template X's fields (recursively); <typeRef> is a FIX type annotation with no bearing on the wire encoding and is ignored. Templates without a numeric id are reference-only building blocks: they are available for expansion but are not registered for decoding.
func (*Registry) RegisterReset ¶
RegisterReset registers a FAST reset template under the given id. Feeds typically reference a reset template by id without declaring it in the template XML, so the caller supplies it programmatically — e.g. reg.RegisterReset(120, "FastResetTemplate").