├── .github
└── workflows
│ ├── .golangci.yml
│ └── go.yml
├── .gitignore
├── LICENSE
├── README.md
├── acceptor.go
├── cmd
└── fixgen
│ └── main.go
├── conn.go
├── errors.go
├── examples
├── acceptor
│ └── main.go
└── initiator
│ └── main.go
├── fix
├── buffer
│ └── buffer.go
├── component.go
├── convertor.go
├── convertor_test.go
├── encoding
│ ├── unmarshaler.go
│ ├── unmarshaller_test.go
│ └── validator.go
├── fix_item.go
├── generator.go
├── generator_test.go
├── group.go
├── group_test.go
├── key_value.go
├── key_value_test.go
├── message.go
├── message_test.go
├── storage.go
├── types.go
├── types_test.go
└── utils.go
├── generator
├── generator.go
├── generator_test.go
├── required.go
├── templates.go
├── testdata
│ ├── fix.4.4.xml
│ └── types.xml
├── type_caster.go
└── xml.go
├── go.mod
├── go.sum
├── handler.go
├── handler_factory.go
├── handler_func_pool.go
├── initiator.go
├── session
├── logon_settings.go
├── messages
│ ├── builder.go
│ ├── contstants.go
│ ├── execution_report.go
│ ├── header.go
│ ├── heartbeat.go
│ ├── logon.go
│ ├── logout.go
│ ├── market_data_request.go
│ ├── mock_message.go
│ ├── new_order_single.go
│ ├── order_cancel_request.go
│ ├── reject.go
│ ├── resend_request.go
│ ├── sequence_reset.go
│ ├── test_request.go
│ └── trailer.go
├── opts.go
├── session.go
├── session_test.go
├── storage.go
└── unmarshaller.go
├── source
├── fix44.xml
└── types.xml
├── storages
└── memory
│ └── storage.go
├── tests
├── acceptor.go
├── acceptor_initiator_test.go
├── fix44
│ ├── altmdsourcegrp.go
│ ├── enum_applqueueaction.go
│ ├── enum_applqueueresolution.go
│ ├── enum_corporateaction.go
│ ├── enum_cpprogram.go
│ ├── enum_deletereason.go
│ ├── enum_encryptmethod.go
│ ├── enum_eventtype.go
│ ├── enum_execinst.go
│ ├── enum_financialstatus.go
│ ├── enum_instrregistry.go
│ ├── enum_mdentrytype.go
│ ├── enum_mdreqrejreason.go
│ ├── enum_mdupdateaction.go
│ ├── enum_mdupdatetype.go
│ ├── enum_msgdirection.go
│ ├── enum_msgtype.go
│ ├── enum_openclosesettlflag.go
│ ├── enum_product.go
│ ├── enum_quotecondition.go
│ ├── enum_scope.go
│ ├── enum_securityidsource.go
│ ├── enum_securitytype.go
│ ├── enum_sessionrejectreason.go
│ ├── enum_subscriptionrequesttype.go
│ ├── enum_symbolsfx.go
│ ├── enum_tickdirection.go
│ ├── enum_timeinforce.go
│ ├── enum_tradecondition.go
│ ├── eventsgrp.go
│ ├── fields.go
│ ├── header.go
│ ├── heartbeat.go
│ ├── hopsgrp.go
│ ├── instrument.go
│ ├── instrumentleg.go
│ ├── legsecurityaltidgrp.go
│ ├── legsgrp.go
│ ├── logon.go
│ ├── logout.go
│ ├── marketdataincrementalrefresh.go
│ ├── marketdatarequest.go
│ ├── marketdatarequestreject.go
│ ├── marketdatasnapshotfullrefresh.go
│ ├── mdentriesgrp.go
│ ├── mdentrytypesgrp.go
│ ├── msgtypesgrp.go
│ ├── reject.go
│ ├── relatedsymgrp.go
│ ├── resendrequest.go
│ ├── securityaltidgrp.go
│ ├── sequencereset.go
│ ├── testrequest.go
│ ├── tradingsessionsgrp.go
│ ├── trailer.go
│ ├── underlyinginstrument.go
│ ├── underlyingsecurityaltidgrp.go
│ ├── underlyingsgrp.go
│ ├── underlyingstipsgrp.go
│ └── underlyingstipulations.go
├── initiator.go
├── opts.go
└── utils.go
└── utils
├── event_handler_pool.go
├── helpers.go
├── timed_wg.go
├── timed_wg_test.go
├── timer.go
└── timer_test.go
/.github/workflows/.golangci.yml:
--------------------------------------------------------------------------------
1 | run:
2 | deadline: 5m
3 | issues-exit-code: 1
4 | tests: false
5 | issues:
6 | exclude-rules:
7 | # Exclude some linters from running on tests files.
8 | - path: .generated.go
9 | linters:
10 | - revive
11 | max-same-issues: 15
12 | max-issues-per-linter: 15
13 |
14 | linters-settings:
15 | govet:
16 | check-shadowing: false
17 | gofmt:
18 | simplify: true
19 | gocritic:
20 | disabled-checks:
21 | - paramTypeCombine
22 | settings:
23 | hugeParam:
24 | sizeThreshold: 80 #default
25 | enabled-tags:
26 | - performance
27 | - style
28 | - experimental
29 | - diagnostic
30 | gocyclo:
31 | max-complexity: 15
32 | misspell:
33 | locale: US
34 |
35 | linters:
36 | enable:
37 | - gocyclo
38 | - gocritic
39 | - gofmt
40 | - goconst
41 | - gosec
42 | - revive
43 | - gochecknoinits
44 | - megacheck
45 | - misspell
46 | - unconvert
47 | - unparam
48 | - depguard
49 | - nakedret
50 | - prealloc
51 | - exportloopref
52 | - govet
53 | - staticcheck
54 | - deadcode
55 | - gosimple
56 | - ineffassign
57 | - structcheck
58 | - unused
59 | - varcheck
60 | disable:
61 | - dupl
62 | - errcheck
63 | disable-all: false
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | lint:
11 | name: Lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: golangci/golangci-lint-action@v4
16 | with:
17 | version: latest
18 | config: .golangci.yml # Явно указываем конфиг
19 |
20 | test:
21 | name: Test
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v4
25 | - uses: actions/setup-go@v5
26 | with:
27 | go-version: 1.24
28 |
29 | - name: Unit tests
30 | run: go test -race ./...
31 |
32 | release:
33 | name: Release
34 | runs-on: ubuntu-latest
35 | needs:
36 | - lint
37 | - test
38 | steps:
39 | - uses: go-semantic-release/action@v1
40 | id: semrel
41 | with:
42 | github-token: ${{ secrets.GITHUB_TOKEN }}
43 | changelog-generator-opt: "emojis=true"
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | *.env
3 | .idea
--------------------------------------------------------------------------------
/acceptor.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net"
7 | "strings"
8 | "time"
9 |
10 | "golang.org/x/sync/errgroup"
11 |
12 | "github.com/b2broker/simplefix-go/utils"
13 | )
14 |
15 | // Sender is an interface implemented by any structure that can issue a SendingMessage.
16 | type Sender interface {
17 | Send(message SendingMessage) error
18 | }
19 |
20 | // OutgoingHandlerFunc is used for handling outgoing messages.
21 | type OutgoingHandlerFunc func(msg SendingMessage) bool
22 |
23 | // IncomingHandlerFunc is used for handling incoming messages.
24 | type IncomingHandlerFunc func(data []byte) bool
25 |
26 | // AcceptorHandler is a set of methods providing basic functionality to the Acceptor.
27 | type AcceptorHandler interface {
28 | ServeIncoming(msg []byte)
29 | Outgoing() <-chan []byte
30 | Run() error
31 | Stop()
32 | StopWithError(err error)
33 | CloseErrorChan()
34 | Send(message SendingMessage) error
35 | SendBatch(messages []SendingMessage) error
36 | SendBuffered(message SendingMessage) error
37 | SendRaw(data []byte) error
38 | RemoveIncomingHandler(msgType string, id int64) (err error)
39 | RemoveOutgoingHandler(msgType string, id int64) (err error)
40 | HandleIncoming(msgType string, handle IncomingHandlerFunc) (id int64)
41 | HandleOutgoing(msgType string, handle OutgoingHandlerFunc) (id int64)
42 | OnDisconnect(handlerFunc utils.EventHandlerFunc)
43 | OnConnect(handlerFunc utils.EventHandlerFunc)
44 | OnStopped(handlerFunc utils.EventHandlerFunc)
45 | Context() context.Context
46 | }
47 |
48 | // HandlerFactory creates handlers for the Acceptor.
49 | type HandlerFactory interface {
50 | MakeHandler(ctx context.Context) AcceptorHandler
51 | }
52 |
53 | // Acceptor is a server-side service used for handling client connections.
54 | type Acceptor struct {
55 | listener net.Listener
56 | factory HandlerFactory
57 | size int
58 | handleNewClient func(handler AcceptorHandler)
59 | writeTimeout time.Duration
60 |
61 | ctx context.Context
62 | cancel context.CancelFunc
63 | }
64 |
65 | // NewAcceptor is used to create a new Acceptor instance.
66 | func NewAcceptor(listener net.Listener, factory HandlerFactory, writeTimeout time.Duration, handleNewClient func(handler AcceptorHandler)) *Acceptor {
67 | s := &Acceptor{
68 | factory: factory,
69 | listener: listener,
70 | handleNewClient: handleNewClient,
71 | writeTimeout: writeTimeout,
72 | }
73 |
74 | s.ctx, s.cancel = context.WithCancel(context.Background())
75 |
76 | return s
77 | }
78 |
79 | // Close is called to cancel the Acceptor context and close a connection.
80 | func (s *Acceptor) Close() {
81 | s.cancel()
82 | }
83 |
84 | // ListenAndServe is used for listening and maintaining connections.
85 | // It verifies and accepts connection requests from new clients.
86 | func (s *Acceptor) ListenAndServe() error {
87 | listenErr := make(chan error, 1)
88 | defer s.Close()
89 | defer s.listener.Close()
90 |
91 | go func() {
92 | for {
93 | conn, err := s.listener.Accept()
94 | if err != nil {
95 | listenErr <- err
96 | return
97 | }
98 |
99 | go s.serve(s.ctx, conn)
100 | }
101 | }()
102 |
103 | for {
104 | select {
105 | case err := <-listenErr:
106 | return fmt.Errorf("could not accept connection: %w", err)
107 |
108 | case <-s.ctx.Done():
109 | return nil
110 | }
111 | }
112 | }
113 |
114 | // serve is used for listening and maintaining connections.
115 | // It handles client connections opened for ClientConn instances.
116 | func (s *Acceptor) serve(parentCtx context.Context, netConn net.Conn) {
117 | ctx, cancel := context.WithCancel(parentCtx)
118 | defer cancel()
119 |
120 | conn := NewConn(parentCtx, netConn, s.size, s.writeTimeout)
121 | defer conn.Close()
122 |
123 | cancelFun := func() {
124 | conn.Close()
125 | cancel()
126 | }
127 |
128 | handler := s.factory.MakeHandler(ctx)
129 | defer handler.CloseErrorChan()
130 |
131 | eg := errgroup.Group{}
132 |
133 | eg.Go(func() error {
134 | defer cancelFun()
135 |
136 | err := conn.serve()
137 | if err != nil {
138 | err = fmt.Errorf("%s: %w", err, ErrConnClosed)
139 | if !strings.Contains(err.Error(), "use of closed network connection") {
140 | handler.StopWithError(err)
141 | }
142 | }
143 |
144 | return err
145 | })
146 |
147 | if s.handleNewClient != nil {
148 | s.handleNewClient(handler)
149 | }
150 |
151 | eg.Go(func() error {
152 | defer cancelFun()
153 |
154 | return handler.Run()
155 | })
156 |
157 | eg.Go(func() error {
158 | defer cancelFun()
159 |
160 | for {
161 | select {
162 | case <-ctx.Done():
163 | return nil
164 |
165 | case msg, ok := <-handler.Outgoing():
166 | if !ok {
167 | return nil
168 | }
169 |
170 | err := conn.Write(msg)
171 | if err != nil {
172 | return err
173 | }
174 | }
175 | }
176 | })
177 |
178 | eg.Go(func() error {
179 | defer cancelFun()
180 |
181 | for {
182 | select {
183 | case <-ctx.Done():
184 | return nil
185 |
186 | case msg, ok := <-conn.Reader():
187 | if !ok {
188 | return nil
189 | }
190 | handler.ServeIncoming(msg)
191 | }
192 | }
193 | })
194 |
195 | _ = eg.Wait()
196 | }
197 |
--------------------------------------------------------------------------------
/cmd/fixgen/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "path/filepath"
8 |
9 | "github.com/b2broker/simplefix-go/generator"
10 | "github.com/b2broker/simplefix-go/utils"
11 | )
12 |
13 | func main() {
14 | var err error
15 |
16 | outputDir := flag.String("o", "./fix44/", "output directory")
17 | typesMappingPath := flag.String("t", "./source/types.xml", "path to XML file with types mapping")
18 | sourceXMLPath := flag.String("s", "./source/fix44.xml", "path to main XML file")
19 |
20 | flag.Parse()
21 |
22 | doc := &generator.Doc{}
23 | if err = utils.ParseXML(*sourceXMLPath, doc); err != nil {
24 | panic(fmt.Errorf("could not make Doc XML: %s", err))
25 | }
26 |
27 | config := &generator.Config{}
28 | if err = utils.ParseXML(*typesMappingPath, config); err != nil {
29 | panic(fmt.Errorf("could not make Doc XML: %s", err))
30 | }
31 |
32 | g := generator.NewGenerator(doc, config, filepath.Base(*outputDir))
33 |
34 | err = os.MkdirAll(*outputDir, os.ModePerm)
35 | if err != nil {
36 | panic(err)
37 | }
38 |
39 | err = g.Execute(*outputDir)
40 | if err != nil {
41 | panic(err)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/conn.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "context"
7 | "fmt"
8 | "net"
9 | "sync"
10 | "time"
11 |
12 | "golang.org/x/sync/errgroup"
13 | )
14 |
15 | // ErrConnClosed handles connection errors.
16 | var ErrConnClosed = fmt.Errorf("the reader is closed")
17 |
18 | const (
19 | endOfMsgTag = "10="
20 | )
21 |
22 | // Conn is a net.Conn wrapper that is used for handling split messages.
23 | type Conn struct {
24 | reader chan []byte
25 | writer chan []byte
26 | conn net.Conn
27 |
28 | ctx context.Context
29 | cancel context.CancelFunc
30 |
31 | writeDeadline time.Duration
32 | closeOnce sync.Once
33 | }
34 |
35 | // NewConn is called to create a new connection.
36 | func NewConn(ctx context.Context, conn net.Conn, msgBuffSize int, writeDeadline time.Duration) *Conn {
37 | c := &Conn{
38 | reader: make(chan []byte, msgBuffSize),
39 | writer: make(chan []byte, msgBuffSize),
40 | writeDeadline: writeDeadline,
41 | conn: conn,
42 | }
43 |
44 | c.ctx, c.cancel = context.WithCancel(ctx)
45 |
46 | return c
47 | }
48 |
49 | // Close is called to cancel a connection context and close a connection.
50 | func (c *Conn) Close() {
51 | c.closeOnce.Do(func() {
52 | _ = c.conn.Close()
53 | c.cancel()
54 | })
55 | }
56 |
57 | func (c *Conn) serve() error {
58 | defer close(c.writer)
59 | defer close(c.reader)
60 |
61 | eg := errgroup.Group{}
62 |
63 | eg.Go(c.runReader)
64 |
65 | return eg.Wait()
66 | }
67 |
68 | func (c *Conn) runReader() error {
69 | defer c.cancel()
70 | r := bufio.NewReader(c.conn)
71 |
72 | var msg []byte
73 | for {
74 | select {
75 | case <-c.ctx.Done():
76 | return nil
77 | default:
78 | }
79 |
80 | buff, err := r.ReadBytes(byte(1))
81 | if err != nil {
82 | return fmt.Errorf("read error: %w", err)
83 | }
84 |
85 | msg = append(msg, buff...)
86 | if len(buff) >= 3 && bytes.Equal(buff[0:3], []byte(endOfMsgTag)) {
87 | c.reader <- msg
88 | msg = []byte{}
89 | }
90 | }
91 | }
92 |
93 | // Reader returns a separate channel for handing incoming messages.
94 | func (c *Conn) Reader() <-chan []byte {
95 | return c.reader
96 | }
97 |
98 | // Write is called to send messages to an outgoing socket.
99 | func (c *Conn) Write(msg []byte) error {
100 | select {
101 | case <-c.ctx.Done():
102 | return ErrConnClosed
103 | default:
104 | }
105 |
106 | if err := c.conn.SetWriteDeadline(time.Now().Add(c.writeDeadline)); err != nil {
107 | c.cancel()
108 | return fmt.Errorf("set write deadline error: %w", err)
109 | }
110 | _, err := c.conn.Write(msg)
111 | if err != nil {
112 | c.cancel()
113 | return fmt.Errorf("write error: %w", err)
114 | }
115 |
116 | return nil
117 | }
118 |
--------------------------------------------------------------------------------
/errors.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import "errors"
4 |
5 | var (
6 | ErrNotEnoughMessages = errors.New("not enough messages in the storage")
7 | ErrInvalidBoundaries = errors.New("invalid boundaries")
8 | ErrInvalidSequence = errors.New("unexpected sequence index")
9 | )
10 |
--------------------------------------------------------------------------------
/examples/acceptor/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "github.com/b2broker/simplefix-go/storages/memory"
7 | "net"
8 | "strconv"
9 | "time"
10 |
11 | simplefixgo "github.com/b2broker/simplefix-go"
12 | "github.com/b2broker/simplefix-go/fix"
13 | "github.com/b2broker/simplefix-go/fix/encoding"
14 | "github.com/b2broker/simplefix-go/session"
15 | "github.com/b2broker/simplefix-go/session/messages"
16 | fixgen "github.com/b2broker/simplefix-go/tests/fix44"
17 | )
18 |
19 | func mustConvToInt(s string) int {
20 | i, err := strconv.Atoi(s)
21 | if err != nil {
22 | panic(err)
23 | }
24 |
25 | return i
26 | }
27 |
28 | // TODO: move boilerplate to generator.
29 | var pseudoGeneratedOpts = session.Opts{
30 | MessageBuilders: session.MessageBuilders{
31 | HeaderBuilder: fixgen.Header{}.New(),
32 | TrailerBuilder: fixgen.Trailer{}.New(),
33 | LogonBuilder: fixgen.Logon{}.New(),
34 | LogoutBuilder: fixgen.Logout{}.New(),
35 | RejectBuilder: fixgen.Reject{}.New(),
36 | HeartbeatBuilder: fixgen.Heartbeat{}.New(),
37 | TestRequestBuilder: fixgen.TestRequest{}.New(),
38 | ResendRequestBuilder: fixgen.ResendRequest{}.New(),
39 | },
40 | Tags: &messages.Tags{
41 | MsgType: mustConvToInt(fixgen.FieldMsgType),
42 | MsgSeqNum: mustConvToInt(fixgen.FieldMsgSeqNum),
43 | HeartBtInt: mustConvToInt(fixgen.FieldHeartBtInt),
44 | EncryptedMethod: mustConvToInt(fixgen.FieldEncryptMethod),
45 | },
46 | AllowedEncryptedMethods: map[string]struct{}{
47 | fixgen.EnumEncryptMethodNoneother: {},
48 | },
49 | SessionErrorCodes: &messages.SessionErrorCodes{
50 | InvalidTagNumber: mustConvToInt(fixgen.EnumSessionRejectReasonInvalidtagnumber),
51 | RequiredTagMissing: mustConvToInt(fixgen.EnumSessionRejectReasonRequiredtagmissing),
52 | TagNotDefinedForMessageType: mustConvToInt(fixgen.EnumSessionRejectReasonTagNotDefinedForThisMessageType),
53 | UndefinedTag: mustConvToInt(fixgen.EnumSessionRejectReasonUndefinedtag),
54 | TagSpecialWithoutValue: mustConvToInt(fixgen.EnumSessionRejectReasonTagspecifiedwithoutavalue),
55 | IncorrectValue: mustConvToInt(fixgen.EnumSessionRejectReasonValueisincorrectoutofrangeforthistag),
56 | IncorrectDataFormatValue: mustConvToInt(fixgen.EnumSessionRejectReasonIncorrectdataformatforvalue),
57 | DecryptionProblem: mustConvToInt(fixgen.EnumSessionRejectReasonDecryptionproblem),
58 | SignatureProblem: mustConvToInt(fixgen.EnumSessionRejectReasonSignatureproblem),
59 | CompIDProblem: mustConvToInt(fixgen.EnumSessionRejectReasonCompidproblem),
60 | Other: mustConvToInt(fixgen.EnumSessionRejectReasonOther),
61 | },
62 | }
63 |
64 | func main() {
65 | listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", 9991))
66 | if err != nil {
67 | panic(err)
68 | }
69 | fmt.Println("Connected")
70 |
71 | handlerFactory := simplefixgo.NewAcceptorHandlerFactory(fixgen.FieldMsgType, 10)
72 |
73 | exampleStorage := memory.NewStorage()
74 |
75 | server := simplefixgo.NewAcceptor(listener, handlerFactory, time.Second*5, func(handler simplefixgo.AcceptorHandler) {
76 | sess, err := session.NewAcceptorSession(
77 | &pseudoGeneratedOpts,
78 | handler,
79 | &session.LogonSettings{
80 | HeartBtInt: 30,
81 | LogonTimeout: time.Second * 30,
82 | HeartBtLimits: &session.IntLimits{
83 | Min: 5,
84 | Max: 60,
85 | },
86 | },
87 | func(request *session.LogonSettings) (err error) {
88 | fmt.Printf(
89 | "Logon passed for '%s' (%s)\n",
90 | request.Username,
91 | request.Password,
92 | )
93 |
94 | return nil
95 | },
96 | exampleStorage,
97 | exampleStorage,
98 | )
99 | if err != nil {
100 | panic(err)
101 | }
102 |
103 | _ = sess.Run()
104 |
105 | handler.HandleIncoming(simplefixgo.AllMsgTypes, func(msg []byte) bool {
106 | fmt.Println("incoming", string(bytes.ReplaceAll(msg, fix.Delimiter, []byte("|"))))
107 | return true
108 | })
109 | handler.HandleOutgoing(simplefixgo.AllMsgTypes, func(msg simplefixgo.SendingMessage) bool {
110 | data, err := msg.ToBytes()
111 | if err != nil {
112 | panic(err)
113 | }
114 | fmt.Println("outgoing", string(bytes.ReplaceAll(data, fix.Delimiter, []byte("|"))))
115 | return true
116 | })
117 |
118 | handler.HandleIncoming(fixgen.MsgTypeMarketDataRequest, func(msg []byte) bool {
119 | request := fixgen.NewMarketDataRequest()
120 | err := encoding.Unmarshal(request, msg)
121 | if err != nil {
122 | panic(err)
123 | }
124 |
125 | for _, relatedSymbolEntry := range request.RelatedSymGrp().Entries() {
126 | fmt.Println(relatedSymbolEntry.Instrument().Symbol())
127 | }
128 |
129 | return true
130 | })
131 | })
132 |
133 | panic(fmt.Errorf("the server was stopped: %s", server.ListenAndServe()))
134 | }
135 |
--------------------------------------------------------------------------------
/examples/initiator/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "github.com/b2broker/simplefix-go/storages/memory"
8 | "net"
9 | "strconv"
10 | "time"
11 |
12 | simplefixgo "github.com/b2broker/simplefix-go"
13 | "github.com/b2broker/simplefix-go/fix"
14 | "github.com/b2broker/simplefix-go/fix/encoding"
15 | "github.com/b2broker/simplefix-go/session"
16 | "github.com/b2broker/simplefix-go/session/messages"
17 | fixgen "github.com/b2broker/simplefix-go/tests/fix44"
18 | "github.com/b2broker/simplefix-go/utils"
19 | )
20 |
21 | func mustConvToInt(s string) int {
22 | i, err := strconv.Atoi(s)
23 | if err != nil {
24 | panic(err)
25 | }
26 |
27 | return i
28 | }
29 |
30 | var pseudoGeneratedOpts = session.Opts{
31 | MessageBuilders: session.MessageBuilders{
32 | HeaderBuilder: fixgen.Header{}.New(),
33 | TrailerBuilder: fixgen.Trailer{}.New(),
34 | LogonBuilder: fixgen.Logon{}.New(),
35 | LogoutBuilder: fixgen.Logout{}.New(),
36 | RejectBuilder: fixgen.Reject{}.New(),
37 | HeartbeatBuilder: fixgen.Heartbeat{}.New(),
38 | TestRequestBuilder: fixgen.TestRequest{}.New(),
39 | ResendRequestBuilder: fixgen.ResendRequest{}.New(),
40 | },
41 | Tags: &messages.Tags{
42 | MsgType: mustConvToInt(fixgen.FieldMsgType),
43 | MsgSeqNum: mustConvToInt(fixgen.FieldMsgSeqNum),
44 | HeartBtInt: mustConvToInt(fixgen.FieldHeartBtInt),
45 | EncryptedMethod: mustConvToInt(fixgen.FieldEncryptMethod),
46 | },
47 | AllowedEncryptedMethods: map[string]struct{}{
48 | fixgen.EnumEncryptMethodNoneother: {},
49 | },
50 | SessionErrorCodes: &messages.SessionErrorCodes{
51 | InvalidTagNumber: mustConvToInt(fixgen.EnumSessionRejectReasonInvalidtagnumber),
52 | RequiredTagMissing: mustConvToInt(fixgen.EnumSessionRejectReasonRequiredtagmissing),
53 | TagNotDefinedForMessageType: mustConvToInt(fixgen.EnumSessionRejectReasonTagNotDefinedForThisMessageType),
54 | UndefinedTag: mustConvToInt(fixgen.EnumSessionRejectReasonUndefinedtag),
55 | TagSpecialWithoutValue: mustConvToInt(fixgen.EnumSessionRejectReasonTagspecifiedwithoutavalue),
56 | IncorrectValue: mustConvToInt(fixgen.EnumSessionRejectReasonValueisincorrectoutofrangeforthistag),
57 | IncorrectDataFormatValue: mustConvToInt(fixgen.EnumSessionRejectReasonIncorrectdataformatforvalue),
58 | DecryptionProblem: mustConvToInt(fixgen.EnumSessionRejectReasonDecryptionproblem),
59 | SignatureProblem: mustConvToInt(fixgen.EnumSessionRejectReasonSignatureproblem),
60 | CompIDProblem: mustConvToInt(fixgen.EnumSessionRejectReasonCompidproblem),
61 | Other: mustConvToInt(fixgen.EnumSessionRejectReasonOther),
62 | },
63 | }
64 |
65 | func main() {
66 | conn, err := net.Dial("tcp", fmt.Sprintf(":%d", 9991))
67 | if err != nil {
68 | panic(fmt.Errorf("could not dial: %s", err))
69 | }
70 |
71 | ctx, cancel := context.WithCancel(context.Background())
72 | defer cancel()
73 |
74 | handler := simplefixgo.NewInitiatorHandler(ctx, fixgen.FieldMsgType, 10)
75 | client := simplefixgo.NewInitiator(conn, handler, 10, time.Second*5)
76 |
77 | handler.OnConnect(func() bool {
78 | return true
79 | })
80 |
81 | exampleStorage := memory.NewStorage()
82 |
83 | sess, err := session.NewInitiatorSession(
84 | handler,
85 | &pseudoGeneratedOpts,
86 | &session.LogonSettings{
87 | TargetCompID: "Server",
88 | SenderCompID: "Client",
89 | HeartBtInt: 5,
90 | EncryptMethod: fixgen.EnumEncryptMethodNoneother,
91 | Password: "password",
92 | Username: "login",
93 | },
94 | exampleStorage,
95 | exampleStorage,
96 | )
97 | if err != nil {
98 | panic(err)
99 | }
100 |
101 | handler.HandleIncoming(fixgen.MsgTypeLogon, func(msg []byte) bool {
102 | incomingLogon := fixgen.NewLogon()
103 | err := encoding.Unmarshal(incomingLogon, msg)
104 | _, _ = incomingLogon, err
105 | return true
106 | })
107 |
108 | handler.HandleIncoming(simplefixgo.AllMsgTypes, func(msg []byte) bool {
109 | fmt.Println("incoming", string(bytes.ReplaceAll(msg, fix.Delimiter, []byte("|"))))
110 | return true
111 | })
112 | handler.HandleOutgoing(simplefixgo.AllMsgTypes, func(msg simplefixgo.SendingMessage) bool {
113 | data, err := msg.ToBytes()
114 | if err != nil {
115 | panic(err)
116 | }
117 | fmt.Println("outgoing", string(bytes.ReplaceAll(data, fix.Delimiter, []byte("|"))))
118 | return true
119 | })
120 |
121 | sess.OnChangeState(utils.EventLogon, func() bool {
122 | err := sess.Send(fixgen.CreateMarketDataRequest(
123 | "test",
124 | fixgen.EnumSubscriptionRequestTypeSnapshot,
125 | 20,
126 | fixgen.NewMDEntryTypesGrp(),
127 | fixgen.NewRelatedSymGrp().
128 | AddEntry(fixgen.NewRelatedSymEntry().SetInstrument(fixgen.NewInstrument().SetSymbol("BTC/USDT"))).
129 | AddEntry(fixgen.NewRelatedSymEntry().SetInstrument(fixgen.NewInstrument().SetSymbol("ETH/USDT"))),
130 | ))
131 | if err != nil {
132 | panic(err)
133 | }
134 |
135 | return true
136 | })
137 |
138 | go func() {
139 | time.Sleep(time.Second * 10)
140 | fmt.Println("resend request after 10 seconds")
141 | _ = sess.Send(fixgen.ResendRequest{}.New().SetFieldBeginSeqNo(2).SetFieldEndSeqNo(3))
142 | }()
143 |
144 | _ = sess.Run()
145 |
146 | panic(client.Serve())
147 | }
148 |
--------------------------------------------------------------------------------
/fix/buffer/buffer.go:
--------------------------------------------------------------------------------
1 | package buffer
2 |
3 | import "bytes"
4 |
5 | type MessageByteBuffers struct {
6 | msgBuffer *bytes.Buffer
7 | bodyBuffer *bytes.Buffer
8 | typeBuffer *bytes.Buffer
9 | headerBuffer *bytes.Buffer
10 | }
11 |
12 | func NewMessageByteBuffers(size int) *MessageByteBuffers {
13 | return &MessageByteBuffers{
14 | typeBuffer: bytes.NewBuffer(make([]byte, 0, 5)), // 35=AA
15 | msgBuffer: bytes.NewBuffer(make([]byte, 0, size)),
16 | bodyBuffer: bytes.NewBuffer(make([]byte, 0, size)),
17 | headerBuffer: bytes.NewBuffer(make([]byte, 0, size)),
18 | }
19 | }
20 |
21 | func (m *MessageByteBuffers) Reset() {
22 | m.msgBuffer.Reset()
23 | m.bodyBuffer.Reset()
24 | m.typeBuffer.Reset()
25 | m.headerBuffer.Reset()
26 | }
27 |
28 | func (m *MessageByteBuffers) GetMessageBuffer() *bytes.Buffer {
29 | return m.msgBuffer
30 | }
31 | func (m *MessageByteBuffers) GetBodyBuffer() *bytes.Buffer {
32 | return m.bodyBuffer
33 | }
34 | func (m *MessageByteBuffers) GetHeaderBuffer() *bytes.Buffer {
35 | return m.headerBuffer
36 | }
37 | func (m *MessageByteBuffers) GetTypeBuffer() *bytes.Buffer {
38 | return m.typeBuffer
39 | }
40 |
--------------------------------------------------------------------------------
/fix/component.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | // Component is an array of various FIX entities.
10 | // It may contain a KeyValue, a Group and another Component.
11 | type Component struct {
12 | items []Item
13 | }
14 |
15 | // NewComponent is used to create a new Component instance.
16 | func NewComponent(items ...Item) *Component {
17 | return &Component{items: items}
18 | }
19 |
20 | // Items returns Component items.
21 | func (c *Component) Items() Items {
22 | return c.items
23 | }
24 |
25 | // AsComponent returns a specified component.
26 | // This is required for structures that integrate this component.
27 | func (c *Component) AsComponent() *Component {
28 | return c
29 | }
30 |
31 | // AsTemplate returns a new structure with the same set of items
32 | // as in a specified component (these items are assigned empty values).
33 | func (c *Component) AsTemplate() Items {
34 | tmp := make([]Item, len(c.items))
35 |
36 | for i, item := range c.items {
37 | switch value := item.(type) {
38 | case *KeyValue:
39 | tmp[i] = value.AsTemplate()
40 |
41 | case *Group:
42 | tmp[i] = NewGroup(value.NoTag(), value.AsTemplate()...)
43 |
44 | case *Component:
45 | tmp[i] = NewComponent(value.AsTemplate()...)
46 | }
47 | }
48 |
49 | return tmp
50 | }
51 |
52 | // ToBytes returns a representation of a message which is native to FIX.
53 | func (c *Component) ToBytes() []byte {
54 | var msg [][]byte
55 | for _, item := range c.items {
56 | itemB := item.ToBytes()
57 | if itemB != nil {
58 | msg = append(msg, itemB)
59 | }
60 | }
61 |
62 | if len(msg) == 0 {
63 | return nil
64 | }
65 |
66 | return joinBody(msg...)
67 | }
68 | func (c *Component) IsEmpty() bool {
69 | for _, item := range c.items {
70 | if !item.IsEmpty() {
71 | return false
72 | }
73 | }
74 | return true
75 | }
76 | func (c *Component) WriteBytes(writer *bytes.Buffer) bool {
77 | addDelimeter := false
78 | written := false
79 | for i, item := range c.items {
80 | if !item.IsEmpty() && addDelimeter {
81 | _ = writer.WriteByte(DelimiterChar)
82 | addDelimeter = false
83 | }
84 | if item.WriteBytes(writer) {
85 | written = true
86 | if i <= len(c.items)-1 {
87 | addDelimeter = true
88 | }
89 | }
90 | }
91 |
92 | return written
93 | }
94 |
95 | // Get returns a specific component item identified by its sequence number.
96 | // Such item may be a *KeyValue, *Component or *Group.
97 | func (c *Component) Get(id int) Item {
98 | return c.items[id]
99 | }
100 |
101 | // Set replaces a component item identified by its sequence number.
102 | func (c *Component) Set(id int, v Item) {
103 | c.items[id] = v
104 | }
105 |
106 | // SetField is used to define an internal field for any item.
107 | func (c *Component) SetField(id int, v Item) {
108 | c.items[id] = v
109 | }
110 |
111 | // SetGroup is used to define an internal group for an item.
112 | func (c *Component) SetGroup(id int, v *Group) {
113 | c.items[id] = v
114 | }
115 |
116 | // SetComponent is used to define an internal component for an item.
117 | func (c *Component) SetComponent(id int, v *Component) {
118 | c.items[id] = v
119 | }
120 |
121 | // String returns a string representation of a component.
122 | func (c *Component) String() string {
123 | var items []string
124 | for _, item := range c.items {
125 | itemStr := item.String()
126 | if itemStr != "" {
127 | items = append(items, itemStr)
128 | }
129 | }
130 | return fmt.Sprintf("[%s]", strings.Join(items, ", "))
131 | }
132 |
--------------------------------------------------------------------------------
/fix/convertor.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/b2broker/simplefix-go/fix/buffer"
7 | )
8 |
9 | type MessageByteConverter struct {
10 | pool sync.Pool
11 | }
12 |
13 | func NewMessageByteConverter(bufferSize int) *MessageByteConverter {
14 | b := &MessageByteConverter{
15 | pool: sync.Pool{
16 | New: func() interface{} {
17 | return buffer.NewMessageByteBuffers(bufferSize)
18 | },
19 | },
20 | }
21 | return b
22 | }
23 |
24 | type ConvertableMessage interface {
25 | ToBytesBuffered(buffers *buffer.MessageByteBuffers) ([]byte, error)
26 | }
27 |
28 | func (m *MessageByteConverter) ConvertToBytes(message ConvertableMessage) ([]byte, error) {
29 | buffers := m.pool.Get().(*buffer.MessageByteBuffers)
30 | buffers.Reset()
31 | defer m.pool.Put(buffers)
32 |
33 | return message.ToBytesBuffered(buffers)
34 | }
35 |
--------------------------------------------------------------------------------
/fix/convertor_test.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "math/rand"
6 | "strconv"
7 | "sync"
8 | "testing"
9 | "time"
10 | )
11 |
12 | func TestMessageByteConverter_ConvertToBytes(t *testing.T) {
13 | conv := NewMessageByteConverter(100)
14 | msg := NewMessage("8", "9", "10", "35", "FIX4.4", "A").
15 | SetHeader(NewComponent(NewKeyValue("49", NewString("test")), NewKeyValue("56", NewString("test")))).
16 | SetBody(NewKeyValue("100", NewString("test"))).
17 | SetTrailer(NewComponent())
18 |
19 | expected := []byte(`8=FIX4.49=3035=A49=test56=test100=test10=023`)
20 | b, err := conv.ConvertToBytes(msg)
21 | if err != nil {
22 | t.Fatalf("Error: %s", err)
23 | }
24 | if !bytes.Equal(expected, b) {
25 | t.Fatalf("Expected %v, got %v", expected, string(b))
26 | }
27 | }
28 |
29 | func TestMessageByteConverter_Concurrent(t *testing.T) {
30 | conv := NewMessageByteConverter(100)
31 | requests := make(chan int, 100)
32 | wg := sync.WaitGroup{}
33 | workers := 100
34 |
35 | for workerID := 0; workerID < workers; workerID++ {
36 | wg.Add(1)
37 | go func(workerID int) {
38 | defer wg.Done()
39 | workerKey := strconv.Itoa(workerID)
40 | for i := range requests {
41 | time.Sleep(time.Duration(rand.Int31n(10)) * time.Millisecond)
42 | key := strconv.Itoa(i)
43 | msg := NewMessage("8", "9", "10", "35", "FIX4.4", "A").
44 | SetHeader(NewComponent(NewKeyValue("worker", NewString(workerKey)), NewKeyValue("messageID", NewString(key)))).
45 | SetBody(NewKeyValue("100", NewString("test"))).
46 | SetTrailer(NewComponent())
47 | expected := []byte(`8=FIX4.49=3035=Aworker=` + workerKey + `messageID=` + key + `100=test10=023`)
48 | b, err := conv.ConvertToBytes(msg)
49 | if err != nil {
50 | t.Errorf("Error: %s", err)
51 | }
52 | // here we replace length and checksum to ignore them
53 | bb := bytes.Split(b, []byte{1})
54 | expectedb := bytes.Split(expected, []byte{1})
55 | bb[1] = expectedb[1]
56 | bb[len(bb)-2] = expectedb[len(expectedb)-2]
57 | b = bytes.Join(bb, []byte{1})
58 | if !bytes.Equal(expected, b) {
59 | t.Errorf("Expected %v,\n got %v", bb, expectedb)
60 | }
61 | }
62 | }(workerID)
63 | }
64 |
65 | for i := 0; i < workers*100; i++ {
66 | requests <- i
67 | }
68 | close(requests)
69 | wg.Wait()
70 | }
71 |
--------------------------------------------------------------------------------
/fix/encoding/unmarshaler.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "reflect"
7 |
8 | "github.com/b2broker/simplefix-go/fix"
9 | "github.com/b2broker/simplefix-go/session/messages"
10 | )
11 |
12 | type Validator interface {
13 | Do(msg messages.Builder) error
14 | }
15 |
16 | type DefaultUnmarshaller struct {
17 | Validator Validator
18 | Strict bool
19 | }
20 |
21 | func NewDefaultUnmarshaller(strict bool) *DefaultUnmarshaller {
22 | return &DefaultUnmarshaller{Strict: strict, Validator: DefaultValidator{}}
23 | }
24 |
25 | func (u DefaultUnmarshaller) Unmarshal(msg messages.Builder, d []byte) error {
26 | if err := validateRaw(msg, d, u.Strict); err != nil {
27 | return err
28 | }
29 |
30 | err := unmarshalItems(msg.Items(), d, u.Strict)
31 | if err != nil {
32 | return err
33 | }
34 |
35 | return u.Validator.Do(msg)
36 | }
37 |
38 | func Unmarshal(msg messages.Builder, d []byte) error {
39 | u := DefaultUnmarshaller{Strict: true, Validator: DefaultValidator{}}
40 |
41 | return u.Unmarshal(msg, d)
42 | }
43 |
44 | // unmarshalItems parses the FIX message data stored as a byte array
45 | // and writes it into the Items object.
46 | func unmarshalItems(msg fix.Items, data []byte, strict bool) error {
47 | s := newState(data, strict)
48 |
49 | for _, item := range msg {
50 | err := s.unmarshal(s.data, item)
51 | if err != nil {
52 | return fmt.Errorf("could not unmarshal items: %s", err)
53 | }
54 | }
55 |
56 | return nil
57 | }
58 |
59 | type state struct {
60 | data []byte
61 | strict bool
62 | }
63 |
64 | func newState(data []byte, strict bool) *state {
65 | return &state{data: data, strict: strict}
66 | }
67 |
68 | // scanKeyValue parses the message data related to key-value pairs
69 | // and writes it into KeyValue objects.
70 | func (s *state) scanKeyValue(data []byte, el *fix.KeyValue) error {
71 | q := bytes.Join([][]byte{[]byte(el.Key), {'='}}, nil)
72 | var keyIndex int
73 | if bytes.Equal(data[:len(q)], q) {
74 | keyIndex = 0
75 | } else {
76 | ks := bytes.Join([][]byte{fix.Delimiter, []byte(el.Key), {'='}}, nil)
77 | keyIndex = bytes.Index(data, ks)
78 | if keyIndex == -1 {
79 | return nil
80 | }
81 | keyIndex++ // An SOH character that is used to delimit key-value groups.
82 | }
83 |
84 | from := keyIndex + len(q)
85 |
86 | d := data[from:]
87 |
88 | end := bytes.Index(d, []byte{1})
89 | if end == -1 {
90 | end = len(d)
91 | }
92 | v := d[:end]
93 | err := el.FromBytes(v)
94 | if err != nil {
95 | return fmt.Errorf("could not unmarshal element %s into %s: %s", el.Key, string(v), err)
96 | }
97 |
98 | return nil
99 | }
100 |
101 | // splitGroup splits message parts which are recognized to be separate groups
102 | // to create individual group items. The function distinguishes repeated parts and detects
103 | // identical tags without repeating key-value groups.
104 | func splitGroup(line []byte, firstTag []byte) (array [][]byte) {
105 | ok := true
106 | var index int
107 | for ok {
108 | next := bytes.Index(line[1:], firstTag)
109 | if next == -1 {
110 | index = len(line)
111 | ok = false
112 | } else {
113 | index = next + 1
114 | }
115 | array = append(array, line[:index])
116 | line = line[next+1:]
117 | }
118 | return array
119 | }
120 |
121 | // unmarshal traverses through a fixItem and parses its byte data,
122 | // which is then assigned to the fixItem. A fixItem is a constructed FIX message (or its portion)
123 | // with assigned KeyValue, Component and Group items.
124 | func (s *state) unmarshal(data []byte, fixItem fix.Item) error {
125 | switch el := fixItem.(type) {
126 | case *fix.KeyValue:
127 | return s.scanKeyValue(data, el)
128 |
129 | case *fix.Group:
130 | noTag := el.NoTag()
131 |
132 | noKv := fix.NewKeyValue(noTag, &fix.Int{})
133 | err := s.unmarshal(data, noKv)
134 | if err != nil {
135 | return fmt.Errorf("could not unmarshal group: %s", err)
136 | }
137 |
138 | cnt := noKv.Value.Value().(int)
139 | startNoTag := bytes.Index(data, append([]byte(noKv.Key), '='))
140 | if startNoTag == -1 {
141 | return nil
142 | }
143 |
144 | startFirstFieldTag := bytes.Index(data[startNoTag:], fix.Delimiter)
145 | arrayString := data[startNoTag+startFirstFieldTag:]
146 | endFirstFieldTag := bytes.Index(arrayString, []byte{'='})
147 |
148 | firstTag := arrayString[:endFirstFieldTag+1]
149 | arrayItems := splitGroup(arrayString, firstTag)
150 |
151 | if len(arrayItems) == 0 {
152 | return fmt.Errorf("no elements found in the array")
153 | }
154 |
155 | if len(arrayItems) != cnt {
156 | return fmt.Errorf("wrong items count: %d != %d", cnt, len(arrayItems))
157 | }
158 |
159 | for i := 0; i < cnt; i++ {
160 | entry := el.AsTemplate()
161 |
162 | for _, item := range entry {
163 | err = s.unmarshal(arrayItems[i], item)
164 | if err != nil {
165 | return fmt.Errorf("could not unmarshal group item: %s", err)
166 | }
167 | }
168 | el.AddEntry(entry)
169 | }
170 |
171 | case *fix.Component:
172 | component := el.Items()
173 | for _, item := range component {
174 | err := s.unmarshal(data, item)
175 | if err != nil {
176 | return fmt.Errorf("could not unmarshal component: %s", err)
177 | }
178 | }
179 |
180 | default:
181 | return fmt.Errorf("unexpected FIX item type: %s %s", reflect.TypeOf(fixItem), fixItem)
182 | }
183 |
184 | return nil
185 | }
186 |
187 | func validateRaw(msg messages.Builder, d []byte, strict bool) error {
188 | bs := fix.NewKeyValue(msg.BeginStringTag(), fix.NewRaw(nil))
189 | bl := fix.NewKeyValue(msg.BodyLengthTag(), fix.NewRaw(nil))
190 | cs := fix.NewKeyValue(msg.CheckSumTag(), fix.NewRaw(nil))
191 |
192 | if err := unmarshalItems(fix.Items{bs, bl, cs}, d, strict); err != nil {
193 | return err
194 | }
195 |
196 | blVal := fix.NewInt(0)
197 | if err := blVal.FromBytes(bl.Load().ToBytes()); err != nil {
198 | return fmt.Errorf("invalid body length: %w", err)
199 | }
200 | if blVal.IsNull() {
201 | return fmt.Errorf("invalid body length: value is empty")
202 | }
203 | bodyLength := blVal.Value().(int)
204 |
205 | offset := len(bs.ToBytes()) + 1 // extra delimiter
206 | offset += len(bl.ToBytes()) + 1 // extra delimiter
207 | length := len(d) - offset
208 | length -= len(cs.ToBytes()) + 1 // extra delimiter
209 |
210 | if length != bodyLength {
211 | return fmt.Errorf("an invalid body length; specified: %d, required: %d",
212 | bodyLength,
213 | length,
214 | )
215 | }
216 |
217 | checkSum := fix.CalcCheckSumOptimized(d[:offset+length-1])
218 |
219 | if !bytes.Equal(cs.Load().ToBytes(), checkSum) {
220 | return fmt.Errorf(
221 | "an invalid checksum; specified: %s, required: %s",
222 | string(cs.Load().ToBytes()),
223 | string(checkSum),
224 | )
225 | }
226 |
227 | return nil
228 | }
229 |
--------------------------------------------------------------------------------
/fix/encoding/unmarshaller_test.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | "bytes"
5 | "github.com/b2broker/simplefix-go/fix"
6 | "testing"
7 | )
8 |
9 | const visibleDelimiter = "|"
10 |
11 | func TestUnmarshalItems(t *testing.T) {
12 | var exampleRawMas = []byte("8=FIX.4.49=6135=A49=Client56=Server34=152=20210706-19:06:12.838108=510=206")
13 |
14 | testData := []struct {
15 | raw []byte
16 | expect []byte
17 | items fix.Items
18 | }{
19 | {
20 | raw: exampleRawMas,
21 | items: fix.Items{
22 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
23 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
24 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
25 | &fix.KeyValue{Key: "49", Value: &fix.String{}},
26 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
27 | &fix.KeyValue{Key: "34", Value: &fix.String{}},
28 | &fix.KeyValue{Key: "52", Value: &fix.String{}},
29 | &fix.KeyValue{Key: "108", Value: &fix.String{}},
30 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
31 | },
32 | expect: exampleRawMas,
33 | },
34 | {
35 | raw: exampleRawMas,
36 | items: fix.Items{
37 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
38 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
39 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
40 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
41 | &fix.KeyValue{Key: "49", Value: &fix.String{}},
42 | &fix.KeyValue{Key: "34", Value: &fix.String{}},
43 | &fix.KeyValue{Key: "52", Value: &fix.String{}},
44 | &fix.KeyValue{Key: "100500", Value: &fix.String{}},
45 | &fix.KeyValue{Key: "108", Value: &fix.String{}},
46 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
47 | },
48 | expect: []byte("8=FIX.4.435=A9=6156=Server49=Client34=152=20210706-19:06:12.838108=510=206"),
49 | },
50 | {
51 | raw: exampleRawMas,
52 | items: fix.Items{
53 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
54 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
55 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
56 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
57 | &fix.KeyValue{Key: "49", Value: &fix.String{}},
58 | &fix.KeyValue{Key: "34", Value: &fix.String{}},
59 | &fix.KeyValue{Key: "108", Value: &fix.String{}},
60 | &fix.KeyValue{Key: "52", Value: &fix.String{}},
61 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
62 | },
63 | expect: []byte("8=FIX.4.49=6135=A56=Server49=Client34=1108=552=20210706-19:06:12.83810=206"),
64 | },
65 | {
66 | raw: []byte("8=FIX.4.49=6135=A56=Server161=4162=A163=1162=B163=2162=C162=DE163=310=206"),
67 | items: fix.Items{
68 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
69 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
70 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
71 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
72 | fix.NewGroup("161", &fix.KeyValue{Key: "162", Value: &fix.String{}}, &fix.KeyValue{Key: "163", Value: &fix.String{}}),
73 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
74 | },
75 | expect: []byte("8=FIX.4.49=6135=A56=Server161=4162=A163=1162=B163=2162=C162=DE163=310=206"),
76 | },
77 | {
78 | raw: []byte("8=FIX.4.49=6135=A56=Server161=4162=A163=1162=B163=2162=C162=DE163=310=206"),
79 | items: fix.Items{
80 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
81 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
82 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
83 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
84 | fix.NewGroup("161", fix.NewComponent(
85 | &fix.KeyValue{Key: "162", Value: &fix.String{}},
86 | &fix.KeyValue{Key: "163", Value: &fix.String{}},
87 | )),
88 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
89 | },
90 | expect: []byte("8=FIX.4.49=6135=A56=Server161=4162=A163=1162=B163=2162=C162=DE163=310=206"),
91 | },
92 | {
93 | raw: []byte("8=FIX.4.49=18335=i34=8649=XC22952=20220314-11:39:13.38556=Q048296=2302=62295=1299=0134=1.0135=1.0188=4588680190=4591680302=64295=1299=0134=100000135=100000188=1.39851190=1.3986510=085"),
94 | items: fix.Items{
95 | &fix.KeyValue{Key: "8", Value: &fix.String{}},
96 | &fix.KeyValue{Key: "9", Value: &fix.String{}},
97 | &fix.KeyValue{Key: "35", Value: &fix.String{}},
98 | &fix.KeyValue{Key: "34", Value: &fix.String{}},
99 | &fix.KeyValue{Key: "49", Value: &fix.String{}},
100 | &fix.KeyValue{Key: "52", Value: &fix.String{}},
101 | &fix.KeyValue{Key: "56", Value: &fix.String{}},
102 | fix.NewGroup("296", fix.NewComponent(
103 | &fix.KeyValue{Key: "302", Value: &fix.String{}},
104 | fix.NewComponent(
105 | &fix.KeyValue{Key: "295", Value: &fix.String{}},
106 | &fix.KeyValue{Key: "299", Value: &fix.String{}},
107 | &fix.KeyValue{Key: "134", Value: &fix.Float{}},
108 | &fix.KeyValue{Key: "135", Value: &fix.Float{}},
109 | &fix.KeyValue{Key: "188", Value: &fix.Float{}},
110 | &fix.KeyValue{Key: "190", Value: &fix.Float{}},
111 | ),
112 | )),
113 | &fix.KeyValue{Key: "10", Value: &fix.String{}},
114 | },
115 | expect: []byte("8=FIX.4.49=18335=i34=8649=XC22952=20220314-11:39:13.38556=Q048296=2302=62295=1299=0134=1.0135=1.0188=4588680190=4591680302=64295=1299=0134=100000135=100000188=1.39851190=1.3986510=085"),
116 | },
117 | }
118 |
119 | for i, item := range testData {
120 | err := unmarshalItems(item.items, item.raw, true)
121 | if err != nil {
122 | t.Fatal(err)
123 | }
124 |
125 | res := item.items.ToBytes()
126 | if !bytes.Equal(item.expect, res) {
127 | t.Logf("%d. expect %s (%d)", i, showDelimiter(item.expect), len(item.expect))
128 | t.Logf("%d. result %s (%d)", i, showDelimiter(res), len(res))
129 | t.Fatal("the result is not equal to expected message")
130 | }
131 | }
132 |
133 | }
134 |
135 | func showDelimiter(in []byte) []byte {
136 | return bytes.ReplaceAll(in, fix.Delimiter, []byte(visibleDelimiter))
137 | }
138 |
--------------------------------------------------------------------------------
/fix/encoding/validator.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | "fmt"
5 | "github.com/b2broker/simplefix-go/session/messages"
6 | )
7 |
8 | type DefaultValidator struct{}
9 |
10 | func (v DefaultValidator) Do(msg messages.Builder) error {
11 | return v.checkRequiredFields(msg)
12 | }
13 |
14 | func (DefaultValidator) checkRequiredFields(msg messages.Builder) error {
15 | if msg.BeginString().Value.IsNull() {
16 | return fmt.Errorf("the required field value is empty: BeginString")
17 | }
18 | if msg.BodyLength() == 0 {
19 | return fmt.Errorf("the required field value is empty: BodyLength")
20 | }
21 | if msg.MsgType() == "" {
22 | return fmt.Errorf("the required field value is empty: MsgType")
23 | }
24 | if msg.CheckSum() == "" {
25 | return fmt.Errorf("the required field value is empty: CheckSum")
26 | }
27 |
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/fix/fix_item.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | // Item is an interface providing a method required to implement basic FIX item functionality.
10 | type Item interface {
11 | ToBytes() []byte
12 | WriteBytes(writer *bytes.Buffer) bool
13 | String() string
14 | IsEmpty() bool
15 | }
16 |
17 | // Items is an array of Item elements.
18 | type Items []Item
19 |
20 | // ToBytes returns a byte representation of an Items array.
21 | func (v Items) ToBytes() []byte {
22 | var msg [][]byte
23 | for _, item := range v {
24 | itemB := item.ToBytes()
25 | if itemB != nil {
26 | msg = append(msg, itemB)
27 | }
28 | }
29 | return joinBody(msg...)
30 | }
31 |
32 | func (v Items) IsEmpty() bool {
33 | for _, item := range v {
34 | if !item.IsEmpty() {
35 | return false
36 | }
37 | }
38 | return true
39 | }
40 |
41 | func (v Items) WriteBytes(writer *bytes.Buffer) bool {
42 | addDelimeter := false
43 | written := false
44 | for i, item := range v {
45 | if !item.IsEmpty() && addDelimeter {
46 | _ = writer.WriteByte(DelimiterChar)
47 | addDelimeter = false
48 | }
49 | if item.WriteBytes(writer) {
50 | written = true
51 | if i <= len(v)-1 {
52 | addDelimeter = true
53 | }
54 | }
55 | }
56 |
57 | return written
58 | }
59 |
60 | // String returns a string representation of an Items array.
61 | func (v Items) String() string {
62 | var items []string
63 | for _, item := range v {
64 | itemStr := item.String()
65 | if itemStr != "" {
66 | items = append(items, itemStr)
67 | }
68 | }
69 | return fmt.Sprintf("{%s}", strings.Join(items, ", "))
70 | }
71 |
--------------------------------------------------------------------------------
/fix/generator.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strconv"
7 | )
8 |
9 | const TimeLayout = "20060102-15:04:05.000"
10 |
11 | const (
12 | // CountOfSOHSymbols
13 | // Deprecated: should not be used, count SOH symbols by yourself
14 | CountOfSOHSymbols = 3
15 | // CountOfSOHSymbolsWithoutBody
16 | // Deprecated: should not be used, count SOH symbols by yourself
17 | CountOfSOHSymbolsWithoutBody = 2
18 | )
19 |
20 | var Delimiter = []byte{1}
21 |
22 | const DelimiterChar = 1
23 |
24 | func joinBody(values ...[]byte) []byte {
25 | return bytes.Join(values, Delimiter)
26 | }
27 |
28 | // nolint
29 | func makeGroup(entries []map[string][]byte, tags []string) []byte {
30 | var groupItems [][]byte
31 | for _, entry := range entries {
32 | for _, tag := range tags {
33 | groupItems = append(groupItems, bytes.Join([][]byte{[]byte(tag), entry[tag]}, []byte{61}))
34 | }
35 | }
36 |
37 | return bytes.Join(groupItems, Delimiter)
38 | }
39 |
40 | func CalcCheckSum(body []byte) []byte {
41 | var sum int
42 | for _, b := range body {
43 | sum += int(b)
44 | }
45 | sum += int(byte(1))
46 |
47 | return []byte(fmt.Sprintf("%03s", strconv.Itoa(sum%256)))
48 | }
49 |
50 | func CalcCheckSumOptimized(bytes []byte) []byte {
51 | var sum int
52 | for _, b := range bytes {
53 | sum += int(b)
54 | }
55 | sum += int(byte(1))
56 | n := sum % 256
57 | return []byte{byte('0' + n/100), byte('0' + (n/10)%10), byte('0' + n%10)}
58 | }
59 |
60 | func CalcCheckSumOptimizedFromBuffer(buffer *bytes.Buffer) []byte {
61 | var sum int
62 | for _, b := range buffer.Bytes() {
63 | sum += int(b)
64 | }
65 | n := (sum + int(byte(1))) % 256
66 | return []byte{byte('0' + n/100), byte('0' + (n/10)%10), byte('0' + n%10)}
67 | }
68 |
--------------------------------------------------------------------------------
/fix/generator_test.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestCalcCheckSum(t *testing.T) {
9 | cases := [][]byte{
10 | []byte("8=FIX.4.2|9="),
11 | []byte("8=FIX.4.2|9=0"),
12 | []byte("8=FIX.4.2|9=0|"),
13 | []byte(""),
14 | nil,
15 | }
16 | for _, c := range cases {
17 | t.Run(string(c), func(t *testing.T) {
18 | if !bytes.Equal(CalcCheckSumOptimized(c), CalcCheckSum(c)) {
19 | t.Fatalf("CalcCheckSumOptimized %s != CalcCheckSum %s", CalcCheckSumOptimized(c), CalcCheckSum(c))
20 | }
21 | })
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/fix/group.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | // Group is a structure used to implement FIX group types.
10 | type Group struct {
11 | noTag string
12 | template Items
13 |
14 | items []Items
15 | }
16 |
17 | // NoTag returns a tag value indicating the number of elements in a group.
18 | func (g *Group) NoTag() string {
19 | return g.noTag
20 | }
21 |
22 | // String returns a string representation of a Group.
23 | func (g *Group) String() string {
24 | var items []string
25 | for _, item := range g.items {
26 | itemStr := item.String()
27 | if itemStr != "" {
28 | items = append(items, itemStr)
29 | }
30 | }
31 | return fmt.Sprintf("[%s]", strings.Join(items, ", "))
32 | }
33 |
34 | // NewGroup is used to create a new group based on:
35 | // - the tag value specifying the number of elements, and
36 | // - the list of tags
37 | func NewGroup(noTags string, tags ...Item) *Group {
38 | return &Group{
39 | noTag: noTags,
40 | template: tags,
41 | }
42 | }
43 |
44 | // ToBytes returns a byte representation of a Group.
45 | func (g *Group) ToBytes() []byte {
46 | var msg [][]byte
47 |
48 | if len(g.items) == 0 {
49 | return nil
50 | }
51 |
52 | msg = append(msg, NewKeyValue(
53 | g.noTag,
54 | NewInt(len(g.items)),
55 | ).ToBytes())
56 |
57 | for _, item := range g.items {
58 | itemB := item.ToBytes()
59 | if itemB != nil {
60 | msg = append(msg, item.ToBytes())
61 | }
62 | }
63 | return joinBody(msg...)
64 | }
65 |
66 | // always have size tag
67 | func (g *Group) IsEmpty() bool {
68 | return false
69 | }
70 |
71 | func (g *Group) WriteBytes(writer *bytes.Buffer) bool {
72 |
73 | if len(g.items) == 0 {
74 | return false
75 | }
76 | intVal := NewInt(len(g.items))
77 | NewKeyValue(g.noTag, intVal).WriteBytes(writer)
78 |
79 | _ = writer.WriteByte(DelimiterChar)
80 |
81 | addDelimeter := false
82 | for i, item := range g.items {
83 | if !item.IsEmpty() && addDelimeter {
84 | _ = writer.WriteByte(DelimiterChar)
85 | addDelimeter = false
86 | }
87 | if item.WriteBytes(writer) {
88 | if i <= len(g.items)-1 {
89 | addDelimeter = true
90 | }
91 | }
92 | }
93 | return true
94 | }
95 |
96 | // AddEntry adds a new entry with the same list of tags as in a specified group.
97 | // To generate all tags for a newly created entry, use the AsTemplate method.
98 | func (g *Group) AddEntry(v Items) *Group {
99 | g.items = append(g.items, v)
100 |
101 | return g
102 | }
103 |
104 | // Entry returns a group entry by its sequence number.
105 | func (g *Group) Entry(id int) Item {
106 | return g.items[id]
107 | }
108 |
109 | // AsTemplate returns a list of group tags as an Items object.
110 | func (g *Group) AsTemplate() Items {
111 | tmp := make([]Item, len(g.template))
112 |
113 | for i, item := range g.template {
114 | switch value := item.(type) {
115 | case *KeyValue:
116 | tmp[i] = value.AsTemplate()
117 |
118 | case *Group:
119 | tmp[i] = NewGroup(value.NoTag(), value.AsTemplate()...)
120 |
121 | case *Component:
122 | tmp[i] = NewComponent(value.AsTemplate()...)
123 | }
124 | }
125 |
126 | return tmp
127 | }
128 |
129 | // Entries returns all entries belonging to a group as an array of Items objects.
130 | func (g *Group) Entries() []Items {
131 | return g.items
132 | }
133 |
--------------------------------------------------------------------------------
/fix/group_test.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | "time"
7 | )
8 |
9 | const (
10 | beginString = "8"
11 | bodyLength = "9"
12 | checksum = "10"
13 | msgType = "35"
14 | msgSeqNum = "34"
15 | senderCompID = "49"
16 | targetCompID = "56"
17 | sendingTime = "52"
18 |
19 | mdReqID = "262"
20 | subscriptionRequestType = "263"
21 | marketDepth = "264"
22 |
23 | noMDEntryTypes = "267"
24 | mdEntryType = "269"
25 |
26 | noRelatedSym = "146"
27 | symbol = "55"
28 |
29 | noEvents = "864"
30 | eventType = "865"
31 | eventText = "868"
32 | )
33 |
34 | func newHeader(msgSeqNumV int, senderCompIDV, targetCompIDV string, sendingTimeV time.Time) *Component {
35 | return NewComponent(
36 | NewKeyValue(msgSeqNum, NewInt(msgSeqNumV)),
37 | NewKeyValue(senderCompID, NewString(senderCompIDV)),
38 | NewKeyValue(targetCompID, NewString(targetCompIDV)),
39 | NewKeyValue(sendingTime, NewTime(sendingTimeV)),
40 | )
41 | }
42 |
43 | func TestGroup_AddItem(t *testing.T) {
44 | var testMsg = []byte("8=FIX.4.49=23635=A34=149=sender56=target52=20210208-12:51:43.000262=1263=1264=20267=2269=0269=1146=355=BTC/USD864=2865=1868=put865=2868=call55=ETH/USD864=2865=1868=put865=2868=call55=KGB/FBI864=2865=1868=put865=2868=call10=048")
45 |
46 | var (
47 | beginStringValue = "FIX.4.4"
48 | msgLogon = "A"
49 |
50 | sender = "sender"
51 | target = "target"
52 | )
53 |
54 | entryTypes := NewGroup(noMDEntryTypes,
55 | NewKeyValue(mdEntryType, &String{}),
56 | )
57 |
58 | relatedSym := NewGroup(noRelatedSym,
59 | NewKeyValue(symbol, &String{}),
60 | )
61 |
62 | msg := NewMessage(beginString, bodyLength, checksum, msgType, beginStringValue, msgLogon).
63 | SetBody(
64 | NewKeyValue(mdReqID, NewString("1")),
65 | NewKeyValue(subscriptionRequestType, NewString("1")),
66 | NewKeyValue(marketDepth, NewString("20")),
67 | entryTypes,
68 | relatedSym,
69 | ).
70 | SetHeader(newHeader(1, sender, target, time.Unix(1612788703, 0).UTC()))
71 |
72 | entryTypes.AddEntry(Items{
73 | NewKeyValue(mdEntryType, NewString("0")),
74 | })
75 | entryTypes.AddEntry(Items{
76 | NewKeyValue(mdEntryType, NewString("1")),
77 | })
78 |
79 | makeInstrumentComponent := func(sym string) *Component {
80 | events := NewGroup(noEvents,
81 | NewKeyValue(eventType, &String{}),
82 | NewKeyValue(eventText, &String{}),
83 | )
84 | events.AddEntry(Items{
85 | NewKeyValue(eventType, NewString("1")),
86 | NewKeyValue(eventText, NewString("put")),
87 | })
88 |
89 | events.AddEntry(Items{
90 | NewKeyValue(eventType, NewString("2")),
91 | NewKeyValue(eventText, NewString("call")),
92 | })
93 |
94 | instrument := NewComponent(
95 | NewKeyValue(symbol, NewString(sym)),
96 | events,
97 | )
98 |
99 | return instrument
100 | }
101 |
102 | relatedSym.AddEntry(Items{
103 | makeInstrumentComponent("BTC/USD"),
104 | })
105 | relatedSym.AddEntry(Items{
106 | makeInstrumentComponent("ETH/USD"),
107 | })
108 | relatedSym.AddEntry(Items{
109 | makeInstrumentComponent("KGB/FBI"),
110 | })
111 |
112 | res, err := msg.ToBytes()
113 | if err != nil {
114 | t.Fatalf("could not marshal message: %s", err)
115 | }
116 | if !bytes.Equal(testMsg, res) {
117 | t.Log(len(testMsg), string(testMsg))
118 | t.Log(len(res), string(res))
119 | t.Fatalf("message length is not equal")
120 | }
121 |
122 | converter := NewMessageByteConverter(200)
123 | res, err = converter.ConvertToBytes(msg)
124 | if err != nil {
125 | t.Fatalf("could not marshal message: %s", err)
126 | }
127 | if !bytes.Equal(testMsg, res) {
128 | t.Log(len(testMsg), string(testMsg))
129 | t.Log(len(res), string(res))
130 | t.Fatalf("message length is not equal")
131 | }
132 | }
133 |
134 | func TestGroup_Parse(t *testing.T) {
135 | var testMsg = []byte("8=FIX.4.49=23635=A34=149=sender56=target52=20210208-15:51:43.000262=1263=1264=20267=2269=0269=1146=355=BTC/USD864=2865=1868=put865=2868=call55=ETH/USD864=2865=1868=put865=2868=call55=KGB/FBI864=2865=1868=put865=2868=call10=051")
136 |
137 | msg := Items{
138 | NewKeyValue(beginString, NewString("FIX.4.4")),
139 | NewKeyValue(bodyLength, NewInt(236)),
140 | NewKeyValue(msgType, NewString("A")),
141 | NewKeyValue(msgSeqNum, NewInt(1)),
142 | NewKeyValue(senderCompID, NewString("sender")),
143 | NewKeyValue(targetCompID, NewString("target")),
144 | NewKeyValue(sendingTime, NewTime(time.Date(2021, 2, 8, 15, 51, 43, 0, time.UTC))),
145 | NewKeyValue(mdReqID, NewInt(1)),
146 | NewKeyValue(subscriptionRequestType, NewString("1")),
147 | NewKeyValue(marketDepth, NewInt(20)),
148 | NewGroup(noMDEntryTypes, NewComponent(
149 | &KeyValue{Key: mdEntryType},
150 | )).
151 | AddEntry(NewComponent(
152 | NewKeyValue(mdEntryType, NewString("0")),
153 | ).Items()).
154 | AddEntry(NewComponent(
155 | NewKeyValue(mdEntryType, NewString("1")),
156 | ).Items()),
157 | NewGroup(noRelatedSym, NewComponent(&KeyValue{Key: symbol},
158 | NewGroup(noEvents, NewComponent(&KeyValue{Key: eventType}, &KeyValue{Key: eventText}))),
159 | ).
160 | AddEntry(NewComponent(
161 | NewKeyValue(symbol, NewString("BTC/USD")),
162 | NewGroup(noEvents, NewComponent(&KeyValue{Key: eventType}, &KeyValue{Key: eventText})).
163 | AddEntry(NewComponent(
164 | NewKeyValue(eventType, NewString("1")),
165 | NewKeyValue(eventText, NewString("put")),
166 | ).Items()).
167 | AddEntry(NewComponent(
168 | NewKeyValue(eventType, NewString("2")),
169 | NewKeyValue(eventText, NewString("call")),
170 | ).Items()),
171 | ).Items()).
172 | AddEntry(NewComponent(
173 | NewKeyValue(symbol, NewString("ETH/USD")),
174 | NewGroup(noEvents, NewComponent(&KeyValue{Key: eventType}, &KeyValue{Key: eventText})).
175 | AddEntry(NewComponent(
176 | NewKeyValue(eventType, NewString("1")),
177 | NewKeyValue(eventText, NewString("put")),
178 | ).Items()).
179 | AddEntry(NewComponent(
180 | NewKeyValue(eventType, NewString("2")),
181 | NewKeyValue(eventText, NewString("call")),
182 | ).Items()),
183 | ).Items()).
184 | AddEntry(NewComponent(
185 | NewKeyValue(symbol, NewString("KGB/FBI")),
186 | NewGroup(noEvents, NewComponent(&KeyValue{Key: eventType}, &KeyValue{Key: eventText})).
187 | AddEntry(NewComponent(
188 | NewKeyValue(eventType, NewString("1")),
189 | NewKeyValue(eventText, NewString("put")),
190 | ).Items()).
191 | AddEntry(NewComponent(
192 | NewKeyValue(eventType, NewString("2")),
193 | NewKeyValue(eventText, NewString("call")),
194 | ).Items()),
195 | ).Items()),
196 | NewKeyValue(checksum, NewString("051")),
197 | }
198 |
199 | res := msg.ToBytes()
200 | if !bytes.Equal(testMsg, res) {
201 | if !bytes.Equal(testMsg, res) {
202 | t.Log(len(testMsg), string(testMsg))
203 | t.Log(len(res), string(res))
204 | t.Fatalf("message length is not equal")
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/fix/key_value.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | )
7 |
8 | // KeyValue is a basic structure used for FIX message implementation.
9 | // It is used to specify the tag and value for each field.
10 | type KeyValue struct {
11 | Key string
12 | Value Value
13 | }
14 |
15 | // NewKeyValue returns a new KeyValue object.
16 | func NewKeyValue(key string, value Value) *KeyValue {
17 | return &KeyValue{Key: key, Value: value}
18 | }
19 |
20 | // AsTemplate returns a copy of a KeyValue object with an empty value assigned to it.
21 | func (kv *KeyValue) AsTemplate() *KeyValue {
22 | switch kv.Value.(type) {
23 | case *String:
24 | return NewKeyValue(kv.Key, &String{})
25 | case *Int:
26 | return NewKeyValue(kv.Key, &Int{})
27 | case *Uint:
28 | return NewKeyValue(kv.Key, &Uint{})
29 | case *Time:
30 | return NewKeyValue(kv.Key, &Time{})
31 | case *Float:
32 | return NewKeyValue(kv.Key, &Float{})
33 | default:
34 | return NewKeyValue(kv.Key, &Raw{})
35 | }
36 | }
37 |
38 | // ToBytes returns a byte representation of a KeyValue.
39 | func (kv *KeyValue) ToBytes() []byte {
40 | if kv == nil || kv.Value == nil || kv.Value.IsNull() {
41 | return nil
42 | }
43 |
44 | v := kv.Value.ToBytes()
45 | if v == nil {
46 | return nil
47 | }
48 |
49 | return bytes.Join([][]byte{
50 | []byte(kv.Key), v,
51 | }, []byte{61})
52 | }
53 | func (kv *KeyValue) IsNull() bool {
54 | if kv == nil || kv.Value == nil || kv.Value.IsNull() {
55 | return false
56 | }
57 | return kv.Value.IsNull()
58 | }
59 | func (kv *KeyValue) IsEmpty() bool {
60 | if kv.IsNull() {
61 | return true
62 | }
63 | return kv.Value.IsEmpty()
64 | }
65 | func (kv *KeyValue) WriteBytes(writer *bytes.Buffer) bool {
66 | if kv == nil || kv.Value == nil || kv.Value.IsNull() {
67 | return false
68 | }
69 | if kv.Value.IsEmpty() {
70 | return false
71 | }
72 | _, _ = writer.WriteString(kv.Key)
73 | _ = writer.WriteByte('=')
74 | kv.Value.WriteBytes(writer)
75 |
76 | return true
77 | }
78 |
79 | // Set replaces a specified value.
80 | func (kv *KeyValue) Set(value Value) {
81 | kv.Value = value
82 | }
83 |
84 | // Load returns a specified value.
85 | func (kv *KeyValue) Load() Value {
86 | return kv.Value
87 | }
88 |
89 | // FromBytes replaces a KeyValue object specified in the form of a byte array.
90 | func (kv *KeyValue) FromBytes(d []byte) error {
91 | return kv.Value.FromBytes(d)
92 | }
93 |
94 | // String returns a string representation of a KeyValue object.
95 | func (kv *KeyValue) String() string {
96 | if kv.Value.IsNull() {
97 | return ""
98 | }
99 | return fmt.Sprintf("%s: %s", kv.Key, kv.Value)
100 | }
101 |
102 | // KeyValues is an array of KeyValue objects.
103 | type KeyValues []*KeyValue
104 |
105 | // ToBytes returns a byte representation of a KeyValues array.
106 | func (kvs KeyValues) ToBytes() []byte {
107 | buffer := bytes.NewBuffer([]byte{})
108 | for i, kv := range kvs {
109 | if kv.WriteBytes(buffer) && i < len(kvs)-1 {
110 | buffer.WriteByte(DelimiterChar)
111 | }
112 | }
113 | return buffer.Bytes()
114 | }
115 |
--------------------------------------------------------------------------------
/fix/key_value_test.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestKeyValues_ToBytes(t *testing.T) {
9 | keyValues := KeyValues{
10 | {"8", NewString("FIX.4.4")},
11 | {"9", NewString("408")},
12 | {"35", NewString("W")},
13 | {"49", NewString("LMAXD-MD")},
14 | {"56", NewString("b2brokerdigmdUATMTF")},
15 | {"34", NewString("111")},
16 | {"52", NewString("20190213-17:41:10.200")},
17 | {"22", NewString("8")},
18 | {"48", NewString("5005")},
19 | {"10", NewString("012")},
20 | }.ToBytes()
21 |
22 | msg := "8=FIX.4.49=40835=W49=LMAXD-MD56=b2brokerdigmdUATMTF34=11152=20190213-17:41:10.20022=848=500510=012"
23 |
24 | if !bytes.Equal(keyValues, []byte(msg)) {
25 | t.Log(string(keyValues))
26 | t.Log(msg)
27 | t.Fatalf("not equal")
28 | }
29 | }
30 |
31 | func TestKeyValue_ToBytes(t *testing.T) {
32 | keyValue := NewKeyValue("8", NewString("FIX.4.4")).ToBytes()
33 |
34 | msg := "8=FIX.4.4"
35 |
36 | if !bytes.Equal(keyValue, []byte(msg)) {
37 | t.Log(string(keyValue))
38 | t.Log(msg)
39 | t.Fatalf("not equal")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/fix/storage.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | type StorageSide string
4 |
5 | const (
6 | Incoming StorageSide = "incoming"
7 | Outgoing StorageSide = "outgoing"
8 | )
9 |
10 | type StorageID struct {
11 | Sender string
12 | Target string
13 | Side StorageSide
14 | }
15 |
--------------------------------------------------------------------------------
/fix/utils.go:
--------------------------------------------------------------------------------
1 | package fix
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | )
7 |
8 | // ValueByTag locates a value by its tag in a FIX message stored as a byte array.
9 | func ValueByTag(msg []byte, tag string) ([]byte, error) {
10 | start := bytes.Index(msg, bytes.Join([][]byte{{1}, []byte(tag), {61}}, nil))
11 | if len(msg) <= len(tag) {
12 | return nil, fmt.Errorf("could not find the tag: %s, the message is too short: %s", tag, msg)
13 | }
14 | if start == -1 && !bytes.Equal(bytes.Join([][]byte{[]byte(tag)}, nil), msg[:len(tag)]) {
15 | return nil, fmt.Errorf("the tag is not found: %s", tag)
16 | }
17 | start += len(tag) + 2
18 | end := bytes.Index(msg[start:], []byte{1})
19 | if end == -1 {
20 | end = len(msg)
21 | } else {
22 | end += start
23 | }
24 | return msg[start:end], nil
25 | }
26 |
--------------------------------------------------------------------------------
/generator/generator_test.go:
--------------------------------------------------------------------------------
1 | package generator
2 |
3 | import (
4 | "fmt"
5 | "github.com/b2broker/simplefix-go/utils"
6 | "os"
7 | "testing"
8 | )
9 |
10 | var generator *Generator
11 |
12 | func TestMain(m *testing.M) {
13 | var err error
14 | doc := &Doc{}
15 | if err = utils.ParseXML("./testdata/fix.4.4.xml", doc); err != nil {
16 | panic(fmt.Errorf("could not make XML document: %s", err))
17 | }
18 |
19 | config := &Config{}
20 | if err = utils.ParseXML("./testdata/types.xml", config); err != nil {
21 | panic(fmt.Errorf("could not make XML document: %s", err))
22 | }
23 |
24 | generator = NewGenerator(doc, config, "fix")
25 |
26 | m.Run()
27 | os.Exit(0)
28 | }
29 |
--------------------------------------------------------------------------------
/generator/required.go:
--------------------------------------------------------------------------------
1 | package generator
2 |
3 | // ExcludedFields specifies the tags that will be omitted in generated messages
4 | // because they are already included into the base message structure.
5 | var ExcludedFields = map[string]bool{
6 | "BeginString": true,
7 | "BodyLength": true,
8 | "MsgType": true,
9 | "CheckSum": true,
10 | }
11 |
12 | // RequiredHeaderFields indicates the required fields that must be contained in the header.
13 | // A FIX message is not considered properly structured unless it contains these fields in its header.
14 | var RequiredHeaderFields = map[string]bool{
15 | // Always unencrypted, must be the first field in a message.
16 | "BeginString": true,
17 |
18 | // Always unencrypted, must be the second field in a message.
19 | "BodyLength": true,
20 |
21 | // Always unencrypted, must be the third field in a message.
22 | "MsgType": true,
23 |
24 | // The assigned value is used to identify the party sending a message.
25 | "SenderCompID": true,
26 |
27 | // The assigned value is used to identify the party receiving a message.
28 | "TargetCompID": true,
29 |
30 | // An integer value, indicating the message sequence number.
31 | "MsgSeqNum": true,
32 |
33 | // The date and time of message transmission, in UTC time.
34 | "SendingTime": true,
35 | }
36 |
37 | // RequiredTrailerFields indicates the required field(s) that must be contained in the trailer.
38 | // A FIX message is not considered properly structured unless it contains these fields in its trailer.
39 | var RequiredTrailerFields = map[string]bool{
40 | // Always unencrypted, must be the last field in a message.
41 | "CheckSum": true,
42 | }
43 |
44 | // DefaultFlowFields indicates the required tags for each message type
45 | // that must be contained in the trailer.
46 | // A FIX session pipeline will not operate properly if any of these tags are missing for the specified messages.
47 | var DefaultFlowFields = map[string][]string{
48 | "Logon": {"HeartBtInt", "EncryptMethod", "Password", "Username", "ResetSeqNumFlag"},
49 | "Logout": nil,
50 | "Heartbeat": {"TestReqID"},
51 | "TestRequest": {"TestReqID"},
52 | "ResendRequest": {"BeginSeqNo", "EndSeqNo"},
53 | "SequenceReset": {"NewSeqNo", "GapFillFlag"},
54 | "Reject": {"SessionRejectReason", "RefSeqNum", "RefTagID"},
55 | "ExecutionReport": nil,
56 | "NewOrderSingle": nil,
57 | "MarketDataRequest": nil,
58 | "OrderCancelRequest": nil,
59 | }
60 |
--------------------------------------------------------------------------------
/generator/testdata/types.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/generator/type_caster.go:
--------------------------------------------------------------------------------
1 | package generator
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | const (
9 | fixFloat = "Float"
10 | fixInt = "Int"
11 | fixRaw = "Raw"
12 | fixBool = "Bool"
13 | fixString = "String"
14 | fixTime = "Time"
15 | )
16 |
17 | var allowedTypes = map[string]string{
18 | fixFloat: "float64",
19 | fixInt: "int",
20 | fixRaw: "[]byte",
21 | fixBool: "bool",
22 | fixString: "string",
23 | fixTime: "time.Time",
24 | }
25 |
26 | func (g *Generator) initTypes() {
27 | g.typeCast = make(map[string]string, len(g.config.Types))
28 |
29 | for _, tp := range g.config.Types {
30 | if tp.CastType == "" {
31 | panic(fmt.Errorf("empty type attribute for type %s", tp))
32 | }
33 |
34 | if _, ok := allowedTypes[tp.CastType]; !ok {
35 | var types []string
36 | for tp := range allowedTypes {
37 | types = append(types, tp)
38 | }
39 |
40 | panic(fmt.Errorf(
41 | "unexpected type attribute %s, should be of the [%s] type",
42 | tp, strings.Join(types, ", "),
43 | ))
44 | }
45 |
46 | g.typeCast[tp.Name] = tp.CastType
47 | }
48 | }
49 |
50 | func (g *Generator) fixTypeToGo(t string) string {
51 | if tp, ok := allowedTypes[t]; ok {
52 | return tp
53 | }
54 |
55 | return "string"
56 | }
57 |
58 | func (g *Generator) typeToFix(t string) string {
59 | if tp, ok := g.typeCast[t]; ok {
60 | return tp
61 | }
62 |
63 | if _, ok := g.enums[t]; !ok {
64 | panic(fmt.Errorf("could not find type %s at map", t))
65 | }
66 |
67 | return fixRaw
68 | }
69 |
--------------------------------------------------------------------------------
/generator/xml.go:
--------------------------------------------------------------------------------
1 | package generator
2 |
3 | import "encoding/xml"
4 |
5 | // Doc is a structure identifying the components and fields required for a custom FIX protocol implementation.
6 | type Doc struct {
7 | Type string `xml:"type,attr"`
8 | Major string `xml:"major,attr"`
9 | Minor string `xml:"minor,attr"`
10 | ServicePack int `xml:"servicepack,attr"`
11 |
12 | Header *Component `xml:"header"`
13 | Trailer *Component `xml:"trailer"`
14 | Messages []*Component `xml:"messages>message"`
15 | Components []*Component `xml:"components>component"`
16 | Fields []*Field `xml:"fields>field"`
17 | }
18 |
19 | // Component is a structure identifying the set of basic elements required for FIX messages,
20 | // such as key-value groups or basic components.
21 | type Component struct {
22 | Name string `xml:"name,attr"`
23 | MsgCat string `xml:"msgcat,attr"`
24 | MsgType string `xml:"msgtype,attr"`
25 |
26 | Members []*ComponentMember `xml:",any"`
27 | }
28 |
29 | // Field is a structure used to implement key-value groups.
30 | type Field struct {
31 | Number string `xml:"number,attr"`
32 | Name string `xml:"name,attr"`
33 | Type string `xml:"type,attr"`
34 | Values []*Value `xml:"value"`
35 | }
36 |
37 | // Value is a structure used to support enumeration-like FIX values.
38 | type Value struct {
39 | Enum string `xml:"enum,attr"`
40 | Description string `xml:"description,attr"`
41 | }
42 |
43 | type ComponentMember struct {
44 | XMLName xml.Name
45 | Name string `xml:"name,attr"`
46 | Required string `xml:"required,attr"`
47 |
48 | Members []*ComponentMember `xml:",any"`
49 | }
50 |
51 | type Config struct {
52 | Types []*Type `xml:"types>type"`
53 | }
54 |
55 | type Type struct {
56 | XMLName xml.Name
57 |
58 | Name string `xml:"name,attr"`
59 | CastType string `xml:"cast,attr"`
60 | }
61 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/b2broker/simplefix-go
2 |
3 | go 1.22
4 |
5 | require golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
6 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
2 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3 |
--------------------------------------------------------------------------------
/handler_factory.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import (
4 | "context"
5 | )
6 |
7 | // AcceptorHandlerFactory is a handler factory for an Acceptor object.
8 | type AcceptorHandlerFactory struct {
9 | bufferSize int
10 | msgTypeTag string
11 | }
12 |
13 | // NewAcceptorHandlerFactory returns a new AcceptorHandlerFactory instance.
14 | func NewAcceptorHandlerFactory(msgTypeTag string, bufferSize int) *AcceptorHandlerFactory {
15 | return &AcceptorHandlerFactory{bufferSize: bufferSize, msgTypeTag: msgTypeTag}
16 | }
17 |
18 | // MakeHandler creates a new AcceptorHandler instance.
19 | func (h *AcceptorHandlerFactory) MakeHandler(ctx context.Context) AcceptorHandler {
20 | return NewAcceptorHandler(ctx, h.msgTypeTag, h.bufferSize)
21 | }
22 |
--------------------------------------------------------------------------------
/handler_func_pool.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import (
4 | "errors"
5 | "sync"
6 | )
7 |
8 | // ErrHandleNotFound is returned when a required handler is not found.
9 | var ErrHandleNotFound = errors.New("handler not found")
10 |
11 | // HandlerPool is used for managing the pool of message handlers.
12 | type HandlerPool struct {
13 | mu sync.RWMutex
14 | handlers map[string][]interface{}
15 | counter *int64
16 | }
17 |
18 | // NewHandlerPool creates a new HandlerPool instance.
19 | func NewHandlerPool() *HandlerPool {
20 | return &HandlerPool{
21 | handlers: make(map[string][]interface{}),
22 | counter: new(int64),
23 | }
24 | }
25 |
26 | func (p *HandlerPool) free(msgType string) {
27 | if len(p.handlers[msgType]) != 0 {
28 | return
29 | }
30 |
31 | delete(p.handlers, msgType)
32 | }
33 |
34 | // Remove is used to remove a handler with a specified identifier.
35 | func (p *HandlerPool) Remove(msgType string, _ int64) error {
36 | if _, ok := p.handlers[msgType]; !ok {
37 | return ErrHandleNotFound
38 | }
39 |
40 | p.free(msgType)
41 |
42 | return nil
43 | }
44 |
45 | func (p *HandlerPool) handlersByMsgType(msgType string) (result []interface{}) {
46 | p.mu.RLock()
47 | defer p.mu.RUnlock()
48 |
49 | handlers, ok := p.handlers[msgType]
50 | if !ok {
51 | return
52 | }
53 |
54 | result = make([]interface{}, 0, len(handlers))
55 | result = append(result, handlers...)
56 |
57 | return result
58 | }
59 |
60 | func (p *HandlerPool) add(msgType string, handle interface{}) int64 {
61 | p.mu.Lock()
62 | defer p.mu.Unlock()
63 |
64 | if _, ok := p.handlers[msgType]; !ok {
65 | p.handlers[msgType] = make([]interface{}, 0)
66 | }
67 |
68 | p.handlers[msgType] = append(p.handlers[msgType], handle)
69 |
70 | return int64(len(p.handlers)) - 1
71 | }
72 |
73 | // IncomingHandlerPool is used to manage the pool of incoming messages stored in the form of byte arrays.
74 | type IncomingHandlerPool struct {
75 | *HandlerPool
76 | }
77 |
78 | // NewIncomingHandlerPool creates a new HandlerPool instance.
79 | func NewIncomingHandlerPool() IncomingHandlerPool {
80 | return IncomingHandlerPool{NewHandlerPool()}
81 | }
82 |
83 | // Range is used for traversal through handlers. The traversal stops if any handler returns false.
84 | func (p IncomingHandlerPool) Range(msgType string, f func(IncomingHandlerFunc) bool) {
85 | for _, handle := range p.handlersByMsgType(msgType) {
86 | if !f(handle.(IncomingHandlerFunc)) {
87 | break
88 | }
89 | }
90 | }
91 |
92 | // Add is used to add a new message handler for the specified message type.
93 | // The function returns the ID of a message for which a handler was added.
94 | func (p *IncomingHandlerPool) Add(msgType string, handle IncomingHandlerFunc) int64 {
95 | return p.add(msgType, handle)
96 | }
97 |
98 | // OutgoingHandlerPool is used to manage the pool of outgoing messages stored as structures.
99 | type OutgoingHandlerPool struct {
100 | *HandlerPool
101 | }
102 |
103 | // NewOutgoingHandlerPool creates a new OutgoingHandlerPool instance.
104 | func NewOutgoingHandlerPool() OutgoingHandlerPool {
105 | return OutgoingHandlerPool{NewHandlerPool()}
106 | }
107 |
108 | // Range is used for traversal through handlers.
109 | // The traversal stops if any handler returns false.
110 | func (p OutgoingHandlerPool) Range(msgType string, f func(OutgoingHandlerFunc) bool) (res bool) {
111 | for _, handle := range p.handlersByMsgType(msgType) {
112 | if !f(handle.(OutgoingHandlerFunc)) {
113 | return false
114 | }
115 | }
116 |
117 | return true
118 | }
119 |
120 | // Add is used to add a new message handler for the specified message type.
121 | // The function returns the ID of a message for which a handler was added.
122 | func (p *HandlerPool) Add(msgType string, handle OutgoingHandlerFunc) int64 {
123 | return p.add(msgType, handle)
124 | }
125 |
--------------------------------------------------------------------------------
/initiator.go:
--------------------------------------------------------------------------------
1 | package simplefixgo
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net"
7 | "sync"
8 | "time"
9 |
10 | "golang.org/x/sync/errgroup"
11 | )
12 |
13 | // InitiatorHandler is an interface implementing basic methods required for handling the Initiator object.
14 | type InitiatorHandler interface {
15 | ServeIncoming(msg []byte)
16 | Outgoing() <-chan []byte
17 | Run() error
18 | StopWithError(err error)
19 | CloseErrorChan()
20 | Send(message SendingMessage) error
21 | SendBuffered(message SendingMessage) error
22 | Context() context.Context
23 | Stop()
24 | }
25 |
26 | // Initiator provides the client-side service functionality.
27 | type Initiator struct {
28 | conn *Conn
29 | handler InitiatorHandler
30 |
31 | ctx context.Context
32 | cancel context.CancelFunc
33 | }
34 |
35 | // NewInitiator creates a new Initiator instance.
36 | func NewInitiator(conn net.Conn, handler InitiatorHandler, bufSize int, writeDeadline time.Duration) *Initiator {
37 | c := &Initiator{handler: handler}
38 | c.ctx, c.cancel = context.WithCancel(context.Background())
39 |
40 | c.conn = NewConn(c.ctx, conn, bufSize, writeDeadline)
41 |
42 | return c
43 | }
44 |
45 | // Close is used to cancel the specified Initiator context.
46 | func (c *Initiator) Close() {
47 | c.conn.Close()
48 | c.cancel()
49 | }
50 |
51 | // Send is used to send a FIX message.
52 | func (c *Initiator) Send(message SendingMessage) error {
53 | return c.handler.Send(message)
54 | }
55 |
56 | // Serve is used to initiate the procedure of delivering messages.
57 | func (c *Initiator) Serve() error {
58 | eg := errgroup.Group{}
59 | defer c.Close()
60 | defer c.handler.CloseErrorChan()
61 |
62 | stopHandler := sync.Once{}
63 |
64 | eg.Go(func() error {
65 | defer c.Close()
66 |
67 | err := c.conn.serve()
68 | if err != nil {
69 | err = fmt.Errorf("%s: %w", err, ErrConnClosed)
70 | defer stopHandler.Do(func() {
71 | c.handler.StopWithError(err)
72 | })
73 | }
74 |
75 | return err
76 | })
77 |
78 | eg.Go(func() error {
79 | defer c.Close()
80 |
81 | return c.handler.Run()
82 | })
83 |
84 | eg.Go(func() error {
85 | defer c.Close()
86 |
87 | for {
88 | select {
89 | case <-c.ctx.Done():
90 | return nil
91 |
92 | case msg, ok := <-c.handler.Outgoing():
93 | if !ok {
94 | return fmt.Errorf("outgoing chan is closed")
95 | }
96 |
97 | err := c.conn.Write(msg)
98 | if err != nil {
99 | c.handler.Stop()
100 |
101 | return ErrConnClosed
102 | }
103 | }
104 | }
105 | })
106 |
107 | eg.Go(func() error {
108 | defer c.Close()
109 | select {
110 | case <-c.handler.Context().Done():
111 | stopHandler.Do(func() {})
112 | case <-c.ctx.Done():
113 | stopHandler.Do(func() {
114 | c.handler.StopWithError(nil)
115 | })
116 | }
117 |
118 | return nil
119 | })
120 |
121 | eg.Go(func() error {
122 | defer c.Close()
123 |
124 | for {
125 | select {
126 | case <-c.ctx.Done():
127 | return nil
128 |
129 | case msg, ok := <-c.conn.Reader():
130 | if !ok {
131 | continue
132 | }
133 | c.handler.ServeIncoming(msg)
134 | }
135 | }
136 | })
137 |
138 | err := eg.Wait()
139 | if err != nil {
140 | stopHandler.Do(func() {
141 | c.handler.StopWithError(err)
142 | })
143 | return fmt.Errorf("stop handler: %w", err)
144 | }
145 |
146 | return nil
147 | }
148 |
--------------------------------------------------------------------------------
/session/logon_settings.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import "time"
4 |
5 | // TODO: constructor for acceptor and initiator
6 | type LogonSettings struct {
7 | TargetCompID string
8 | SenderCompID string
9 | HeartBtInt int
10 | EncryptMethod string
11 | Password string
12 | Username string
13 | LogonTimeout time.Duration // todo
14 | HeartBtLimits *IntLimits
15 | CloseTimeout time.Duration
16 | ResetSeqNumFlag bool
17 | }
18 |
--------------------------------------------------------------------------------
/session/messages/builder.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "github.com/b2broker/simplefix-go/fix"
5 | "github.com/b2broker/simplefix-go/fix/buffer"
6 | )
7 |
8 | type Builder interface {
9 | Items() fix.Items
10 | CalcBodyLength() int
11 | BodyLength() int
12 | BytesWithoutChecksum() []byte
13 | CheckSum() string
14 | BeginString() *fix.KeyValue
15 | MsgType() string
16 | ToBytes() ([]byte, error)
17 | ToBytesBuffered(buffers *buffer.MessageByteBuffers) ([]byte, error)
18 | BeginStringTag() string
19 | BodyLengthTag() string
20 | CheckSumTag() string
21 | }
22 |
23 | type PipelineBuilder interface {
24 | HeaderBuilder() HeaderBuilder
25 | Builder
26 | }
27 |
--------------------------------------------------------------------------------
/session/messages/contstants.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "github.com/b2broker/simplefix-go/fix/buffer"
5 | )
6 |
7 | // Tags is a structure specifying the required tags for session pipelines.
8 | type Tags struct {
9 | MsgType int
10 | MsgSeqNum int
11 | HeartBtInt int
12 | EncryptedMethod int
13 | }
14 |
15 | // SessionErrorCodes is a structure specifying the session error codes.
16 | type SessionErrorCodes struct {
17 | InvalidTagNumber int
18 | RequiredTagMissing int
19 | TagNotDefinedForMessageType int
20 | UndefinedTag int
21 | TagSpecialWithoutValue int
22 | IncorrectValue int
23 | IncorrectDataFormatValue int
24 | DecryptionProblem int
25 | SignatureProblem int
26 | CompIDProblem int
27 | Other int
28 | }
29 |
30 | // Message is an interface providing the functionality required for sending messages.
31 | type Message interface {
32 | HeaderBuilder() HeaderBuilder
33 | MsgType() string
34 | ToBytes() ([]byte, error)
35 | ToBytesBuffered(buffers *buffer.MessageByteBuffers) ([]byte, error)
36 | }
37 |
--------------------------------------------------------------------------------
/session/messages/execution_report.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type ExecutionReport interface {
4 | New() ExecutionReportBuilder
5 | Build() ExecutionReportBuilder
6 | }
7 |
8 | // ExecutionReportBuilder is an interface providing functionality to a builder of auto-generated ExecutionReport messages.
9 | type ExecutionReportBuilder interface {
10 | ExecutionReport
11 | PipelineBuilder
12 | }
13 |
--------------------------------------------------------------------------------
/session/messages/header.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import "github.com/b2broker/simplefix-go/fix"
4 |
5 | // ComponentConverter is an interface providing functionality to a builder of trailer messages.
6 | type ComponentConverter interface {
7 | AsComponent() *fix.Component
8 | }
9 |
10 | // HeaderBuilder is an interface providing functionality to a builder of header messages.
11 | type HeaderBuilder interface {
12 | New() HeaderBuilder
13 |
14 | SenderCompID() string
15 | SetFieldSenderCompID(senderCompID string) HeaderBuilder
16 | TargetCompID() string
17 | SetFieldTargetCompID(targetCompID string) HeaderBuilder
18 | MsgSeqNum() int
19 | SetFieldMsgSeqNum(msgSeqNum int) HeaderBuilder
20 | SendingTime() string
21 | SetFieldSendingTime(string) HeaderBuilder
22 |
23 | AsComponent() *fix.Component
24 | }
25 |
--------------------------------------------------------------------------------
/session/messages/heartbeat.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type Heartbeat interface {
4 | New() HeartbeatBuilder
5 | Build() HeartbeatBuilder
6 | TestReqID() string
7 | SetFieldTestReqID(string) HeartbeatBuilder
8 | HeaderBuilder() HeaderBuilder
9 | }
10 |
11 | // HeartbeatBuilder is an interface providing functionality to a builder of auto-generated Heartbeat messages.
12 | type HeartbeatBuilder interface {
13 | Heartbeat
14 | PipelineBuilder
15 | }
16 |
--------------------------------------------------------------------------------
/session/messages/logon.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type Logon interface {
4 | New() LogonBuilder
5 | Build() LogonBuilder
6 | EncryptMethod() string
7 | SetFieldEncryptMethod(string) LogonBuilder
8 | HeartBtInt() int
9 | SetFieldHeartBtInt(int) LogonBuilder
10 |
11 | Password() string
12 | SetFieldPassword(string) LogonBuilder
13 | Username() string
14 | SetFieldUsername(string) LogonBuilder
15 | ResetSeqNumFlag() bool
16 | SetFieldResetSeqNumFlag(bool) LogonBuilder
17 | }
18 |
19 | // LogonBuilder is an interface providing functionality to a builder of auto-generated Logon messages.
20 | type LogonBuilder interface {
21 | Logon
22 | PipelineBuilder
23 | }
24 |
--------------------------------------------------------------------------------
/session/messages/logout.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type Logout interface {
4 | New() LogoutBuilder
5 | Build() LogoutBuilder
6 | }
7 |
8 | // LogoutBuilder is an interface providing functionality to a builder of auto-generated Logout messages.
9 | type LogoutBuilder interface {
10 | Logout
11 | PipelineBuilder
12 | }
13 |
--------------------------------------------------------------------------------
/session/messages/market_data_request.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type MarketDataRequest interface {
4 | New() MarketDataRequestBuilder
5 | Build() MarketDataRequestBuilder
6 | }
7 |
8 | // MarketDataRequestBuilder is an interface providing functionality to a builder of auto-generated MarketDataRequest messages.
9 | type MarketDataRequestBuilder interface {
10 | MarketDataRequest
11 | PipelineBuilder
12 | }
13 |
--------------------------------------------------------------------------------
/session/messages/mock_message.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "github.com/b2broker/simplefix-go/fix/buffer"
5 | )
6 |
7 | type MockMessage struct {
8 | Type string
9 | Data []byte
10 | Err error
11 | }
12 |
13 | func NewMockMessage(tp string, data []byte, err error) *MockMessage {
14 | return &MockMessage{Type: tp, Data: data, Err: err}
15 | }
16 |
17 | func (m MockMessage) HeaderBuilder() HeaderBuilder {
18 | return nil
19 | }
20 |
21 | func (m MockMessage) MsgType() string {
22 | return m.Type
23 | }
24 |
25 | func (m MockMessage) ToBytes() ([]byte, error) {
26 | return m.Data, m.Err
27 | }
28 | func (m MockMessage) ToBytesBuffered(_ *buffer.MessageByteBuffers) ([]byte, error) {
29 | return m.Data, m.Err
30 | }
31 |
--------------------------------------------------------------------------------
/session/messages/new_order_single.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type NewOrderSingle interface {
4 | New() NewOrderSingleBuilder
5 | Build() NewOrderSingleBuilder
6 | }
7 |
8 | // NewOrderSingleBuilder is an interface providing functionality to a builder of auto-generated NewOrderSingle messages.
9 | type NewOrderSingleBuilder interface {
10 | NewOrderSingle
11 | PipelineBuilder
12 | }
13 |
--------------------------------------------------------------------------------
/session/messages/order_cancel_request.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type OrderCancelRequest interface {
4 | New() OrderCancelRequestBuilder
5 | Build() OrderCancelRequestBuilder
6 | }
7 |
8 | // OrderCancelRequestBuilder is an interface providing functionality to a builder of auto-generated OrderCancelRequest messages.
9 | type OrderCancelRequestBuilder interface {
10 | OrderCancelRequest
11 | PipelineBuilder
12 | }
13 |
--------------------------------------------------------------------------------
/session/messages/reject.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type Reject interface {
4 | New() RejectBuilder
5 | Build() RejectBuilder
6 | RefTagID() int
7 | SetFieldRefTagID(int) RejectBuilder
8 | RefSeqNum() int
9 | SetFieldRefSeqNum(int) RejectBuilder
10 | SessionRejectReason() string
11 | SetFieldSessionRejectReason(string) RejectBuilder
12 | }
13 |
14 | // RejectBuilder is an interface providing functionality to a builder of auto-generated Reject messages.
15 | type RejectBuilder interface {
16 | Reject
17 | PipelineBuilder
18 | }
19 |
--------------------------------------------------------------------------------
/session/messages/resend_request.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type ResendRequest interface {
4 | New() ResendRequestBuilder
5 | Build() ResendRequestBuilder
6 | BeginSeqNo() int
7 | SetFieldBeginSeqNo(int) ResendRequestBuilder
8 | EndSeqNo() int
9 | SetFieldEndSeqNo(int) ResendRequestBuilder
10 | }
11 |
12 | // ResendRequestBuilder is an interface providing functionality to a builder of auto-generated ResendRequest messages.
13 | type ResendRequestBuilder interface {
14 | ResendRequest
15 | PipelineBuilder
16 | }
17 |
--------------------------------------------------------------------------------
/session/messages/sequence_reset.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type SequenceReset interface {
4 | New() SequenceResetBuilder
5 | Build() SequenceResetBuilder
6 | NewSeqNo() int
7 | SetFieldNewSeqNo(int) SequenceResetBuilder
8 | GapFillFlag() bool
9 | SetFieldGapFillFlag(bool) SequenceResetBuilder
10 | }
11 |
12 | // SequenceResetBuilder is an interface providing functionality to a builder of auto-generated SequenceReset messages.
13 | type SequenceResetBuilder interface {
14 | SequenceReset
15 | PipelineBuilder
16 | }
17 |
--------------------------------------------------------------------------------
/session/messages/test_request.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | type TestRequest interface {
4 | New() TestRequestBuilder
5 | Build() TestRequestBuilder
6 | SetFieldTestReqID(string) TestRequestBuilder
7 | TestReqID() string
8 | }
9 |
10 | // TestRequestBuilder is an interface providing functionality to a builder of auto-generated TestRequest messages.
11 | type TestRequestBuilder interface {
12 | TestRequest
13 | PipelineBuilder
14 | }
15 |
--------------------------------------------------------------------------------
/session/messages/trailer.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import "github.com/b2broker/simplefix-go/fix"
4 |
5 | // TrailerBuilder is an interface providing functionality to a builder of Trailer messages.
6 | type TrailerBuilder interface {
7 | New() TrailerBuilder
8 |
9 | AsComponent() *fix.Component
10 | }
11 |
--------------------------------------------------------------------------------
/session/opts.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "fmt"
5 | "github.com/b2broker/simplefix-go/session/messages"
6 | )
7 |
8 | type MessageBuilders struct {
9 | HeaderBuilder messages.HeaderBuilder
10 | TrailerBuilder messages.TrailerBuilder
11 | LogonBuilder messages.LogonBuilder
12 | LogoutBuilder messages.LogoutBuilder
13 | RejectBuilder messages.RejectBuilder
14 | HeartbeatBuilder messages.HeartbeatBuilder
15 | TestRequestBuilder messages.TestRequestBuilder
16 | ResendRequestBuilder messages.ResendRequestBuilder
17 | SequenceResetBuilder messages.SequenceResetBuilder
18 | ExecutionReportBuilder messages.ExecutionReportBuilder
19 | NewOrderSingleBuilder messages.NewOrderSingleBuilder
20 | MarketDataRequestBuilder messages.MarketDataRequestBuilder
21 | OrderCancelRequestBuilder messages.OrderCancelRequestBuilder
22 | }
23 |
24 | // Opts is a structure providing auto-generated Session options.
25 | type Opts struct {
26 | Location string
27 | MessageBuilders MessageBuilders
28 | Tags *messages.Tags
29 | AllowedEncryptedMethods map[string]struct{} // Can only be of the "None" type.
30 | SessionErrorCodes *messages.SessionErrorCodes
31 | }
32 |
33 | type Side int64
34 |
35 | const (
36 | sideAcceptor = iota
37 | sideInitiator
38 | )
39 |
40 | func (opts *Opts) validate() error {
41 | if opts == nil {
42 | return ErrMissingSessionOts
43 | }
44 |
45 | if opts.MessageBuilders.HeaderBuilder == nil {
46 | return fmt.Errorf("%w: header", ErrMissingMessageBuilder)
47 | }
48 |
49 | if opts.MessageBuilders.TrailerBuilder == nil {
50 | return fmt.Errorf("%w: trailer", ErrMissingMessageBuilder)
51 | }
52 |
53 | if opts.MessageBuilders.HeartbeatBuilder == nil {
54 | return fmt.Errorf("%w: heartbeat", ErrMissingMessageBuilder)
55 | }
56 |
57 | if opts.MessageBuilders.ResendRequestBuilder == nil {
58 | return fmt.Errorf("%w: resend request", ErrMissingMessageBuilder)
59 | }
60 |
61 | if opts.MessageBuilders.TestRequestBuilder == nil {
62 | return fmt.Errorf("%w: test request", ErrMissingMessageBuilder)
63 | }
64 |
65 | if opts.MessageBuilders.LogoutBuilder == nil {
66 | return fmt.Errorf("%w: logout", ErrMissingMessageBuilder)
67 | }
68 |
69 | if opts.MessageBuilders.LogonBuilder == nil {
70 | return fmt.Errorf("%w: logon", ErrMissingMessageBuilder)
71 | }
72 |
73 | if opts.MessageBuilders.RejectBuilder == nil {
74 | return fmt.Errorf("%w: reject", ErrMissingMessageBuilder)
75 | }
76 |
77 | if opts.Tags == nil {
78 | return ErrMissingRequiredTag
79 | }
80 |
81 | if opts.SessionErrorCodes == nil {
82 | return ErrMissingErrorCodes
83 | }
84 |
85 | return nil
86 | }
87 |
--------------------------------------------------------------------------------
/session/storage.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | simplefixgo "github.com/b2broker/simplefix-go"
5 | "github.com/b2broker/simplefix-go/fix"
6 | )
7 |
8 | // MessageStorage is an interface providing a basic method for storing messages awaiting to be sent.
9 | type MessageStorage interface {
10 | Save(storageID fix.StorageID, msg simplefixgo.SendingMessage, msgSeqNum int) error
11 | Messages(storageID fix.StorageID, msgSeqNumFrom, msgSeqNumTo int) ([]simplefixgo.SendingMessage, error)
12 | }
13 |
14 | type CounterStorage interface {
15 | GetNextSeqNum(storageID fix.StorageID) (int, error)
16 | GetCurrSeqNum(storageID fix.StorageID) (int, error)
17 | ResetSeqNum(storageID fix.StorageID) error
18 | SetSeqNum(storageID fix.StorageID, seqNum int) error
19 | }
20 |
--------------------------------------------------------------------------------
/session/unmarshaller.go:
--------------------------------------------------------------------------------
1 | package session
2 |
3 | import (
4 | "github.com/b2broker/simplefix-go/session/messages"
5 | )
6 |
7 | type Unmarshaller interface {
8 | Unmarshal(msg messages.Builder, d []byte) error
9 | }
10 |
--------------------------------------------------------------------------------
/source/types.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/storages/memory/storage.go:
--------------------------------------------------------------------------------
1 | package memory
2 |
3 | import (
4 | simplefixgo "github.com/b2broker/simplefix-go"
5 | "github.com/b2broker/simplefix-go/fix"
6 | "sync"
7 | "sync/atomic"
8 | )
9 |
10 | // Storage is used to store the most recent messages.
11 | type Storage struct {
12 | counterIncoming int64
13 | counterOutgoing int64
14 | messages map[int]simplefixgo.SendingMessage
15 | mu sync.Mutex
16 | }
17 |
18 | // NewStorage is a constructor for creation of a new in-memory Storage.
19 | func NewStorage() *Storage {
20 | return &Storage{
21 | messages: map[int]simplefixgo.SendingMessage{},
22 | mu: sync.Mutex{},
23 | }
24 | }
25 |
26 | func (s *Storage) GetNextSeqNum(storageID fix.StorageID) (int, error) {
27 | if storageID.Side == fix.Incoming {
28 | return int(atomic.AddInt64(&s.counterIncoming, 1)), nil
29 | } else {
30 | return int(atomic.AddInt64(&s.counterOutgoing, 1)), nil
31 | }
32 | }
33 |
34 | func (s *Storage) GetCurrSeqNum(storageID fix.StorageID) (int, error) {
35 | if storageID.Side == fix.Incoming {
36 | return int(s.counterIncoming), nil
37 | } else {
38 | return int(s.counterOutgoing), nil
39 | }
40 | }
41 |
42 | func (s *Storage) ResetSeqNum(storageID fix.StorageID) error {
43 | if storageID.Side == fix.Incoming {
44 | s.counterIncoming = 0
45 | } else {
46 | s.counterOutgoing = 0
47 | }
48 | return nil
49 | }
50 |
51 | func (s *Storage) SetSeqNum(storageID fix.StorageID, seqNum int) error {
52 | if storageID.Side == fix.Incoming {
53 | s.counterIncoming = int64(seqNum)
54 | } else {
55 | s.counterOutgoing = int64(seqNum)
56 | }
57 | return nil
58 | }
59 |
60 | // Save saves a message with seq number to storage
61 | func (s *Storage) Save(_ fix.StorageID, msg simplefixgo.SendingMessage, msgSeqNum int) error {
62 | s.mu.Lock()
63 | defer s.mu.Unlock()
64 | s.messages[msgSeqNum] = msg
65 | return nil
66 | }
67 |
68 | // Messages returns a message list, in a sequential order
69 | // (starting with msgSeqNumFrom and ending with msgSeqNumTo).
70 | func (s *Storage) Messages(_ fix.StorageID, msgSeqNumFrom, msgSeqNumTo int) ([]simplefixgo.SendingMessage, error) {
71 | s.mu.Lock()
72 | defer s.mu.Unlock()
73 |
74 | if msgSeqNumFrom > msgSeqNumTo {
75 | return nil, simplefixgo.ErrInvalidBoundaries
76 | }
77 |
78 | if int64(msgSeqNumTo) > s.counterOutgoing {
79 | return nil, simplefixgo.ErrNotEnoughMessages
80 | }
81 |
82 | var sendingMessages []simplefixgo.SendingMessage
83 | for i := msgSeqNumFrom; i <= msgSeqNumTo; i++ {
84 | if _, ok := s.messages[i]; !ok {
85 | return nil, simplefixgo.ErrNotEnoughMessages
86 | }
87 | sendingMessages = append(sendingMessages, s.messages[i])
88 | }
89 |
90 | return sendingMessages, nil
91 | }
92 |
--------------------------------------------------------------------------------
/tests/acceptor.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "fmt"
5 | "github.com/b2broker/simplefix-go/storages/memory"
6 | "net"
7 | "testing"
8 | "time"
9 |
10 | simplefixgo "github.com/b2broker/simplefix-go"
11 | "github.com/b2broker/simplefix-go/session"
12 | fixgen "github.com/b2broker/simplefix-go/tests/fix44"
13 | )
14 |
15 | func RunAcceptor(port int, t *testing.T) (acceptor *simplefixgo.Acceptor, addr string) {
16 | listener, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
17 | if err != nil {
18 | t.Fatalf("listen error: %s", err)
19 | }
20 |
21 | handlerFactory := simplefixgo.NewAcceptorHandlerFactory(fixgen.FieldMsgType, 10)
22 |
23 | testStorage := memory.NewStorage()
24 |
25 | server := simplefixgo.NewAcceptor(listener, handlerFactory, time.Minute*50, func(handler simplefixgo.AcceptorHandler) {
26 | s, err := session.NewAcceptorSession(
27 | &pseudoGeneratedOpts,
28 | handler,
29 | &session.LogonSettings{
30 | LogonTimeout: time.Second * 30,
31 | HeartBtLimits: &session.IntLimits{
32 | Min: 1,
33 | Max: 60,
34 | },
35 | },
36 | func(request *session.LogonSettings) (err error) {
37 | t.Logf(
38 | "user '%s' connected with password '%s'",
39 | request.Username,
40 | request.Password,
41 | )
42 |
43 | return nil
44 | },
45 | testStorage,
46 | testStorage,
47 | )
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | err = s.Run()
53 | if err != nil {
54 | t.Fatalf("run s: %s", err)
55 | }
56 | })
57 |
58 | return server, listener.Addr().String()
59 | }
60 |
--------------------------------------------------------------------------------
/tests/fix44/altmdsourcegrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type AltMDSourceGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewAltMDSourceGrp() *AltMDSourceGrp {
14 | return &AltMDSourceGrp{
15 | fix.NewGroup(FieldNoAltMDSource,
16 | fix.NewKeyValue(FieldAltMDSourceID, &fix.String{}),
17 | ),
18 | }
19 | }
20 |
21 | func (group *AltMDSourceGrp) AddEntry(entry *AltMDSourceEntry) *AltMDSourceGrp {
22 | group.Group.AddEntry(entry.Items())
23 |
24 | return group
25 | }
26 |
27 | func (group *AltMDSourceGrp) Entries() []*AltMDSourceEntry {
28 | items := make([]*AltMDSourceEntry, len(group.Group.Entries()))
29 |
30 | for i, item := range group.Group.Entries() {
31 | items[i] = &AltMDSourceEntry{fix.NewComponent(item...)}
32 | }
33 |
34 | return items
35 | }
36 |
37 | type AltMDSourceEntry struct {
38 | *fix.Component
39 | }
40 |
41 | func makeAltMDSourceEntry() *AltMDSourceEntry {
42 | return &AltMDSourceEntry{fix.NewComponent(
43 | fix.NewKeyValue(FieldAltMDSourceID, &fix.String{}),
44 | )}
45 | }
46 |
47 | func NewAltMDSourceEntry() *AltMDSourceEntry {
48 | return makeAltMDSourceEntry()
49 | }
50 |
51 | func (altMDSourceEntry *AltMDSourceEntry) AltMDSourceID() string {
52 | kv := altMDSourceEntry.Get(0)
53 | v := kv.(*fix.KeyValue).Load().Value()
54 | return v.(string)
55 | }
56 |
57 | func (altMDSourceEntry *AltMDSourceEntry) SetAltMDSourceID(altMDSourceID string) *AltMDSourceEntry {
58 | kv := altMDSourceEntry.Get(0).(*fix.KeyValue)
59 | _ = kv.Load().Set(altMDSourceID)
60 | return altMDSourceEntry
61 | }
62 |
--------------------------------------------------------------------------------
/tests/fix44/enum_applqueueaction.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumApplQueueAction
6 | const (
7 | EnumApplQueueActionNoactiontaken string = "0"
8 | EnumApplQueueActionQueueflushed string = "1"
9 | EnumApplQueueActionOverlaylast string = "2"
10 | EnumApplQueueActionEndsession string = "3"
11 | )
12 |
--------------------------------------------------------------------------------
/tests/fix44/enum_applqueueresolution.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumApplQueueResolution
6 | const (
7 | EnumApplQueueResolutionNoactiontaken string = "0"
8 | EnumApplQueueResolutionQueueflushed string = "1"
9 | EnumApplQueueResolutionOverlaylast string = "2"
10 | EnumApplQueueResolutionEndsession string = "3"
11 | )
12 |
--------------------------------------------------------------------------------
/tests/fix44/enum_corporateaction.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumCorporateAction
6 | const (
7 | EnumCorporateActionExdividend string = "A"
8 | EnumCorporateActionExdist string = "B"
9 | EnumCorporateActionExrights string = "C"
10 | EnumCorporateActionNew string = "D"
11 | EnumCorporateActionExinterest string = "E"
12 | )
13 |
--------------------------------------------------------------------------------
/tests/fix44/enum_cpprogram.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumCPProgram
6 | const (
7 | EnumCPProgram3a3 string = "1"
8 | EnumCPProgram42 string = "2"
9 | EnumCPProgramOther string = "99"
10 | )
11 |
--------------------------------------------------------------------------------
/tests/fix44/enum_deletereason.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumDeleteReason
6 | const (
7 | EnumDeleteReasonCanceltradebust string = "0"
8 | EnumDeleteReasonError string = "1"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/fix44/enum_encryptmethod.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumEncryptMethod
6 | const (
7 | EnumEncryptMethodNoneother string = "0"
8 | EnumEncryptMethodPkcsproprietary string = "1"
9 | EnumEncryptMethodDesecbmode string = "2"
10 | EnumEncryptMethodPkcsdesproprietary string = "3"
11 | EnumEncryptMethodPgpdesdefunct string = "4"
12 | EnumEncryptMethodPgpdesmd5seeappnoteonfixwebsite string = "5"
13 | EnumEncryptMethodPemdesmd5seeappnoteonfixwebsitenaforfixmlnotused string = "6"
14 | )
15 |
--------------------------------------------------------------------------------
/tests/fix44/enum_eventtype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumEventType
6 | const (
7 | EnumEventTypePut string = "1"
8 | EnumEventTypeCall string = "2"
9 | EnumEventTypeTender string = "3"
10 | EnumEventTypeSinkingfundcall string = "4"
11 | EnumEventTypeOther string = "99"
12 | )
13 |
--------------------------------------------------------------------------------
/tests/fix44/enum_execinst.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumExecInst
6 | const (
7 | EnumExecInstStayoffer string = "0"
8 | EnumExecInstNotheld string = "1"
9 | EnumExecInstWork string = "2"
10 | EnumExecInstGoalong string = "3"
11 | EnumExecInstOverday string = "4"
12 | EnumExecInstHeld string = "5"
13 | EnumExecInstPartnotinit string = "6"
14 | EnumExecInstStrictscale string = "7"
15 | EnumExecInstTrytoscale string = "8"
16 | EnumExecInstStaybid string = "9"
17 | EnumExecInstNocross string = "A"
18 | EnumExecInstTrailstoppeg string = "a"
19 | EnumExecInstOkcross string = "B"
20 | EnumExecInstStrictlimit string = "b"
21 | EnumExecInstIgnorepricechk string = "c"
22 | EnumExecInstCallfirst string = "C"
23 | EnumExecInstPegtolimit string = "d"
24 | EnumExecInstPercvol string = "D"
25 | EnumExecInstDni string = "E"
26 | EnumExecInstWorktostrategy string = "e"
27 | EnumExecInstDnr string = "F"
28 | EnumExecInstAon string = "G"
29 | EnumExecInstRestateonsysfail string = "H"
30 | EnumExecInstInstitonly string = "I"
31 | EnumExecInstRestateontradinghalt string = "J"
32 | EnumExecInstCancelontradinghalt string = "K"
33 | EnumExecInstLastpeg string = "L"
34 | EnumExecInstMidprcpeg string = "M"
35 | EnumExecInstNonnego string = "N"
36 | EnumExecInstOpenpeg string = "O"
37 | EnumExecInstMarkpeg string = "P"
38 | EnumExecInstCancelonsysfail string = "Q"
39 | EnumExecInstPrimpeg string = "R"
40 | EnumExecInstSuspend string = "S"
41 | EnumExecInstCustdispinst string = "U"
42 | EnumExecInstNetting string = "V"
43 | EnumExecInstPegvwap string = "W"
44 | EnumExecInstTradealong string = "X"
45 | EnumExecInstTrytostop string = "Y"
46 | EnumExecInstCxlifnotbest string = "Z"
47 | )
48 |
--------------------------------------------------------------------------------
/tests/fix44/enum_financialstatus.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumFinancialStatus
6 | const (
7 | EnumFinancialStatusBankrupt string = "1"
8 | EnumFinancialStatusPendingdelisting string = "2"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/fix44/enum_instrregistry.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumInstrRegistry
6 | const (
7 | EnumInstrRegistryCustodian string = "BIC"
8 | EnumInstrRegistryCountry string = "ISO"
9 | EnumInstrRegistryPhysical string = "ZZ"
10 | )
11 |
--------------------------------------------------------------------------------
/tests/fix44/enum_mdentrytype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMDEntryType
6 | const (
7 | EnumMDEntryTypeBid string = "0"
8 | EnumMDEntryTypeOffer string = "1"
9 | EnumMDEntryTypeTrade string = "2"
10 | EnumMDEntryTypeIndexvalue string = "3"
11 | EnumMDEntryTypeOpening string = "4"
12 | EnumMDEntryTypeClosing string = "5"
13 | EnumMDEntryTypeSettlement string = "6"
14 | EnumMDEntryTypeTradinghigh string = "7"
15 | EnumMDEntryTypeTradinglow string = "8"
16 | EnumMDEntryTypeTradingvwap string = "9"
17 | EnumMDEntryTypeImbalance string = "A"
18 | EnumMDEntryTypeTradevolume string = "B"
19 | EnumMDEntryTypeOpeninterest string = "C"
20 | )
21 |
--------------------------------------------------------------------------------
/tests/fix44/enum_mdreqrejreason.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMDReqRejReason
6 | const (
7 | EnumMDReqRejReasonUnknownsym string = "0"
8 | EnumMDReqRejReasonDupid string = "1"
9 | EnumMDReqRejReasonInsband string = "2"
10 | EnumMDReqRejReasonInsperm string = "3"
11 | EnumMDReqRejReasonUnsuppsub string = "4"
12 | EnumMDReqRejReasonUnsuppmktdepth string = "5"
13 | EnumMDReqRejReasonUnsuppmdupdate string = "6"
14 | EnumMDReqRejReasonUnsuppaggbk string = "7"
15 | EnumMDReqRejReasonUnsuppentry string = "8"
16 | EnumMDReqRejReasonUnsupptrdsessionid string = "9"
17 | EnumMDReqRejReasonUnsuppscope string = "A"
18 | EnumMDReqRejReasonUnsupppositioneffectsettleflag string = "B"
19 | EnumMDReqRejReasonUnsuppmdimplicitdelete string = "C"
20 | )
21 |
--------------------------------------------------------------------------------
/tests/fix44/enum_mdupdateaction.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMDUpdateAction
6 | const (
7 | EnumMDUpdateActionNew string = "0"
8 | EnumMDUpdateActionChange string = "1"
9 | EnumMDUpdateActionDelete string = "2"
10 | )
11 |
--------------------------------------------------------------------------------
/tests/fix44/enum_mdupdatetype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMDUpdateType
6 | const (
7 | EnumMDUpdateTypeFull string = "0"
8 | EnumMDUpdateTypeIncremental string = "1"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/fix44/enum_msgdirection.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMsgDirection
6 | const (
7 | EnumMsgDirectionReceive string = "R"
8 | EnumMsgDirectionSend string = "S"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/fix44/enum_msgtype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumMsgType
6 | const (
7 | EnumMsgTypeHeartbeat string = "0"
8 | EnumMsgTypeTestrequest string = "1"
9 | EnumMsgTypeResendrequest string = "2"
10 | EnumMsgTypeReject string = "3"
11 | EnumMsgTypeSequencereset string = "4"
12 | EnumMsgTypeLogout string = "5"
13 | EnumMsgTypeIoi string = "6"
14 | EnumMsgTypeAdvertisement string = "7"
15 | EnumMsgTypeExecutionreport string = "8"
16 | EnumMsgTypeOrdercancelreject string = "9"
17 | EnumMsgTypeQuotestatusrequest string = "a"
18 | EnumMsgTypeLogon string = "A"
19 | EnumMsgTypeDerivativesecuritylist string = "AA"
20 | EnumMsgTypeNewordermultileg string = "AB"
21 | EnumMsgTypeMultilegordercancelreplace string = "AC"
22 | EnumMsgTypeTradecapturereportrequest string = "AD"
23 | EnumMsgTypeTradecapturereport string = "AE"
24 | EnumMsgTypeOrdermassstatusrequest string = "AF"
25 | EnumMsgTypeQuoterequestreject string = "AG"
26 | EnumMsgTypeRfqrequest string = "AH"
27 | EnumMsgTypeQuotestatusreport string = "AI"
28 | EnumMsgTypeQuoteresponse string = "AJ"
29 | EnumMsgTypeConfirmation string = "AK"
30 | EnumMsgTypePositionmaintenancerequest string = "AL"
31 | EnumMsgTypePositionmaintenancereport string = "AM"
32 | EnumMsgTypeRequestforpositions string = "AN"
33 | EnumMsgTypeRequestforpositionsack string = "AO"
34 | EnumMsgTypePositionreport string = "AP"
35 | EnumMsgTypeTradecapturereportrequestack string = "AQ"
36 | EnumMsgTypeTradecapturereportack string = "AR"
37 | EnumMsgTypeAllocationreport string = "AS"
38 | EnumMsgTypeAllocationreportack string = "AT"
39 | EnumMsgTypeConfirmationack string = "AU"
40 | EnumMsgTypeSettlementinstructionrequest string = "AV"
41 | EnumMsgTypeAssignmentreport string = "AW"
42 | EnumMsgTypeCollateralrequest string = "AX"
43 | EnumMsgTypeCollateralassignment string = "AY"
44 | EnumMsgTypeCollateralresponse string = "AZ"
45 | EnumMsgTypeNews string = "B"
46 | EnumMsgTypeMassquoteacknowledgement string = "b"
47 | EnumMsgTypeCollateralreport string = "BA"
48 | EnumMsgTypeCollateralinquiry string = "BB"
49 | EnumMsgTypeNetworkcounterpartysystemstatusrequest string = "BC"
50 | EnumMsgTypeNetworkcounterpartysystemstatusresponse string = "BD"
51 | EnumMsgTypeUserrequest string = "BE"
52 | EnumMsgTypeUserresponse string = "BF"
53 | EnumMsgTypeCollateralinquiryack string = "BG"
54 | EnumMsgTypeConfirmationrequest string = "BH"
55 | EnumMsgTypeEmail string = "C"
56 | EnumMsgTypeSecuritydefinitionrequest string = "c"
57 | EnumMsgTypeSecuritydefinition string = "d"
58 | EnumMsgTypeNewordersingle string = "D"
59 | EnumMsgTypeSecuritystatusrequest string = "e"
60 | EnumMsgTypeNeworderlist string = "E"
61 | EnumMsgTypeOrdercancelrequest string = "F"
62 | EnumMsgTypeSecuritystatus string = "f"
63 | EnumMsgTypeOrdercancelreplacerequest string = "G"
64 | EnumMsgTypeTradingsessionstatusrequest string = "g"
65 | EnumMsgTypeOrderstatusrequest string = "H"
66 | EnumMsgTypeTradingsessionstatus string = "h"
67 | EnumMsgTypeMassquote string = "i"
68 | EnumMsgTypeBusinessmessagereject string = "j"
69 | EnumMsgTypeAllocationinstruction string = "J"
70 | EnumMsgTypeBidrequest string = "k"
71 | EnumMsgTypeListcancelrequest string = "K"
72 | EnumMsgTypeBidresponse string = "l"
73 | EnumMsgTypeListexecute string = "L"
74 | EnumMsgTypeListstrikeprice string = "m"
75 | EnumMsgTypeListstatusrequest string = "M"
76 | EnumMsgTypeXmlnonfix string = "n"
77 | EnumMsgTypeListstatus string = "N"
78 | EnumMsgTypeRegistrationinstructions string = "o"
79 | EnumMsgTypeRegistrationinstructionsresponse string = "p"
80 | EnumMsgTypeAllocationinstructionack string = "P"
81 | EnumMsgTypeOrdermasscancelrequest string = "q"
82 | EnumMsgTypeDontknowtradedk string = "Q"
83 | EnumMsgTypeQuoterequest string = "R"
84 | EnumMsgTypeOrdermasscancelreport string = "r"
85 | EnumMsgTypeQuote string = "S"
86 | EnumMsgTypeNewordercross string = "s"
87 | EnumMsgTypeSettlementinstructions string = "T"
88 | EnumMsgTypeCrossordercancelreplacerequest string = "t"
89 | EnumMsgTypeCrossordercancelrequest string = "u"
90 | EnumMsgTypeMarketdatarequest string = "V"
91 | EnumMsgTypeSecuritytyperequest string = "v"
92 | EnumMsgTypeSecuritytypes string = "w"
93 | EnumMsgTypeMarketdatasnapshotfullrefresh string = "W"
94 | EnumMsgTypeSecuritylistrequest string = "x"
95 | EnumMsgTypeMarketdataincrementalrefresh string = "X"
96 | EnumMsgTypeMarketdatarequestreject string = "Y"
97 | EnumMsgTypeSecuritylist string = "y"
98 | EnumMsgTypeQuotecancel string = "Z"
99 | EnumMsgTypeDerivativesecuritylistrequest string = "z"
100 | )
101 |
--------------------------------------------------------------------------------
/tests/fix44/enum_openclosesettlflag.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumOpenCloseSettlFlag
6 | const (
7 | EnumOpenCloseSettlFlagDailyopen string = "0"
8 | EnumOpenCloseSettlFlagSessionopen string = "1"
9 | EnumOpenCloseSettlFlagDeliverysettlement string = "2"
10 | EnumOpenCloseSettlFlagExpectedentry string = "3"
11 | EnumOpenCloseSettlFlagEntryfromprevbusinessday string = "4"
12 | EnumOpenCloseSettlFlagTheoreticalprice string = "5"
13 | )
14 |
--------------------------------------------------------------------------------
/tests/fix44/enum_product.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumProduct
6 | const (
7 | EnumProductAgency string = "1"
8 | EnumProductMortgage string = "10"
9 | EnumProductMunicipal string = "11"
10 | EnumProductOther string = "12"
11 | EnumProductFinancing string = "13"
12 | EnumProductCommodity string = "2"
13 | EnumProductCorporate string = "3"
14 | EnumProductCurrency string = "4"
15 | EnumProductEquity string = "5"
16 | EnumProductGovernment string = "6"
17 | EnumProductIndex string = "7"
18 | EnumProductLoan string = "8"
19 | EnumProductMoneymarket string = "9"
20 | )
21 |
--------------------------------------------------------------------------------
/tests/fix44/enum_quotecondition.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumQuoteCondition
6 | const (
7 | EnumQuoteConditionOpen string = "A"
8 | EnumQuoteConditionClosed string = "B"
9 | EnumQuoteConditionExchbest string = "C"
10 | EnumQuoteConditionConsolbest string = "D"
11 | EnumQuoteConditionLocked string = "E"
12 | EnumQuoteConditionCrossed string = "F"
13 | EnumQuoteConditionDepth string = "G"
14 | EnumQuoteConditionFast string = "H"
15 | EnumQuoteConditionNonfirm string = "I"
16 | )
17 |
--------------------------------------------------------------------------------
/tests/fix44/enum_scope.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumScope
6 | const (
7 | EnumScopeLocalmarket string = "1"
8 | EnumScopeNational string = "2"
9 | EnumScopeGlobal string = "3"
10 | )
11 |
--------------------------------------------------------------------------------
/tests/fix44/enum_securityidsource.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumSecurityIDSource
6 | const (
7 | EnumSecurityIDSourceCusip string = "1"
8 | EnumSecurityIDSourceSedol string = "2"
9 | EnumSecurityIDSourceQuik string = "3"
10 | EnumSecurityIDSourceIsin string = "4"
11 | EnumSecurityIDSourceRic string = "5"
12 | EnumSecurityIDSourceIsocurr string = "6"
13 | EnumSecurityIDSourceIsocountry string = "7"
14 | EnumSecurityIDSourceExchsymb string = "8"
15 | EnumSecurityIDSourceCta string = "9"
16 | EnumSecurityIDSourceBlmbrg string = "A"
17 | EnumSecurityIDSourceWertpapier string = "B"
18 | EnumSecurityIDSourceDutch string = "C"
19 | EnumSecurityIDSourceValoren string = "D"
20 | EnumSecurityIDSourceSicovam string = "E"
21 | EnumSecurityIDSourceBelgian string = "F"
22 | EnumSecurityIDSourceCommon string = "G"
23 | EnumSecurityIDSourceClearinghouse string = "H"
24 | EnumSecurityIDSourceFpml string = "I"
25 | EnumSecurityIDSourceOptionpricereportingauthority string = "J"
26 | )
27 |
--------------------------------------------------------------------------------
/tests/fix44/enum_securitytype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumSecurityType
6 | const (
7 | EnumSecurityTypeAssetbackedsecurities string = "ABS"
8 | EnumSecurityTypeAmendedrestated string = "AMENDED"
9 | EnumSecurityTypeOtheranticipationnotesbanganetc string = "AN"
10 | EnumSecurityTypeBankersacceptance string = "BA"
11 | EnumSecurityTypeBanknotes string = "BN"
12 | EnumSecurityTypeBillofexchanges string = "BOX"
13 | EnumSecurityTypeBradybond string = "BRADY"
14 | EnumSecurityTypeBridgeloan string = "BRIDGE"
15 | EnumSecurityTypeBuysellback string = "BUYSELL"
16 | EnumSecurityTypeConvertiblebond string = "CB"
17 | EnumSecurityTypeCertificateofdeposit string = "CD"
18 | EnumSecurityTypeCallloans string = "CL"
19 | EnumSecurityTypeCorpmortgagebackedsecurities string = "CMBS"
20 | EnumSecurityTypeCollateralizedmortgageobligation string = "CMO"
21 | EnumSecurityTypeCertificateofobligation string = "COFO"
22 | EnumSecurityTypeCertificateofparticipation string = "COFP"
23 | EnumSecurityTypeCorporatebond string = "CORP"
24 | EnumSecurityTypeCommercialpaper string = "CP"
25 | EnumSecurityTypeCorporateprivateplacement string = "CPP"
26 | EnumSecurityTypeCommonstock string = "CS"
27 | EnumSecurityTypeDefaulted string = "DEFLTED"
28 | EnumSecurityTypeDebtorinpossession string = "DINP"
29 | EnumSecurityTypeDepositnotes string = "DN"
30 | EnumSecurityTypeDualcurrency string = "DUAL"
31 | EnumSecurityTypeEurocertificateofdeposit string = "EUCD"
32 | EnumSecurityTypeEurocorporatebond string = "EUCORP"
33 | EnumSecurityTypeEurocommercialpaper string = "EUCP"
34 | EnumSecurityTypeEurosovereigns string = "EUSOV"
35 | EnumSecurityTypeEurosupranationalcoupons string = "EUSUPRA"
36 | EnumSecurityTypeFederalagencycoupon string = "FAC"
37 | EnumSecurityTypeFederalagencydiscountnote string = "FADN"
38 | EnumSecurityTypeForeignexchangecontract string = "FOR"
39 | EnumSecurityTypeForward string = "FORWARD"
40 | EnumSecurityTypeFuture string = "FUT"
41 | EnumSecurityTypeGeneralobligationbonds string = "GO"
42 | EnumSecurityTypeIoettemortgage string = "IET"
43 | EnumSecurityTypeLetterofcredit string = "LOFC"
44 | EnumSecurityTypeLiquiditynote string = "LQN"
45 | EnumSecurityTypeMatured string = "MATURED"
46 | EnumSecurityTypeMortgagebackedsecurities string = "MBS"
47 | EnumSecurityTypeMutualfund string = "MF"
48 | EnumSecurityTypeMortgageinterestonly string = "MIO"
49 | EnumSecurityTypeMultileginstrument string = "MLEG"
50 | EnumSecurityTypeMortgageprincipalonly string = "MPO"
51 | EnumSecurityTypeMortgageprivateplacement string = "MPP"
52 | EnumSecurityTypeMiscellaneouspassthrough string = "MPT"
53 | EnumSecurityTypeMandatorytender string = "MT"
54 | EnumSecurityTypeMediumtermnotes string = "MTN"
55 | EnumSecurityTypeNosecuritytype string = "NONE"
56 | EnumSecurityTypeOvernight string = "ONITE"
57 | EnumSecurityTypeOption string = "OPT"
58 | EnumSecurityTypePrivateexportfunding string = "PEF"
59 | EnumSecurityTypePfandbriefe string = "PFAND"
60 | EnumSecurityTypePromissorynote string = "PN"
61 | EnumSecurityTypePreferredstock string = "PS"
62 | EnumSecurityTypePlazosfijos string = "PZFJ"
63 | EnumSecurityTypeRevenueanticipationnote string = "RAN"
64 | EnumSecurityTypeReplaced string = "REPLACD"
65 | EnumSecurityTypeRepurchase string = "REPO"
66 | EnumSecurityTypeRetired string = "RETIRED"
67 | EnumSecurityTypeRevenuebonds string = "REV"
68 | EnumSecurityTypeRevolverloan string = "RVLV"
69 | EnumSecurityTypeRevolvertermloan string = "RVLVTRM"
70 | EnumSecurityTypeSecuritiesloan string = "SECLOAN"
71 | EnumSecurityTypeSecuritiespledge string = "SECPLEDGE"
72 | EnumSecurityTypeSpecialassessment string = "SPCLA"
73 | EnumSecurityTypeSpecialobligation string = "SPCLO"
74 | EnumSecurityTypeSpecialtax string = "SPCLT"
75 | EnumSecurityTypeShorttermloannote string = "STN"
76 | EnumSecurityTypeStructurednotes string = "STRUCT"
77 | EnumSecurityTypeUsdsupranationalcoupons string = "SUPRA"
78 | EnumSecurityTypeSwinglinefacility string = "SWING"
79 | EnumSecurityTypeTaxanticipationnote string = "TAN"
80 | EnumSecurityTypeTaxallocation string = "TAXA"
81 | EnumSecurityTypeTobeannounced string = "TBA"
82 | EnumSecurityTypeUstreasurybill string = "TBILL"
83 | EnumSecurityTypeUstreasurybond string = "TBOND"
84 | EnumSecurityTypePrincipalstripofacallablebondornote string = "TCAL"
85 | EnumSecurityTypeTimedeposit string = "TD"
86 | EnumSecurityTypeTaxexemptcommercialpaper string = "TECP"
87 | EnumSecurityTypeTermloan string = "TERM"
88 | EnumSecurityTypeIntereststripfromanybondornote string = "TINT"
89 | EnumSecurityTypeTreasuryinflationprotectedsecurities string = "TIPS"
90 | EnumSecurityTypeUstreasurynote string = "TNOTE"
91 | EnumSecurityTypePrincipalstripfromanoncallablebondornote string = "TPRN"
92 | EnumSecurityTypeTaxrevenueanticipationnote string = "TRAN"
93 | EnumSecurityTypeUstreasurynotedeprecatedvalueusetnote string = "UST"
94 | EnumSecurityTypeUstreasurybilldeprecatedvalueusetbill string = "USTB"
95 | EnumSecurityTypeVariableratedemandnote string = "VRDN"
96 | EnumSecurityTypeWarrant string = "WAR"
97 | EnumSecurityTypeWithdrawn string = "WITHDRN"
98 | EnumSecurityTypeWildcardentry string = "WLD"
99 | EnumSecurityTypeExtendedcommnote string = "XCN"
100 | EnumSecurityTypeIndexedlinked string = "XLINKD"
101 | EnumSecurityTypeYankeecorporatebond string = "YANK"
102 | EnumSecurityTypeYankeecertificateofdeposit string = "YCD"
103 | )
104 |
--------------------------------------------------------------------------------
/tests/fix44/enum_sessionrejectreason.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumSessionRejectReason
6 | const (
7 | EnumSessionRejectReasonInvalidtagnumber string = "0"
8 | EnumSessionRejectReasonRequiredtagmissing string = "1"
9 | EnumSessionRejectReasonSendingtimeaccuracyproblem string = "10"
10 | EnumSessionRejectReasonInvalidmsgtype string = "11"
11 | EnumSessionRejectReasonXmlvalidationerror string = "12"
12 | EnumSessionRejectReasonTagappearsmorethanonce string = "13"
13 | EnumSessionRejectReasonTagspecifiedoutofrequiredorder string = "14"
14 | EnumSessionRejectReasonRepeatinggroupfieldsoutoforder string = "15"
15 | EnumSessionRejectReasonIncorrectnumingroupcountforrepeatinggroup string = "16"
16 | EnumSessionRejectReasonNondatavalueincludesfielddelimitersohcharacter string = "17"
17 | EnumSessionRejectReasonTagNotDefinedForThisMessageType string = "2"
18 | EnumSessionRejectReasonUndefinedtag string = "3"
19 | EnumSessionRejectReasonTagspecifiedwithoutavalue string = "4"
20 | EnumSessionRejectReasonValueisincorrectoutofrangeforthistag string = "5"
21 | EnumSessionRejectReasonIncorrectdataformatforvalue string = "6"
22 | EnumSessionRejectReasonDecryptionproblem string = "7"
23 | EnumSessionRejectReasonSignatureproblem string = "8"
24 | EnumSessionRejectReasonCompidproblem string = "9"
25 | EnumSessionRejectReasonOther string = "99"
26 | )
27 |
--------------------------------------------------------------------------------
/tests/fix44/enum_subscriptionrequesttype.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumSubscriptionRequestType
6 | const (
7 | EnumSubscriptionRequestTypeSnapshot string = "0"
8 | EnumSubscriptionRequestTypeSnapshotupdate string = "1"
9 | EnumSubscriptionRequestTypeUnsubscribe string = "2"
10 | )
11 |
--------------------------------------------------------------------------------
/tests/fix44/enum_symbolsfx.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumSymbolSfx
6 | const (
7 | EnumSymbolSfxEucplumpsuminterest string = "CD"
8 | EnumSymbolSfxWhenissued string = "WI"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/fix44/enum_tickdirection.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumTickDirection
6 | const (
7 | EnumTickDirectionPlus string = "0"
8 | EnumTickDirectionZeroplus string = "1"
9 | EnumTickDirectionMinus string = "2"
10 | EnumTickDirectionZerominus string = "3"
11 | )
12 |
--------------------------------------------------------------------------------
/tests/fix44/enum_timeinforce.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumTimeInForce
6 | const (
7 | EnumTimeInForceDay string = "0"
8 | EnumTimeInForceGoodtillcancel string = "1"
9 | EnumTimeInForceAttheopening string = "2"
10 | EnumTimeInForceImmediateorcancel string = "3"
11 | EnumTimeInForceFillorkill string = "4"
12 | EnumTimeInForceGoodtillcrossing string = "5"
13 | EnumTimeInForceGoodtilldate string = "6"
14 | EnumTimeInForceAttheclose string = "7"
15 | )
16 |
--------------------------------------------------------------------------------
/tests/fix44/enum_tradecondition.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | // Enum type EnumTradeCondition
6 | const (
7 | EnumTradeConditionCashmkt string = "A"
8 | EnumTradeConditionAvgpx string = "B"
9 | EnumTradeConditionCashtrade string = "C"
10 | EnumTradeConditionNextdayD string = "D"
11 | EnumTradeConditionOpening string = "E"
12 | EnumTradeConditionIntraday string = "F"
13 | EnumTradeConditionRule127 string = "G"
14 | EnumTradeConditionRule155 string = "H"
15 | EnumTradeConditionSoldlast string = "I"
16 | EnumTradeConditionNextdayJ string = "J"
17 | EnumTradeConditionOpened string = "K"
18 | EnumTradeConditionSeller string = "L"
19 | EnumTradeConditionSold string = "M"
20 | EnumTradeConditionStopped string = "N"
21 | EnumTradeConditionImbalancemorebuyers string = "P"
22 | EnumTradeConditionImbalancemoresellers string = "Q"
23 | EnumTradeConditionOpeningprice string = "R"
24 | )
25 |
--------------------------------------------------------------------------------
/tests/fix44/eventsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type EventsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewEventsGrp() *EventsGrp {
14 | return &EventsGrp{
15 | fix.NewGroup(FieldNoEvents,
16 | fix.NewKeyValue(FieldEventType, &fix.String{}),
17 | fix.NewKeyValue(FieldEventDate, &fix.String{}),
18 | fix.NewKeyValue(FieldEventPx, &fix.Float{}),
19 | fix.NewKeyValue(FieldEventText, &fix.String{}),
20 | ),
21 | }
22 | }
23 |
24 | func (group *EventsGrp) AddEntry(entry *EventsEntry) *EventsGrp {
25 | group.Group.AddEntry(entry.Items())
26 |
27 | return group
28 | }
29 |
30 | func (group *EventsGrp) Entries() []*EventsEntry {
31 | items := make([]*EventsEntry, len(group.Group.Entries()))
32 |
33 | for i, item := range group.Group.Entries() {
34 | items[i] = &EventsEntry{fix.NewComponent(item...)}
35 | }
36 |
37 | return items
38 | }
39 |
40 | type EventsEntry struct {
41 | *fix.Component
42 | }
43 |
44 | func makeEventsEntry() *EventsEntry {
45 | return &EventsEntry{fix.NewComponent(
46 | fix.NewKeyValue(FieldEventType, &fix.String{}),
47 | fix.NewKeyValue(FieldEventDate, &fix.String{}),
48 | fix.NewKeyValue(FieldEventPx, &fix.Float{}),
49 | fix.NewKeyValue(FieldEventText, &fix.String{}),
50 | )}
51 | }
52 |
53 | func NewEventsEntry() *EventsEntry {
54 | return makeEventsEntry()
55 | }
56 |
57 | func (eventsEntry *EventsEntry) EventType() string {
58 | kv := eventsEntry.Get(0)
59 | v := kv.(*fix.KeyValue).Load().Value()
60 | return v.(string)
61 | }
62 |
63 | func (eventsEntry *EventsEntry) SetEventType(eventType string) *EventsEntry {
64 | kv := eventsEntry.Get(0).(*fix.KeyValue)
65 | _ = kv.Load().Set(eventType)
66 | return eventsEntry
67 | }
68 |
69 | func (eventsEntry *EventsEntry) EventDate() string {
70 | kv := eventsEntry.Get(1)
71 | v := kv.(*fix.KeyValue).Load().Value()
72 | return v.(string)
73 | }
74 |
75 | func (eventsEntry *EventsEntry) SetEventDate(eventDate string) *EventsEntry {
76 | kv := eventsEntry.Get(1).(*fix.KeyValue)
77 | _ = kv.Load().Set(eventDate)
78 | return eventsEntry
79 | }
80 |
81 | func (eventsEntry *EventsEntry) EventPx() float64 {
82 | kv := eventsEntry.Get(2)
83 | v := kv.(*fix.KeyValue).Load().Value()
84 | return v.(float64)
85 | }
86 |
87 | func (eventsEntry *EventsEntry) SetEventPx(eventPx float64) *EventsEntry {
88 | kv := eventsEntry.Get(2).(*fix.KeyValue)
89 | _ = kv.Load().Set(eventPx)
90 | return eventsEntry
91 | }
92 |
93 | func (eventsEntry *EventsEntry) EventText() string {
94 | kv := eventsEntry.Get(3)
95 | v := kv.(*fix.KeyValue).Load().Value()
96 | return v.(string)
97 | }
98 |
99 | func (eventsEntry *EventsEntry) SetEventText(eventText string) *EventsEntry {
100 | kv := eventsEntry.Get(3).(*fix.KeyValue)
101 | _ = kv.Load().Set(eventText)
102 | return eventsEntry
103 | }
104 |
--------------------------------------------------------------------------------
/tests/fix44/heartbeat.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeHeartbeat = "0"
11 |
12 | type Heartbeat struct {
13 | *fix.Message
14 | }
15 |
16 | func makeHeartbeat() *Heartbeat {
17 | msg := &Heartbeat{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeHeartbeat).
19 | SetBody(
20 | fix.NewKeyValue(FieldTestReqID, &fix.String{}),
21 | ),
22 | }
23 |
24 | msg.SetHeader(makeHeader().AsComponent())
25 | msg.SetTrailer(makeTrailer().AsComponent())
26 |
27 | return msg
28 | }
29 |
30 | func CreateHeartbeat() *Heartbeat {
31 | msg := makeHeartbeat()
32 |
33 | return msg
34 | }
35 |
36 | func NewHeartbeat() *Heartbeat {
37 | m := makeHeartbeat()
38 | return &Heartbeat{
39 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeHeartbeat).
40 | SetBody(m.Body()...).
41 | SetHeader(m.Header().AsComponent()).
42 | SetTrailer(m.Trailer().AsComponent()),
43 | }
44 | }
45 |
46 | func (heartbeat *Heartbeat) Header() *Header {
47 | header := heartbeat.Message.Header()
48 |
49 | return &Header{header}
50 | }
51 |
52 | func (heartbeat *Heartbeat) HeaderBuilder() messages.HeaderBuilder {
53 | return heartbeat.Header()
54 | }
55 |
56 | func (heartbeat *Heartbeat) Trailer() *Trailer {
57 | trailer := heartbeat.Message.Trailer()
58 |
59 | return &Trailer{trailer}
60 | }
61 |
62 | func (heartbeat *Heartbeat) TestReqID() string {
63 | kv := heartbeat.Get(0)
64 | v := kv.(*fix.KeyValue).Load().Value()
65 | return v.(string)
66 | }
67 |
68 | func (heartbeat *Heartbeat) SetTestReqID(testReqID string) *Heartbeat {
69 | kv := heartbeat.Get(0).(*fix.KeyValue)
70 | _ = kv.Load().Set(testReqID)
71 | return heartbeat
72 | }
73 |
74 | // New is a plane message constructor
75 | func (Heartbeat) New() messages.HeartbeatBuilder {
76 | return makeHeartbeat()
77 | }
78 |
79 | // Build provides an opportunity to customize message during building outgoing message
80 | func (Heartbeat) Build() messages.HeartbeatBuilder {
81 | return makeHeartbeat()
82 | }
83 |
84 | func (heartbeat *Heartbeat) SetFieldTestReqID(testReqID string) messages.HeartbeatBuilder {
85 | return heartbeat.SetTestReqID(testReqID)
86 | }
87 |
--------------------------------------------------------------------------------
/tests/fix44/hopsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type HopsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewHopsGrp() *HopsGrp {
14 | return &HopsGrp{
15 | fix.NewGroup(FieldNoHops,
16 | fix.NewKeyValue(FieldHopCompID, &fix.String{}),
17 | fix.NewKeyValue(FieldHopSendingTime, &fix.String{}),
18 | fix.NewKeyValue(FieldHopRefID, &fix.Int{}),
19 | ),
20 | }
21 | }
22 |
23 | func (group *HopsGrp) AddEntry(entry *HopsEntry) *HopsGrp {
24 | group.Group.AddEntry(entry.Items())
25 |
26 | return group
27 | }
28 |
29 | func (group *HopsGrp) Entries() []*HopsEntry {
30 | items := make([]*HopsEntry, len(group.Group.Entries()))
31 |
32 | for i, item := range group.Group.Entries() {
33 | items[i] = &HopsEntry{fix.NewComponent(item...)}
34 | }
35 |
36 | return items
37 | }
38 |
39 | type HopsEntry struct {
40 | *fix.Component
41 | }
42 |
43 | func makeHopsEntry() *HopsEntry {
44 | return &HopsEntry{fix.NewComponent(
45 | fix.NewKeyValue(FieldHopCompID, &fix.String{}),
46 | fix.NewKeyValue(FieldHopSendingTime, &fix.String{}),
47 | fix.NewKeyValue(FieldHopRefID, &fix.Int{}),
48 | )}
49 | }
50 |
51 | func NewHopsEntry() *HopsEntry {
52 | return makeHopsEntry()
53 | }
54 |
55 | func (hopsEntry *HopsEntry) HopCompID() string {
56 | kv := hopsEntry.Get(0)
57 | v := kv.(*fix.KeyValue).Load().Value()
58 | return v.(string)
59 | }
60 |
61 | func (hopsEntry *HopsEntry) SetHopCompID(hopCompID string) *HopsEntry {
62 | kv := hopsEntry.Get(0).(*fix.KeyValue)
63 | _ = kv.Load().Set(hopCompID)
64 | return hopsEntry
65 | }
66 |
67 | func (hopsEntry *HopsEntry) HopSendingTime() string {
68 | kv := hopsEntry.Get(1)
69 | v := kv.(*fix.KeyValue).Load().Value()
70 | return v.(string)
71 | }
72 |
73 | func (hopsEntry *HopsEntry) SetHopSendingTime(hopSendingTime string) *HopsEntry {
74 | kv := hopsEntry.Get(1).(*fix.KeyValue)
75 | _ = kv.Load().Set(hopSendingTime)
76 | return hopsEntry
77 | }
78 |
79 | func (hopsEntry *HopsEntry) HopRefID() int {
80 | kv := hopsEntry.Get(2)
81 | v := kv.(*fix.KeyValue).Load().Value()
82 | return v.(int)
83 | }
84 |
85 | func (hopsEntry *HopsEntry) SetHopRefID(hopRefID int) *HopsEntry {
86 | kv := hopsEntry.Get(2).(*fix.KeyValue)
87 | _ = kv.Load().Set(hopRefID)
88 | return hopsEntry
89 | }
90 |
--------------------------------------------------------------------------------
/tests/fix44/legsecurityaltidgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type LegSecurityAltIDGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewLegSecurityAltIDGrp() *LegSecurityAltIDGrp {
14 | return &LegSecurityAltIDGrp{
15 | fix.NewGroup(FieldNoLegSecurityAltID,
16 | fix.NewKeyValue(FieldLegSecurityAltID, &fix.String{}),
17 | fix.NewKeyValue(FieldLegSecurityAltIDSource, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *LegSecurityAltIDGrp) AddEntry(entry *LegSecurityAltIDEntry) *LegSecurityAltIDGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *LegSecurityAltIDGrp) Entries() []*LegSecurityAltIDEntry {
29 | items := make([]*LegSecurityAltIDEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &LegSecurityAltIDEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type LegSecurityAltIDEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeLegSecurityAltIDEntry() *LegSecurityAltIDEntry {
43 | return &LegSecurityAltIDEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldLegSecurityAltID, &fix.String{}),
45 | fix.NewKeyValue(FieldLegSecurityAltIDSource, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewLegSecurityAltIDEntry() *LegSecurityAltIDEntry {
50 | return makeLegSecurityAltIDEntry()
51 | }
52 |
53 | func (legSecurityAltIDEntry *LegSecurityAltIDEntry) LegSecurityAltID() string {
54 | kv := legSecurityAltIDEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (legSecurityAltIDEntry *LegSecurityAltIDEntry) SetLegSecurityAltID(legSecurityAltID string) *LegSecurityAltIDEntry {
60 | kv := legSecurityAltIDEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(legSecurityAltID)
62 | return legSecurityAltIDEntry
63 | }
64 |
65 | func (legSecurityAltIDEntry *LegSecurityAltIDEntry) LegSecurityAltIDSource() string {
66 | kv := legSecurityAltIDEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (legSecurityAltIDEntry *LegSecurityAltIDEntry) SetLegSecurityAltIDSource(legSecurityAltIDSource string) *LegSecurityAltIDEntry {
72 | kv := legSecurityAltIDEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(legSecurityAltIDSource)
74 | return legSecurityAltIDEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/legsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type LegsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewLegsGrp() *LegsGrp {
14 | return &LegsGrp{
15 | fix.NewGroup(FieldNoLegs,
16 | makeInstrumentLeg().Component,
17 | ),
18 | }
19 | }
20 |
21 | func (group *LegsGrp) AddEntry(entry *LegsEntry) *LegsGrp {
22 | group.Group.AddEntry(entry.Items())
23 |
24 | return group
25 | }
26 |
27 | func (group *LegsGrp) Entries() []*LegsEntry {
28 | items := make([]*LegsEntry, len(group.Group.Entries()))
29 |
30 | for i, item := range group.Group.Entries() {
31 | items[i] = &LegsEntry{fix.NewComponent(item...)}
32 | }
33 |
34 | return items
35 | }
36 |
37 | type LegsEntry struct {
38 | *fix.Component
39 | }
40 |
41 | func makeLegsEntry() *LegsEntry {
42 | return &LegsEntry{fix.NewComponent(
43 | makeInstrumentLeg().Component,
44 | )}
45 | }
46 |
47 | func NewLegsEntry() *LegsEntry {
48 | return makeLegsEntry()
49 | }
50 |
51 | func (legsEntry *LegsEntry) InstrumentLeg() *InstrumentLeg {
52 | component := legsEntry.Get(0).(*fix.Component)
53 |
54 | return &InstrumentLeg{component}
55 | }
56 |
57 | func (legsEntry *LegsEntry) SetInstrumentLeg(instrumentLeg *InstrumentLeg) *LegsEntry {
58 | legsEntry.Set(0, instrumentLeg.Component)
59 |
60 | return legsEntry
61 | }
62 |
--------------------------------------------------------------------------------
/tests/fix44/logon.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeLogon = "A"
11 |
12 | type Logon struct {
13 | *fix.Message
14 | }
15 |
16 | func makeLogon() *Logon {
17 | msg := &Logon{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeLogon).
19 | SetBody(
20 | fix.NewKeyValue(FieldEncryptMethod, &fix.String{}),
21 | fix.NewKeyValue(FieldHeartBtInt, &fix.Int{}),
22 | fix.NewKeyValue(FieldRawDataLength, &fix.Int{}),
23 | fix.NewKeyValue(FieldRawData, &fix.String{}),
24 | fix.NewKeyValue(FieldResetSeqNumFlag, &fix.Bool{}),
25 | fix.NewKeyValue(FieldNextExpectedMsgSeqNum, &fix.Int{}),
26 | fix.NewKeyValue(FieldMaxMessageSize, &fix.Int{}),
27 | NewMsgTypesGrp().Group,
28 | fix.NewKeyValue(FieldTestMessageIndicator, &fix.Bool{}),
29 | fix.NewKeyValue(FieldUsername, &fix.String{}),
30 | fix.NewKeyValue(FieldPassword, &fix.String{}),
31 | ),
32 | }
33 |
34 | msg.SetHeader(makeHeader().AsComponent())
35 | msg.SetTrailer(makeTrailer().AsComponent())
36 |
37 | return msg
38 | }
39 |
40 | func CreateLogon(encryptMethod string, heartBtInt int) *Logon {
41 | msg := makeLogon().
42 | SetEncryptMethod(encryptMethod).
43 | SetHeartBtInt(heartBtInt)
44 |
45 | return msg
46 | }
47 |
48 | func NewLogon() *Logon {
49 | m := makeLogon()
50 | return &Logon{
51 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeLogon).
52 | SetBody(m.Body()...).
53 | SetHeader(m.Header().AsComponent()).
54 | SetTrailer(m.Trailer().AsComponent()),
55 | }
56 | }
57 |
58 | func (logon *Logon) Header() *Header {
59 | header := logon.Message.Header()
60 |
61 | return &Header{header}
62 | }
63 |
64 | func (logon *Logon) HeaderBuilder() messages.HeaderBuilder {
65 | return logon.Header()
66 | }
67 |
68 | func (logon *Logon) Trailer() *Trailer {
69 | trailer := logon.Message.Trailer()
70 |
71 | return &Trailer{trailer}
72 | }
73 |
74 | func (logon *Logon) EncryptMethod() string {
75 | kv := logon.Get(0)
76 | v := kv.(*fix.KeyValue).Load().Value()
77 | return v.(string)
78 | }
79 |
80 | func (logon *Logon) SetEncryptMethod(encryptMethod string) *Logon {
81 | kv := logon.Get(0).(*fix.KeyValue)
82 | _ = kv.Load().Set(encryptMethod)
83 | return logon
84 | }
85 |
86 | func (logon *Logon) HeartBtInt() int {
87 | kv := logon.Get(1)
88 | v := kv.(*fix.KeyValue).Load().Value()
89 | return v.(int)
90 | }
91 |
92 | func (logon *Logon) SetHeartBtInt(heartBtInt int) *Logon {
93 | kv := logon.Get(1).(*fix.KeyValue)
94 | _ = kv.Load().Set(heartBtInt)
95 | return logon
96 | }
97 |
98 | func (logon *Logon) RawDataLength() int {
99 | kv := logon.Get(2)
100 | v := kv.(*fix.KeyValue).Load().Value()
101 | return v.(int)
102 | }
103 |
104 | func (logon *Logon) SetRawDataLength(rawDataLength int) *Logon {
105 | kv := logon.Get(2).(*fix.KeyValue)
106 | _ = kv.Load().Set(rawDataLength)
107 | return logon
108 | }
109 |
110 | func (logon *Logon) RawData() string {
111 | kv := logon.Get(3)
112 | v := kv.(*fix.KeyValue).Load().Value()
113 | return v.(string)
114 | }
115 |
116 | func (logon *Logon) SetRawData(rawData string) *Logon {
117 | kv := logon.Get(3).(*fix.KeyValue)
118 | _ = kv.Load().Set(rawData)
119 | return logon
120 | }
121 |
122 | func (logon *Logon) ResetSeqNumFlag() bool {
123 | kv := logon.Get(4)
124 | v := kv.(*fix.KeyValue).Load().Value()
125 | return v.(bool)
126 | }
127 |
128 | func (logon *Logon) SetResetSeqNumFlag(resetSeqNumFlag bool) *Logon {
129 | kv := logon.Get(4).(*fix.KeyValue)
130 | _ = kv.Load().Set(resetSeqNumFlag)
131 | return logon
132 | }
133 |
134 | func (logon *Logon) NextExpectedMsgSeqNum() int {
135 | kv := logon.Get(5)
136 | v := kv.(*fix.KeyValue).Load().Value()
137 | return v.(int)
138 | }
139 |
140 | func (logon *Logon) SetNextExpectedMsgSeqNum(nextExpectedMsgSeqNum int) *Logon {
141 | kv := logon.Get(5).(*fix.KeyValue)
142 | _ = kv.Load().Set(nextExpectedMsgSeqNum)
143 | return logon
144 | }
145 |
146 | func (logon *Logon) MaxMessageSize() int {
147 | kv := logon.Get(6)
148 | v := kv.(*fix.KeyValue).Load().Value()
149 | return v.(int)
150 | }
151 |
152 | func (logon *Logon) SetMaxMessageSize(maxMessageSize int) *Logon {
153 | kv := logon.Get(6).(*fix.KeyValue)
154 | _ = kv.Load().Set(maxMessageSize)
155 | return logon
156 | }
157 |
158 | func (logon *Logon) MsgTypesGrp() *MsgTypesGrp {
159 | group := logon.Get(7).(*fix.Group)
160 |
161 | return &MsgTypesGrp{group}
162 | }
163 |
164 | func (logon *Logon) SetMsgTypesGrp(noMsgTypes *MsgTypesGrp) *Logon {
165 | logon.Set(7, noMsgTypes.Group)
166 |
167 | return logon
168 | }
169 |
170 | func (logon *Logon) TestMessageIndicator() bool {
171 | kv := logon.Get(8)
172 | v := kv.(*fix.KeyValue).Load().Value()
173 | return v.(bool)
174 | }
175 |
176 | func (logon *Logon) SetTestMessageIndicator(testMessageIndicator bool) *Logon {
177 | kv := logon.Get(8).(*fix.KeyValue)
178 | _ = kv.Load().Set(testMessageIndicator)
179 | return logon
180 | }
181 |
182 | func (logon *Logon) Username() string {
183 | kv := logon.Get(9)
184 | v := kv.(*fix.KeyValue).Load().Value()
185 | return v.(string)
186 | }
187 |
188 | func (logon *Logon) SetUsername(username string) *Logon {
189 | kv := logon.Get(9).(*fix.KeyValue)
190 | _ = kv.Load().Set(username)
191 | return logon
192 | }
193 |
194 | func (logon *Logon) Password() string {
195 | kv := logon.Get(10)
196 | v := kv.(*fix.KeyValue).Load().Value()
197 | return v.(string)
198 | }
199 |
200 | func (logon *Logon) SetPassword(password string) *Logon {
201 | kv := logon.Get(10).(*fix.KeyValue)
202 | _ = kv.Load().Set(password)
203 | return logon
204 | }
205 |
206 | // New is a plane message constructor
207 | func (Logon) New() messages.LogonBuilder {
208 | return makeLogon()
209 | }
210 |
211 | // Build provides an opportunity to customize message during building outgoing message
212 | func (Logon) Build() messages.LogonBuilder {
213 | return makeLogon()
214 | }
215 |
216 | func (logon *Logon) SetFieldHeartBtInt(heartBtInt int) messages.LogonBuilder {
217 | return logon.SetHeartBtInt(heartBtInt)
218 | }
219 |
220 | func (logon *Logon) SetFieldEncryptMethod(encryptMethod string) messages.LogonBuilder {
221 | return logon.SetEncryptMethod(encryptMethod)
222 | }
223 |
224 | func (logon *Logon) SetFieldPassword(password string) messages.LogonBuilder {
225 | return logon.SetPassword(password)
226 | }
227 |
228 | func (logon *Logon) SetFieldUsername(username string) messages.LogonBuilder {
229 | return logon.SetUsername(username)
230 | }
231 |
232 | func (logon *Logon) SetFieldResetSeqNumFlag(resetSeqNumFlag bool) messages.LogonBuilder {
233 | return logon.SetResetSeqNumFlag(resetSeqNumFlag)
234 | }
235 |
--------------------------------------------------------------------------------
/tests/fix44/logout.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeLogout = "5"
11 |
12 | type Logout struct {
13 | *fix.Message
14 | }
15 |
16 | func makeLogout() *Logout {
17 | msg := &Logout{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeLogout).
19 | SetBody(
20 | fix.NewKeyValue(FieldText, &fix.String{}),
21 | fix.NewKeyValue(FieldEncodedTextLen, &fix.Int{}),
22 | fix.NewKeyValue(FieldEncodedText, &fix.String{}),
23 | ),
24 | }
25 |
26 | msg.SetHeader(makeHeader().AsComponent())
27 | msg.SetTrailer(makeTrailer().AsComponent())
28 |
29 | return msg
30 | }
31 |
32 | func CreateLogout() *Logout {
33 | msg := makeLogout()
34 |
35 | return msg
36 | }
37 |
38 | func NewLogout() *Logout {
39 | m := makeLogout()
40 | return &Logout{
41 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeLogout).
42 | SetBody(m.Body()...).
43 | SetHeader(m.Header().AsComponent()).
44 | SetTrailer(m.Trailer().AsComponent()),
45 | }
46 | }
47 |
48 | func (logout *Logout) Header() *Header {
49 | header := logout.Message.Header()
50 |
51 | return &Header{header}
52 | }
53 |
54 | func (logout *Logout) HeaderBuilder() messages.HeaderBuilder {
55 | return logout.Header()
56 | }
57 |
58 | func (logout *Logout) Trailer() *Trailer {
59 | trailer := logout.Message.Trailer()
60 |
61 | return &Trailer{trailer}
62 | }
63 |
64 | func (logout *Logout) Text() string {
65 | kv := logout.Get(0)
66 | v := kv.(*fix.KeyValue).Load().Value()
67 | return v.(string)
68 | }
69 |
70 | func (logout *Logout) SetText(text string) *Logout {
71 | kv := logout.Get(0).(*fix.KeyValue)
72 | _ = kv.Load().Set(text)
73 | return logout
74 | }
75 |
76 | func (logout *Logout) EncodedTextLen() int {
77 | kv := logout.Get(1)
78 | v := kv.(*fix.KeyValue).Load().Value()
79 | return v.(int)
80 | }
81 |
82 | func (logout *Logout) SetEncodedTextLen(encodedTextLen int) *Logout {
83 | kv := logout.Get(1).(*fix.KeyValue)
84 | _ = kv.Load().Set(encodedTextLen)
85 | return logout
86 | }
87 |
88 | func (logout *Logout) EncodedText() string {
89 | kv := logout.Get(2)
90 | v := kv.(*fix.KeyValue).Load().Value()
91 | return v.(string)
92 | }
93 |
94 | func (logout *Logout) SetEncodedText(encodedText string) *Logout {
95 | kv := logout.Get(2).(*fix.KeyValue)
96 | _ = kv.Load().Set(encodedText)
97 | return logout
98 | }
99 |
100 | // New is a plane message constructor
101 | func (Logout) New() messages.LogoutBuilder {
102 | return makeLogout()
103 | }
104 |
105 | // Build provides an opportunity to customize message during building outgoing message
106 | func (Logout) Build() messages.LogoutBuilder {
107 | return makeLogout()
108 | }
109 |
--------------------------------------------------------------------------------
/tests/fix44/marketdataincrementalrefresh.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeMarketDataIncrementalRefresh = "X"
11 |
12 | type MarketDataIncrementalRefresh struct {
13 | *fix.Message
14 | }
15 |
16 | func makeMarketDataIncrementalRefresh() *MarketDataIncrementalRefresh {
17 | msg := &MarketDataIncrementalRefresh{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataIncrementalRefresh).
19 | SetBody(
20 | fix.NewKeyValue(FieldMDReqID, &fix.String{}),
21 | NewMDEntriesGrp().Group,
22 | fix.NewKeyValue(FieldApplQueueDepth, &fix.Int{}),
23 | fix.NewKeyValue(FieldApplQueueResolution, &fix.String{}),
24 | ),
25 | }
26 |
27 | msg.SetHeader(makeHeader().AsComponent())
28 | msg.SetTrailer(makeTrailer().AsComponent())
29 |
30 | return msg
31 | }
32 |
33 | func CreateMarketDataIncrementalRefresh(noMDEntries *MDEntriesGrp) *MarketDataIncrementalRefresh {
34 | msg := makeMarketDataIncrementalRefresh().
35 | SetMDEntriesGrp(noMDEntries)
36 |
37 | return msg
38 | }
39 |
40 | func NewMarketDataIncrementalRefresh() *MarketDataIncrementalRefresh {
41 | m := makeMarketDataIncrementalRefresh()
42 | return &MarketDataIncrementalRefresh{
43 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataIncrementalRefresh).
44 | SetBody(m.Body()...).
45 | SetHeader(m.Header().AsComponent()).
46 | SetTrailer(m.Trailer().AsComponent()),
47 | }
48 | }
49 |
50 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) Header() *Header {
51 | header := marketDataIncrementalRefresh.Message.Header()
52 |
53 | return &Header{header}
54 | }
55 |
56 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) HeaderBuilder() messages.HeaderBuilder {
57 | return marketDataIncrementalRefresh.Header()
58 | }
59 |
60 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) Trailer() *Trailer {
61 | trailer := marketDataIncrementalRefresh.Message.Trailer()
62 |
63 | return &Trailer{trailer}
64 | }
65 |
66 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) MDReqID() string {
67 | kv := marketDataIncrementalRefresh.Get(0)
68 | v := kv.(*fix.KeyValue).Load().Value()
69 | return v.(string)
70 | }
71 |
72 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) SetMDReqID(mDReqID string) *MarketDataIncrementalRefresh {
73 | kv := marketDataIncrementalRefresh.Get(0).(*fix.KeyValue)
74 | _ = kv.Load().Set(mDReqID)
75 | return marketDataIncrementalRefresh
76 | }
77 |
78 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) MDEntriesGrp() *MDEntriesGrp {
79 | group := marketDataIncrementalRefresh.Get(1).(*fix.Group)
80 |
81 | return &MDEntriesGrp{group}
82 | }
83 |
84 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) SetMDEntriesGrp(noMDEntries *MDEntriesGrp) *MarketDataIncrementalRefresh {
85 | marketDataIncrementalRefresh.Set(1, noMDEntries.Group)
86 |
87 | return marketDataIncrementalRefresh
88 | }
89 |
90 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) ApplQueueDepth() int {
91 | kv := marketDataIncrementalRefresh.Get(2)
92 | v := kv.(*fix.KeyValue).Load().Value()
93 | return v.(int)
94 | }
95 |
96 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) SetApplQueueDepth(applQueueDepth int) *MarketDataIncrementalRefresh {
97 | kv := marketDataIncrementalRefresh.Get(2).(*fix.KeyValue)
98 | _ = kv.Load().Set(applQueueDepth)
99 | return marketDataIncrementalRefresh
100 | }
101 |
102 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) ApplQueueResolution() string {
103 | kv := marketDataIncrementalRefresh.Get(3)
104 | v := kv.(*fix.KeyValue).Load().Value()
105 | return v.(string)
106 | }
107 |
108 | func (marketDataIncrementalRefresh *MarketDataIncrementalRefresh) SetApplQueueResolution(applQueueResolution string) *MarketDataIncrementalRefresh {
109 | kv := marketDataIncrementalRefresh.Get(3).(*fix.KeyValue)
110 | _ = kv.Load().Set(applQueueResolution)
111 | return marketDataIncrementalRefresh
112 | }
113 |
--------------------------------------------------------------------------------
/tests/fix44/marketdatarequest.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeMarketDataRequest = "V"
11 |
12 | type MarketDataRequest struct {
13 | *fix.Message
14 | }
15 |
16 | func makeMarketDataRequest() *MarketDataRequest {
17 | msg := &MarketDataRequest{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataRequest).
19 | SetBody(
20 | fix.NewKeyValue(FieldMDReqID, &fix.String{}),
21 | fix.NewKeyValue(FieldSubscriptionRequestType, &fix.String{}),
22 | fix.NewKeyValue(FieldMarketDepth, &fix.Int{}),
23 | fix.NewKeyValue(FieldMDUpdateType, &fix.String{}),
24 | fix.NewKeyValue(FieldAggregatedBook, &fix.Bool{}),
25 | fix.NewKeyValue(FieldOpenCloseSettlFlag, &fix.String{}),
26 | fix.NewKeyValue(FieldScope, &fix.String{}),
27 | fix.NewKeyValue(FieldMDImplicitDelete, &fix.Bool{}),
28 | NewMDEntryTypesGrp().Group,
29 | NewRelatedSymGrp().Group,
30 | ),
31 | }
32 |
33 | msg.SetHeader(makeHeader().AsComponent())
34 | msg.SetTrailer(makeTrailer().AsComponent())
35 |
36 | return msg
37 | }
38 |
39 | func CreateMarketDataRequest(mDReqID string, subscriptionRequestType string, marketDepth int, noMDEntryTypes *MDEntryTypesGrp, noRelatedSym *RelatedSymGrp) *MarketDataRequest {
40 | msg := makeMarketDataRequest().
41 | SetMDReqID(mDReqID).
42 | SetSubscriptionRequestType(subscriptionRequestType).
43 | SetMarketDepth(marketDepth).
44 | SetMDEntryTypesGrp(noMDEntryTypes).
45 | SetRelatedSymGrp(noRelatedSym)
46 |
47 | return msg
48 | }
49 |
50 | func NewMarketDataRequest() *MarketDataRequest {
51 | m := makeMarketDataRequest()
52 | return &MarketDataRequest{
53 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataRequest).
54 | SetBody(m.Body()...).
55 | SetHeader(m.Header().AsComponent()).
56 | SetTrailer(m.Trailer().AsComponent()),
57 | }
58 | }
59 |
60 | func (marketDataRequest *MarketDataRequest) Header() *Header {
61 | header := marketDataRequest.Message.Header()
62 |
63 | return &Header{header}
64 | }
65 |
66 | func (marketDataRequest *MarketDataRequest) HeaderBuilder() messages.HeaderBuilder {
67 | return marketDataRequest.Header()
68 | }
69 |
70 | func (marketDataRequest *MarketDataRequest) Trailer() *Trailer {
71 | trailer := marketDataRequest.Message.Trailer()
72 |
73 | return &Trailer{trailer}
74 | }
75 |
76 | func (marketDataRequest *MarketDataRequest) MDReqID() string {
77 | kv := marketDataRequest.Get(0)
78 | v := kv.(*fix.KeyValue).Load().Value()
79 | return v.(string)
80 | }
81 |
82 | func (marketDataRequest *MarketDataRequest) SetMDReqID(mDReqID string) *MarketDataRequest {
83 | kv := marketDataRequest.Get(0).(*fix.KeyValue)
84 | _ = kv.Load().Set(mDReqID)
85 | return marketDataRequest
86 | }
87 |
88 | func (marketDataRequest *MarketDataRequest) SubscriptionRequestType() string {
89 | kv := marketDataRequest.Get(1)
90 | v := kv.(*fix.KeyValue).Load().Value()
91 | return v.(string)
92 | }
93 |
94 | func (marketDataRequest *MarketDataRequest) SetSubscriptionRequestType(subscriptionRequestType string) *MarketDataRequest {
95 | kv := marketDataRequest.Get(1).(*fix.KeyValue)
96 | _ = kv.Load().Set(subscriptionRequestType)
97 | return marketDataRequest
98 | }
99 |
100 | func (marketDataRequest *MarketDataRequest) MarketDepth() int {
101 | kv := marketDataRequest.Get(2)
102 | v := kv.(*fix.KeyValue).Load().Value()
103 | return v.(int)
104 | }
105 |
106 | func (marketDataRequest *MarketDataRequest) SetMarketDepth(marketDepth int) *MarketDataRequest {
107 | kv := marketDataRequest.Get(2).(*fix.KeyValue)
108 | _ = kv.Load().Set(marketDepth)
109 | return marketDataRequest
110 | }
111 |
112 | func (marketDataRequest *MarketDataRequest) MDUpdateType() string {
113 | kv := marketDataRequest.Get(3)
114 | v := kv.(*fix.KeyValue).Load().Value()
115 | return v.(string)
116 | }
117 |
118 | func (marketDataRequest *MarketDataRequest) SetMDUpdateType(mDUpdateType string) *MarketDataRequest {
119 | kv := marketDataRequest.Get(3).(*fix.KeyValue)
120 | _ = kv.Load().Set(mDUpdateType)
121 | return marketDataRequest
122 | }
123 |
124 | func (marketDataRequest *MarketDataRequest) AggregatedBook() bool {
125 | kv := marketDataRequest.Get(4)
126 | v := kv.(*fix.KeyValue).Load().Value()
127 | return v.(bool)
128 | }
129 |
130 | func (marketDataRequest *MarketDataRequest) SetAggregatedBook(aggregatedBook bool) *MarketDataRequest {
131 | kv := marketDataRequest.Get(4).(*fix.KeyValue)
132 | _ = kv.Load().Set(aggregatedBook)
133 | return marketDataRequest
134 | }
135 |
136 | func (marketDataRequest *MarketDataRequest) OpenCloseSettlFlag() string {
137 | kv := marketDataRequest.Get(5)
138 | v := kv.(*fix.KeyValue).Load().Value()
139 | return v.(string)
140 | }
141 |
142 | func (marketDataRequest *MarketDataRequest) SetOpenCloseSettlFlag(openCloseSettlFlag string) *MarketDataRequest {
143 | kv := marketDataRequest.Get(5).(*fix.KeyValue)
144 | _ = kv.Load().Set(openCloseSettlFlag)
145 | return marketDataRequest
146 | }
147 |
148 | func (marketDataRequest *MarketDataRequest) Scope() string {
149 | kv := marketDataRequest.Get(6)
150 | v := kv.(*fix.KeyValue).Load().Value()
151 | return v.(string)
152 | }
153 |
154 | func (marketDataRequest *MarketDataRequest) SetScope(scope string) *MarketDataRequest {
155 | kv := marketDataRequest.Get(6).(*fix.KeyValue)
156 | _ = kv.Load().Set(scope)
157 | return marketDataRequest
158 | }
159 |
160 | func (marketDataRequest *MarketDataRequest) MDImplicitDelete() bool {
161 | kv := marketDataRequest.Get(7)
162 | v := kv.(*fix.KeyValue).Load().Value()
163 | return v.(bool)
164 | }
165 |
166 | func (marketDataRequest *MarketDataRequest) SetMDImplicitDelete(mDImplicitDelete bool) *MarketDataRequest {
167 | kv := marketDataRequest.Get(7).(*fix.KeyValue)
168 | _ = kv.Load().Set(mDImplicitDelete)
169 | return marketDataRequest
170 | }
171 |
172 | func (marketDataRequest *MarketDataRequest) MDEntryTypesGrp() *MDEntryTypesGrp {
173 | group := marketDataRequest.Get(8).(*fix.Group)
174 |
175 | return &MDEntryTypesGrp{group}
176 | }
177 |
178 | func (marketDataRequest *MarketDataRequest) SetMDEntryTypesGrp(noMDEntryTypes *MDEntryTypesGrp) *MarketDataRequest {
179 | marketDataRequest.Set(8, noMDEntryTypes.Group)
180 |
181 | return marketDataRequest
182 | }
183 |
184 | func (marketDataRequest *MarketDataRequest) RelatedSymGrp() *RelatedSymGrp {
185 | group := marketDataRequest.Get(9).(*fix.Group)
186 |
187 | return &RelatedSymGrp{group}
188 | }
189 |
190 | func (marketDataRequest *MarketDataRequest) SetRelatedSymGrp(noRelatedSym *RelatedSymGrp) *MarketDataRequest {
191 | marketDataRequest.Set(9, noRelatedSym.Group)
192 |
193 | return marketDataRequest
194 | }
195 |
196 | // New is a plane message constructor
197 | func (MarketDataRequest) New() messages.MarketDataRequestBuilder {
198 | return makeMarketDataRequest()
199 | }
200 |
201 | // Build provides an opportunity to customize message during building outgoing message
202 | func (MarketDataRequest) Build() messages.MarketDataRequestBuilder {
203 | return makeMarketDataRequest()
204 | }
205 |
--------------------------------------------------------------------------------
/tests/fix44/marketdatarequestreject.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeMarketDataRequestReject = "Y"
11 |
12 | type MarketDataRequestReject struct {
13 | *fix.Message
14 | }
15 |
16 | func makeMarketDataRequestReject() *MarketDataRequestReject {
17 | msg := &MarketDataRequestReject{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataRequestReject).
19 | SetBody(
20 | fix.NewKeyValue(FieldMDReqID, &fix.String{}),
21 | fix.NewKeyValue(FieldMDReqRejReason, &fix.String{}),
22 | NewAltMDSourceGrp().Group,
23 | fix.NewKeyValue(FieldText, &fix.String{}),
24 | fix.NewKeyValue(FieldEncodedTextLen, &fix.Int{}),
25 | fix.NewKeyValue(FieldEncodedText, &fix.String{}),
26 | ),
27 | }
28 |
29 | msg.SetHeader(makeHeader().AsComponent())
30 | msg.SetTrailer(makeTrailer().AsComponent())
31 |
32 | return msg
33 | }
34 |
35 | func CreateMarketDataRequestReject(mDReqID string) *MarketDataRequestReject {
36 | msg := makeMarketDataRequestReject().
37 | SetMDReqID(mDReqID)
38 |
39 | return msg
40 | }
41 |
42 | func NewMarketDataRequestReject() *MarketDataRequestReject {
43 | m := makeMarketDataRequestReject()
44 | return &MarketDataRequestReject{
45 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeMarketDataRequestReject).
46 | SetBody(m.Body()...).
47 | SetHeader(m.Header().AsComponent()).
48 | SetTrailer(m.Trailer().AsComponent()),
49 | }
50 | }
51 |
52 | func (marketDataRequestReject *MarketDataRequestReject) Header() *Header {
53 | header := marketDataRequestReject.Message.Header()
54 |
55 | return &Header{header}
56 | }
57 |
58 | func (marketDataRequestReject *MarketDataRequestReject) HeaderBuilder() messages.HeaderBuilder {
59 | return marketDataRequestReject.Header()
60 | }
61 |
62 | func (marketDataRequestReject *MarketDataRequestReject) Trailer() *Trailer {
63 | trailer := marketDataRequestReject.Message.Trailer()
64 |
65 | return &Trailer{trailer}
66 | }
67 |
68 | func (marketDataRequestReject *MarketDataRequestReject) MDReqID() string {
69 | kv := marketDataRequestReject.Get(0)
70 | v := kv.(*fix.KeyValue).Load().Value()
71 | return v.(string)
72 | }
73 |
74 | func (marketDataRequestReject *MarketDataRequestReject) SetMDReqID(mDReqID string) *MarketDataRequestReject {
75 | kv := marketDataRequestReject.Get(0).(*fix.KeyValue)
76 | _ = kv.Load().Set(mDReqID)
77 | return marketDataRequestReject
78 | }
79 |
80 | func (marketDataRequestReject *MarketDataRequestReject) MDReqRejReason() string {
81 | kv := marketDataRequestReject.Get(1)
82 | v := kv.(*fix.KeyValue).Load().Value()
83 | return v.(string)
84 | }
85 |
86 | func (marketDataRequestReject *MarketDataRequestReject) SetMDReqRejReason(mDReqRejReason string) *MarketDataRequestReject {
87 | kv := marketDataRequestReject.Get(1).(*fix.KeyValue)
88 | _ = kv.Load().Set(mDReqRejReason)
89 | return marketDataRequestReject
90 | }
91 |
92 | func (marketDataRequestReject *MarketDataRequestReject) AltMDSourceGrp() *AltMDSourceGrp {
93 | group := marketDataRequestReject.Get(2).(*fix.Group)
94 |
95 | return &AltMDSourceGrp{group}
96 | }
97 |
98 | func (marketDataRequestReject *MarketDataRequestReject) SetAltMDSourceGrp(noAltMDSource *AltMDSourceGrp) *MarketDataRequestReject {
99 | marketDataRequestReject.Set(2, noAltMDSource.Group)
100 |
101 | return marketDataRequestReject
102 | }
103 |
104 | func (marketDataRequestReject *MarketDataRequestReject) Text() string {
105 | kv := marketDataRequestReject.Get(3)
106 | v := kv.(*fix.KeyValue).Load().Value()
107 | return v.(string)
108 | }
109 |
110 | func (marketDataRequestReject *MarketDataRequestReject) SetText(text string) *MarketDataRequestReject {
111 | kv := marketDataRequestReject.Get(3).(*fix.KeyValue)
112 | _ = kv.Load().Set(text)
113 | return marketDataRequestReject
114 | }
115 |
116 | func (marketDataRequestReject *MarketDataRequestReject) EncodedTextLen() int {
117 | kv := marketDataRequestReject.Get(4)
118 | v := kv.(*fix.KeyValue).Load().Value()
119 | return v.(int)
120 | }
121 |
122 | func (marketDataRequestReject *MarketDataRequestReject) SetEncodedTextLen(encodedTextLen int) *MarketDataRequestReject {
123 | kv := marketDataRequestReject.Get(4).(*fix.KeyValue)
124 | _ = kv.Load().Set(encodedTextLen)
125 | return marketDataRequestReject
126 | }
127 |
128 | func (marketDataRequestReject *MarketDataRequestReject) EncodedText() string {
129 | kv := marketDataRequestReject.Get(5)
130 | v := kv.(*fix.KeyValue).Load().Value()
131 | return v.(string)
132 | }
133 |
134 | func (marketDataRequestReject *MarketDataRequestReject) SetEncodedText(encodedText string) *MarketDataRequestReject {
135 | kv := marketDataRequestReject.Get(5).(*fix.KeyValue)
136 | _ = kv.Load().Set(encodedText)
137 | return marketDataRequestReject
138 | }
139 |
--------------------------------------------------------------------------------
/tests/fix44/mdentrytypesgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type MDEntryTypesGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewMDEntryTypesGrp() *MDEntryTypesGrp {
14 | return &MDEntryTypesGrp{
15 | fix.NewGroup(FieldNoMDEntryTypes,
16 | fix.NewKeyValue(FieldMDEntryType, &fix.String{}),
17 | ),
18 | }
19 | }
20 |
21 | func (group *MDEntryTypesGrp) AddEntry(entry *MDEntryTypesEntry) *MDEntryTypesGrp {
22 | group.Group.AddEntry(entry.Items())
23 |
24 | return group
25 | }
26 |
27 | func (group *MDEntryTypesGrp) Entries() []*MDEntryTypesEntry {
28 | items := make([]*MDEntryTypesEntry, len(group.Group.Entries()))
29 |
30 | for i, item := range group.Group.Entries() {
31 | items[i] = &MDEntryTypesEntry{fix.NewComponent(item...)}
32 | }
33 |
34 | return items
35 | }
36 |
37 | type MDEntryTypesEntry struct {
38 | *fix.Component
39 | }
40 |
41 | func makeMDEntryTypesEntry() *MDEntryTypesEntry {
42 | return &MDEntryTypesEntry{fix.NewComponent(
43 | fix.NewKeyValue(FieldMDEntryType, &fix.String{}),
44 | )}
45 | }
46 |
47 | func NewMDEntryTypesEntry() *MDEntryTypesEntry {
48 | return makeMDEntryTypesEntry()
49 | }
50 |
51 | func (mDEntryTypesEntry *MDEntryTypesEntry) MDEntryType() string {
52 | kv := mDEntryTypesEntry.Get(0)
53 | v := kv.(*fix.KeyValue).Load().Value()
54 | return v.(string)
55 | }
56 |
57 | func (mDEntryTypesEntry *MDEntryTypesEntry) SetMDEntryType(mDEntryType string) *MDEntryTypesEntry {
58 | kv := mDEntryTypesEntry.Get(0).(*fix.KeyValue)
59 | _ = kv.Load().Set(mDEntryType)
60 | return mDEntryTypesEntry
61 | }
62 |
--------------------------------------------------------------------------------
/tests/fix44/msgtypesgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type MsgTypesGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewMsgTypesGrp() *MsgTypesGrp {
14 | return &MsgTypesGrp{
15 | fix.NewGroup(FieldNoMsgTypes,
16 | fix.NewKeyValue(FieldRefMsgType, &fix.String{}),
17 | fix.NewKeyValue(FieldMsgDirection, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *MsgTypesGrp) AddEntry(entry *MsgTypesEntry) *MsgTypesGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *MsgTypesGrp) Entries() []*MsgTypesEntry {
29 | items := make([]*MsgTypesEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &MsgTypesEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type MsgTypesEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeMsgTypesEntry() *MsgTypesEntry {
43 | return &MsgTypesEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldRefMsgType, &fix.String{}),
45 | fix.NewKeyValue(FieldMsgDirection, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewMsgTypesEntry() *MsgTypesEntry {
50 | return makeMsgTypesEntry()
51 | }
52 |
53 | func (msgTypesEntry *MsgTypesEntry) RefMsgType() string {
54 | kv := msgTypesEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (msgTypesEntry *MsgTypesEntry) SetRefMsgType(refMsgType string) *MsgTypesEntry {
60 | kv := msgTypesEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(refMsgType)
62 | return msgTypesEntry
63 | }
64 |
65 | func (msgTypesEntry *MsgTypesEntry) MsgDirection() string {
66 | kv := msgTypesEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (msgTypesEntry *MsgTypesEntry) SetMsgDirection(msgDirection string) *MsgTypesEntry {
72 | kv := msgTypesEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(msgDirection)
74 | return msgTypesEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/reject.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeReject = "3"
11 |
12 | type Reject struct {
13 | *fix.Message
14 | }
15 |
16 | func makeReject() *Reject {
17 | msg := &Reject{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeReject).
19 | SetBody(
20 | fix.NewKeyValue(FieldRefSeqNum, &fix.Int{}),
21 | fix.NewKeyValue(FieldRefTagID, &fix.Int{}),
22 | fix.NewKeyValue(FieldRefMsgType, &fix.String{}),
23 | fix.NewKeyValue(FieldSessionRejectReason, &fix.String{}),
24 | fix.NewKeyValue(FieldText, &fix.String{}),
25 | fix.NewKeyValue(FieldEncodedTextLen, &fix.Int{}),
26 | fix.NewKeyValue(FieldEncodedText, &fix.String{}),
27 | ),
28 | }
29 |
30 | msg.SetHeader(makeHeader().AsComponent())
31 | msg.SetTrailer(makeTrailer().AsComponent())
32 |
33 | return msg
34 | }
35 |
36 | func CreateReject(refSeqNum int) *Reject {
37 | msg := makeReject().
38 | SetRefSeqNum(refSeqNum)
39 |
40 | return msg
41 | }
42 |
43 | func NewReject() *Reject {
44 | m := makeReject()
45 | return &Reject{
46 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeReject).
47 | SetBody(m.Body()...).
48 | SetHeader(m.Header().AsComponent()).
49 | SetTrailer(m.Trailer().AsComponent()),
50 | }
51 | }
52 |
53 | func (reject *Reject) Header() *Header {
54 | header := reject.Message.Header()
55 |
56 | return &Header{header}
57 | }
58 |
59 | func (reject *Reject) HeaderBuilder() messages.HeaderBuilder {
60 | return reject.Header()
61 | }
62 |
63 | func (reject *Reject) Trailer() *Trailer {
64 | trailer := reject.Message.Trailer()
65 |
66 | return &Trailer{trailer}
67 | }
68 |
69 | func (reject *Reject) RefSeqNum() int {
70 | kv := reject.Get(0)
71 | v := kv.(*fix.KeyValue).Load().Value()
72 | return v.(int)
73 | }
74 |
75 | func (reject *Reject) SetRefSeqNum(refSeqNum int) *Reject {
76 | kv := reject.Get(0).(*fix.KeyValue)
77 | _ = kv.Load().Set(refSeqNum)
78 | return reject
79 | }
80 |
81 | func (reject *Reject) RefTagID() int {
82 | kv := reject.Get(1)
83 | v := kv.(*fix.KeyValue).Load().Value()
84 | return v.(int)
85 | }
86 |
87 | func (reject *Reject) SetRefTagID(refTagID int) *Reject {
88 | kv := reject.Get(1).(*fix.KeyValue)
89 | _ = kv.Load().Set(refTagID)
90 | return reject
91 | }
92 |
93 | func (reject *Reject) RefMsgType() string {
94 | kv := reject.Get(2)
95 | v := kv.(*fix.KeyValue).Load().Value()
96 | return v.(string)
97 | }
98 |
99 | func (reject *Reject) SetRefMsgType(refMsgType string) *Reject {
100 | kv := reject.Get(2).(*fix.KeyValue)
101 | _ = kv.Load().Set(refMsgType)
102 | return reject
103 | }
104 |
105 | func (reject *Reject) SessionRejectReason() string {
106 | kv := reject.Get(3)
107 | v := kv.(*fix.KeyValue).Load().Value()
108 | return v.(string)
109 | }
110 |
111 | func (reject *Reject) SetSessionRejectReason(sessionRejectReason string) *Reject {
112 | kv := reject.Get(3).(*fix.KeyValue)
113 | _ = kv.Load().Set(sessionRejectReason)
114 | return reject
115 | }
116 |
117 | func (reject *Reject) Text() string {
118 | kv := reject.Get(4)
119 | v := kv.(*fix.KeyValue).Load().Value()
120 | return v.(string)
121 | }
122 |
123 | func (reject *Reject) SetText(text string) *Reject {
124 | kv := reject.Get(4).(*fix.KeyValue)
125 | _ = kv.Load().Set(text)
126 | return reject
127 | }
128 |
129 | func (reject *Reject) EncodedTextLen() int {
130 | kv := reject.Get(5)
131 | v := kv.(*fix.KeyValue).Load().Value()
132 | return v.(int)
133 | }
134 |
135 | func (reject *Reject) SetEncodedTextLen(encodedTextLen int) *Reject {
136 | kv := reject.Get(5).(*fix.KeyValue)
137 | _ = kv.Load().Set(encodedTextLen)
138 | return reject
139 | }
140 |
141 | func (reject *Reject) EncodedText() string {
142 | kv := reject.Get(6)
143 | v := kv.(*fix.KeyValue).Load().Value()
144 | return v.(string)
145 | }
146 |
147 | func (reject *Reject) SetEncodedText(encodedText string) *Reject {
148 | kv := reject.Get(6).(*fix.KeyValue)
149 | _ = kv.Load().Set(encodedText)
150 | return reject
151 | }
152 |
153 | // New is a plane message constructor
154 | func (Reject) New() messages.RejectBuilder {
155 | return makeReject()
156 | }
157 |
158 | // Build provides an opportunity to customize message during building outgoing message
159 | func (Reject) Build() messages.RejectBuilder {
160 | return makeReject()
161 | }
162 |
163 | func (reject *Reject) SetFieldSessionRejectReason(sessionRejectReason string) messages.RejectBuilder {
164 | return reject.SetSessionRejectReason(sessionRejectReason)
165 | }
166 |
167 | func (reject *Reject) SetFieldRefSeqNum(refSeqNum int) messages.RejectBuilder {
168 | return reject.SetRefSeqNum(refSeqNum)
169 | }
170 |
171 | func (reject *Reject) SetFieldRefTagID(refTagID int) messages.RejectBuilder {
172 | return reject.SetRefTagID(refTagID)
173 | }
174 |
--------------------------------------------------------------------------------
/tests/fix44/relatedsymgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type RelatedSymGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewRelatedSymGrp() *RelatedSymGrp {
14 | return &RelatedSymGrp{
15 | fix.NewGroup(FieldNoRelatedSym,
16 | makeInstrument().Component,
17 | NewUnderlyingsGrp().Group,
18 | NewLegsGrp().Group,
19 | NewTradingSessionsGrp().Group,
20 | fix.NewKeyValue(FieldApplQueueAction, &fix.String{}),
21 | fix.NewKeyValue(FieldApplQueueMax, &fix.Int{}),
22 | ),
23 | }
24 | }
25 |
26 | func (group *RelatedSymGrp) AddEntry(entry *RelatedSymEntry) *RelatedSymGrp {
27 | group.Group.AddEntry(entry.Items())
28 |
29 | return group
30 | }
31 |
32 | func (group *RelatedSymGrp) Entries() []*RelatedSymEntry {
33 | items := make([]*RelatedSymEntry, len(group.Group.Entries()))
34 |
35 | for i, item := range group.Group.Entries() {
36 | items[i] = &RelatedSymEntry{fix.NewComponent(item...)}
37 | }
38 |
39 | return items
40 | }
41 |
42 | type RelatedSymEntry struct {
43 | *fix.Component
44 | }
45 |
46 | func makeRelatedSymEntry() *RelatedSymEntry {
47 | return &RelatedSymEntry{fix.NewComponent(
48 | makeInstrument().Component,
49 | NewUnderlyingsGrp().Group,
50 | NewLegsGrp().Group,
51 | NewTradingSessionsGrp().Group,
52 | fix.NewKeyValue(FieldApplQueueAction, &fix.String{}),
53 | fix.NewKeyValue(FieldApplQueueMax, &fix.Int{}),
54 | )}
55 | }
56 |
57 | func NewRelatedSymEntry() *RelatedSymEntry {
58 | return makeRelatedSymEntry()
59 | }
60 |
61 | func (relatedSymEntry *RelatedSymEntry) Instrument() *Instrument {
62 | component := relatedSymEntry.Get(0).(*fix.Component)
63 |
64 | return &Instrument{component}
65 | }
66 |
67 | func (relatedSymEntry *RelatedSymEntry) SetInstrument(instrument *Instrument) *RelatedSymEntry {
68 | relatedSymEntry.Set(0, instrument.Component)
69 |
70 | return relatedSymEntry
71 | }
72 |
73 | func (relatedSymEntry *RelatedSymEntry) UnderlyingsGrp() *UnderlyingsGrp {
74 | group := relatedSymEntry.Get(1).(*fix.Group)
75 |
76 | return &UnderlyingsGrp{group}
77 | }
78 |
79 | func (relatedSymEntry *RelatedSymEntry) SetUnderlyingsGrp(noUnderlyings *UnderlyingsGrp) *RelatedSymEntry {
80 | relatedSymEntry.Set(1, noUnderlyings.Group)
81 |
82 | return relatedSymEntry
83 | }
84 |
85 | func (relatedSymEntry *RelatedSymEntry) LegsGrp() *LegsGrp {
86 | group := relatedSymEntry.Get(2).(*fix.Group)
87 |
88 | return &LegsGrp{group}
89 | }
90 |
91 | func (relatedSymEntry *RelatedSymEntry) SetLegsGrp(noLegs *LegsGrp) *RelatedSymEntry {
92 | relatedSymEntry.Set(2, noLegs.Group)
93 |
94 | return relatedSymEntry
95 | }
96 |
97 | func (relatedSymEntry *RelatedSymEntry) TradingSessionsGrp() *TradingSessionsGrp {
98 | group := relatedSymEntry.Get(3).(*fix.Group)
99 |
100 | return &TradingSessionsGrp{group}
101 | }
102 |
103 | func (relatedSymEntry *RelatedSymEntry) SetTradingSessionsGrp(noTradingSessions *TradingSessionsGrp) *RelatedSymEntry {
104 | relatedSymEntry.Set(3, noTradingSessions.Group)
105 |
106 | return relatedSymEntry
107 | }
108 |
109 | func (relatedSymEntry *RelatedSymEntry) ApplQueueAction() string {
110 | kv := relatedSymEntry.Get(4)
111 | v := kv.(*fix.KeyValue).Load().Value()
112 | return v.(string)
113 | }
114 |
115 | func (relatedSymEntry *RelatedSymEntry) SetApplQueueAction(applQueueAction string) *RelatedSymEntry {
116 | kv := relatedSymEntry.Get(4).(*fix.KeyValue)
117 | _ = kv.Load().Set(applQueueAction)
118 | return relatedSymEntry
119 | }
120 |
121 | func (relatedSymEntry *RelatedSymEntry) ApplQueueMax() int {
122 | kv := relatedSymEntry.Get(5)
123 | v := kv.(*fix.KeyValue).Load().Value()
124 | return v.(int)
125 | }
126 |
127 | func (relatedSymEntry *RelatedSymEntry) SetApplQueueMax(applQueueMax int) *RelatedSymEntry {
128 | kv := relatedSymEntry.Get(5).(*fix.KeyValue)
129 | _ = kv.Load().Set(applQueueMax)
130 | return relatedSymEntry
131 | }
132 |
--------------------------------------------------------------------------------
/tests/fix44/resendrequest.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeResendRequest = "2"
11 |
12 | type ResendRequest struct {
13 | *fix.Message
14 | }
15 |
16 | func makeResendRequest() *ResendRequest {
17 | msg := &ResendRequest{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeResendRequest).
19 | SetBody(
20 | fix.NewKeyValue(FieldBeginSeqNo, &fix.Int{}),
21 | fix.NewKeyValue(FieldEndSeqNo, &fix.Int{}),
22 | ),
23 | }
24 |
25 | msg.SetHeader(makeHeader().AsComponent())
26 | msg.SetTrailer(makeTrailer().AsComponent())
27 |
28 | return msg
29 | }
30 |
31 | func CreateResendRequest(beginSeqNo int, endSeqNo int) *ResendRequest {
32 | msg := makeResendRequest().
33 | SetBeginSeqNo(beginSeqNo).
34 | SetEndSeqNo(endSeqNo)
35 |
36 | return msg
37 | }
38 |
39 | func NewResendRequest() *ResendRequest {
40 | m := makeResendRequest()
41 | return &ResendRequest{
42 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeResendRequest).
43 | SetBody(m.Body()...).
44 | SetHeader(m.Header().AsComponent()).
45 | SetTrailer(m.Trailer().AsComponent()),
46 | }
47 | }
48 |
49 | func (resendRequest *ResendRequest) Header() *Header {
50 | header := resendRequest.Message.Header()
51 |
52 | return &Header{header}
53 | }
54 |
55 | func (resendRequest *ResendRequest) HeaderBuilder() messages.HeaderBuilder {
56 | return resendRequest.Header()
57 | }
58 |
59 | func (resendRequest *ResendRequest) Trailer() *Trailer {
60 | trailer := resendRequest.Message.Trailer()
61 |
62 | return &Trailer{trailer}
63 | }
64 |
65 | func (resendRequest *ResendRequest) BeginSeqNo() int {
66 | kv := resendRequest.Get(0)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(int)
69 | }
70 |
71 | func (resendRequest *ResendRequest) SetBeginSeqNo(beginSeqNo int) *ResendRequest {
72 | kv := resendRequest.Get(0).(*fix.KeyValue)
73 | _ = kv.Load().Set(beginSeqNo)
74 | return resendRequest
75 | }
76 |
77 | func (resendRequest *ResendRequest) EndSeqNo() int {
78 | kv := resendRequest.Get(1)
79 | v := kv.(*fix.KeyValue).Load().Value()
80 | return v.(int)
81 | }
82 |
83 | func (resendRequest *ResendRequest) SetEndSeqNo(endSeqNo int) *ResendRequest {
84 | kv := resendRequest.Get(1).(*fix.KeyValue)
85 | _ = kv.Load().Set(endSeqNo)
86 | return resendRequest
87 | }
88 |
89 | // New is a plane message constructor
90 | func (ResendRequest) New() messages.ResendRequestBuilder {
91 | return makeResendRequest()
92 | }
93 |
94 | // Build provides an opportunity to customize message during building outgoing message
95 | func (ResendRequest) Build() messages.ResendRequestBuilder {
96 | return makeResendRequest()
97 | }
98 |
99 | func (resendRequest *ResendRequest) SetFieldBeginSeqNo(beginSeqNo int) messages.ResendRequestBuilder {
100 | return resendRequest.SetBeginSeqNo(beginSeqNo)
101 | }
102 |
103 | func (resendRequest *ResendRequest) SetFieldEndSeqNo(endSeqNo int) messages.ResendRequestBuilder {
104 | return resendRequest.SetEndSeqNo(endSeqNo)
105 | }
106 |
--------------------------------------------------------------------------------
/tests/fix44/securityaltidgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type SecurityAltIDGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewSecurityAltIDGrp() *SecurityAltIDGrp {
14 | return &SecurityAltIDGrp{
15 | fix.NewGroup(FieldNoSecurityAltID,
16 | fix.NewKeyValue(FieldSecurityAltID, &fix.String{}),
17 | fix.NewKeyValue(FieldSecurityAltIDSource, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *SecurityAltIDGrp) AddEntry(entry *SecurityAltIDEntry) *SecurityAltIDGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *SecurityAltIDGrp) Entries() []*SecurityAltIDEntry {
29 | items := make([]*SecurityAltIDEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &SecurityAltIDEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type SecurityAltIDEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeSecurityAltIDEntry() *SecurityAltIDEntry {
43 | return &SecurityAltIDEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldSecurityAltID, &fix.String{}),
45 | fix.NewKeyValue(FieldSecurityAltIDSource, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewSecurityAltIDEntry() *SecurityAltIDEntry {
50 | return makeSecurityAltIDEntry()
51 | }
52 |
53 | func (securityAltIDEntry *SecurityAltIDEntry) SecurityAltID() string {
54 | kv := securityAltIDEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (securityAltIDEntry *SecurityAltIDEntry) SetSecurityAltID(securityAltID string) *SecurityAltIDEntry {
60 | kv := securityAltIDEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(securityAltID)
62 | return securityAltIDEntry
63 | }
64 |
65 | func (securityAltIDEntry *SecurityAltIDEntry) SecurityAltIDSource() string {
66 | kv := securityAltIDEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (securityAltIDEntry *SecurityAltIDEntry) SetSecurityAltIDSource(securityAltIDSource string) *SecurityAltIDEntry {
72 | kv := securityAltIDEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(securityAltIDSource)
74 | return securityAltIDEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/sequencereset.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeSequenceReset = "4"
11 |
12 | type SequenceReset struct {
13 | *fix.Message
14 | }
15 |
16 | func makeSequenceReset() *SequenceReset {
17 | msg := &SequenceReset{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeSequenceReset).
19 | SetBody(
20 | fix.NewKeyValue(FieldGapFillFlag, &fix.Bool{}),
21 | fix.NewKeyValue(FieldNewSeqNo, &fix.Int{}),
22 | ),
23 | }
24 |
25 | msg.SetHeader(makeHeader().AsComponent())
26 | msg.SetTrailer(makeTrailer().AsComponent())
27 |
28 | return msg
29 | }
30 |
31 | func CreateSequenceReset(newSeqNo int) *SequenceReset {
32 | msg := makeSequenceReset().
33 | SetNewSeqNo(newSeqNo)
34 |
35 | return msg
36 | }
37 |
38 | func NewSequenceReset() *SequenceReset {
39 | m := makeSequenceReset()
40 | return &SequenceReset{
41 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeSequenceReset).
42 | SetBody(m.Body()...).
43 | SetHeader(m.Header().AsComponent()).
44 | SetTrailer(m.Trailer().AsComponent()),
45 | }
46 | }
47 |
48 | func (sequenceReset *SequenceReset) Header() *Header {
49 | header := sequenceReset.Message.Header()
50 |
51 | return &Header{header}
52 | }
53 |
54 | func (sequenceReset *SequenceReset) HeaderBuilder() messages.HeaderBuilder {
55 | return sequenceReset.Header()
56 | }
57 |
58 | func (sequenceReset *SequenceReset) Trailer() *Trailer {
59 | trailer := sequenceReset.Message.Trailer()
60 |
61 | return &Trailer{trailer}
62 | }
63 |
64 | func (sequenceReset *SequenceReset) GapFillFlag() bool {
65 | kv := sequenceReset.Get(0)
66 | v := kv.(*fix.KeyValue).Load().Value()
67 | return v.(bool)
68 | }
69 |
70 | func (sequenceReset *SequenceReset) SetGapFillFlag(gapFillFlag bool) *SequenceReset {
71 | kv := sequenceReset.Get(0).(*fix.KeyValue)
72 | _ = kv.Load().Set(gapFillFlag)
73 | return sequenceReset
74 | }
75 |
76 | func (sequenceReset *SequenceReset) NewSeqNo() int {
77 | kv := sequenceReset.Get(1)
78 | v := kv.(*fix.KeyValue).Load().Value()
79 | return v.(int)
80 | }
81 |
82 | func (sequenceReset *SequenceReset) SetNewSeqNo(newSeqNo int) *SequenceReset {
83 | kv := sequenceReset.Get(1).(*fix.KeyValue)
84 | _ = kv.Load().Set(newSeqNo)
85 | return sequenceReset
86 | }
87 |
88 | // New is a plane message constructor
89 | func (SequenceReset) New() messages.SequenceResetBuilder {
90 | return makeSequenceReset()
91 | }
92 |
93 | // Build provides an opportunity to customize message during building outgoing message
94 | func (SequenceReset) Build() messages.SequenceResetBuilder {
95 | return makeSequenceReset()
96 | }
97 |
98 | func (sequenceReset *SequenceReset) SetFieldNewSeqNo(newSeqNo int) messages.SequenceResetBuilder {
99 | return sequenceReset.SetNewSeqNo(newSeqNo)
100 | }
101 |
102 | func (sequenceReset *SequenceReset) SetFieldGapFillFlag(gapFillFlag bool) messages.SequenceResetBuilder {
103 | return sequenceReset.SetGapFillFlag(gapFillFlag)
104 | }
105 |
--------------------------------------------------------------------------------
/tests/fix44/testrequest.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | const MsgTypeTestRequest = "1"
11 |
12 | type TestRequest struct {
13 | *fix.Message
14 | }
15 |
16 | func makeTestRequest() *TestRequest {
17 | msg := &TestRequest{
18 | Message: fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeTestRequest).
19 | SetBody(
20 | fix.NewKeyValue(FieldTestReqID, &fix.String{}),
21 | ),
22 | }
23 |
24 | msg.SetHeader(makeHeader().AsComponent())
25 | msg.SetTrailer(makeTrailer().AsComponent())
26 |
27 | return msg
28 | }
29 |
30 | func CreateTestRequest(testReqID string) *TestRequest {
31 | msg := makeTestRequest().
32 | SetTestReqID(testReqID)
33 |
34 | return msg
35 | }
36 |
37 | func NewTestRequest() *TestRequest {
38 | m := makeTestRequest()
39 | return &TestRequest{
40 | fix.NewMessage(FieldBeginString, FieldBodyLength, FieldCheckSum, FieldMsgType, beginString, MsgTypeTestRequest).
41 | SetBody(m.Body()...).
42 | SetHeader(m.Header().AsComponent()).
43 | SetTrailer(m.Trailer().AsComponent()),
44 | }
45 | }
46 |
47 | func (testRequest *TestRequest) Header() *Header {
48 | header := testRequest.Message.Header()
49 |
50 | return &Header{header}
51 | }
52 |
53 | func (testRequest *TestRequest) HeaderBuilder() messages.HeaderBuilder {
54 | return testRequest.Header()
55 | }
56 |
57 | func (testRequest *TestRequest) Trailer() *Trailer {
58 | trailer := testRequest.Message.Trailer()
59 |
60 | return &Trailer{trailer}
61 | }
62 |
63 | func (testRequest *TestRequest) TestReqID() string {
64 | kv := testRequest.Get(0)
65 | v := kv.(*fix.KeyValue).Load().Value()
66 | return v.(string)
67 | }
68 |
69 | func (testRequest *TestRequest) SetTestReqID(testReqID string) *TestRequest {
70 | kv := testRequest.Get(0).(*fix.KeyValue)
71 | _ = kv.Load().Set(testReqID)
72 | return testRequest
73 | }
74 |
75 | // New is a plane message constructor
76 | func (TestRequest) New() messages.TestRequestBuilder {
77 | return makeTestRequest()
78 | }
79 |
80 | // Build provides an opportunity to customize message during building outgoing message
81 | func (TestRequest) Build() messages.TestRequestBuilder {
82 | return makeTestRequest()
83 | }
84 |
85 | func (testRequest *TestRequest) SetFieldTestReqID(testReqID string) messages.TestRequestBuilder {
86 | return testRequest.SetTestReqID(testReqID)
87 | }
88 |
--------------------------------------------------------------------------------
/tests/fix44/tradingsessionsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type TradingSessionsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewTradingSessionsGrp() *TradingSessionsGrp {
14 | return &TradingSessionsGrp{
15 | fix.NewGroup(FieldNoTradingSessions,
16 | fix.NewKeyValue(FieldTradingSessionID, &fix.String{}),
17 | fix.NewKeyValue(FieldTradingSessionSubID, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *TradingSessionsGrp) AddEntry(entry *TradingSessionsEntry) *TradingSessionsGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *TradingSessionsGrp) Entries() []*TradingSessionsEntry {
29 | items := make([]*TradingSessionsEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &TradingSessionsEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type TradingSessionsEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeTradingSessionsEntry() *TradingSessionsEntry {
43 | return &TradingSessionsEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldTradingSessionID, &fix.String{}),
45 | fix.NewKeyValue(FieldTradingSessionSubID, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewTradingSessionsEntry() *TradingSessionsEntry {
50 | return makeTradingSessionsEntry()
51 | }
52 |
53 | func (tradingSessionsEntry *TradingSessionsEntry) TradingSessionID() string {
54 | kv := tradingSessionsEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (tradingSessionsEntry *TradingSessionsEntry) SetTradingSessionID(tradingSessionID string) *TradingSessionsEntry {
60 | kv := tradingSessionsEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(tradingSessionID)
62 | return tradingSessionsEntry
63 | }
64 |
65 | func (tradingSessionsEntry *TradingSessionsEntry) TradingSessionSubID() string {
66 | kv := tradingSessionsEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (tradingSessionsEntry *TradingSessionsEntry) SetTradingSessionSubID(tradingSessionSubID string) *TradingSessionsEntry {
72 | kv := tradingSessionsEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(tradingSessionSubID)
74 | return tradingSessionsEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/trailer.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | "github.com/b2broker/simplefix-go/session/messages"
8 | )
9 |
10 | type Trailer struct {
11 | *fix.Component
12 | }
13 |
14 | func makeTrailer() *Trailer {
15 | return &Trailer{fix.NewComponent(
16 | fix.NewKeyValue(FieldSignatureLength, &fix.Int{}),
17 | fix.NewKeyValue(FieldSignature, &fix.String{}),
18 | )}
19 | }
20 |
21 | func NewTrailer() *Trailer {
22 | return makeTrailer()
23 | }
24 |
25 | func (trailer *Trailer) SignatureLength() int {
26 | kv := trailer.Get(0)
27 | v := kv.(*fix.KeyValue).Load().Value()
28 | return v.(int)
29 | }
30 |
31 | func (trailer *Trailer) SetSignatureLength(signatureLength int) *Trailer {
32 | kv := trailer.Get(0).(*fix.KeyValue)
33 | _ = kv.Load().Set(signatureLength)
34 | return trailer
35 | }
36 |
37 | func (trailer *Trailer) Signature() string {
38 | kv := trailer.Get(1)
39 | v := kv.(*fix.KeyValue).Load().Value()
40 | return v.(string)
41 | }
42 |
43 | func (trailer *Trailer) SetSignature(signature string) *Trailer {
44 | kv := trailer.Get(1).(*fix.KeyValue)
45 | _ = kv.Load().Set(signature)
46 | return trailer
47 | }
48 |
49 | func (Trailer) New() messages.TrailerBuilder {
50 | return makeTrailer()
51 | }
52 |
--------------------------------------------------------------------------------
/tests/fix44/underlyingsecurityaltidgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type UnderlyingSecurityAltIDGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewUnderlyingSecurityAltIDGrp() *UnderlyingSecurityAltIDGrp {
14 | return &UnderlyingSecurityAltIDGrp{
15 | fix.NewGroup(FieldNoUnderlyingSecurityAltID,
16 | fix.NewKeyValue(FieldUnderlyingSecurityAltID, &fix.String{}),
17 | fix.NewKeyValue(FieldUnderlyingSecurityAltIDSource, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *UnderlyingSecurityAltIDGrp) AddEntry(entry *UnderlyingSecurityAltIDEntry) *UnderlyingSecurityAltIDGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *UnderlyingSecurityAltIDGrp) Entries() []*UnderlyingSecurityAltIDEntry {
29 | items := make([]*UnderlyingSecurityAltIDEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &UnderlyingSecurityAltIDEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type UnderlyingSecurityAltIDEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeUnderlyingSecurityAltIDEntry() *UnderlyingSecurityAltIDEntry {
43 | return &UnderlyingSecurityAltIDEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldUnderlyingSecurityAltID, &fix.String{}),
45 | fix.NewKeyValue(FieldUnderlyingSecurityAltIDSource, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewUnderlyingSecurityAltIDEntry() *UnderlyingSecurityAltIDEntry {
50 | return makeUnderlyingSecurityAltIDEntry()
51 | }
52 |
53 | func (underlyingSecurityAltIDEntry *UnderlyingSecurityAltIDEntry) UnderlyingSecurityAltID() string {
54 | kv := underlyingSecurityAltIDEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (underlyingSecurityAltIDEntry *UnderlyingSecurityAltIDEntry) SetUnderlyingSecurityAltID(underlyingSecurityAltID string) *UnderlyingSecurityAltIDEntry {
60 | kv := underlyingSecurityAltIDEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(underlyingSecurityAltID)
62 | return underlyingSecurityAltIDEntry
63 | }
64 |
65 | func (underlyingSecurityAltIDEntry *UnderlyingSecurityAltIDEntry) UnderlyingSecurityAltIDSource() string {
66 | kv := underlyingSecurityAltIDEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (underlyingSecurityAltIDEntry *UnderlyingSecurityAltIDEntry) SetUnderlyingSecurityAltIDSource(underlyingSecurityAltIDSource string) *UnderlyingSecurityAltIDEntry {
72 | kv := underlyingSecurityAltIDEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(underlyingSecurityAltIDSource)
74 | return underlyingSecurityAltIDEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/underlyingsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type UnderlyingsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewUnderlyingsGrp() *UnderlyingsGrp {
14 | return &UnderlyingsGrp{
15 | fix.NewGroup(FieldNoUnderlyings,
16 | makeUnderlyingInstrument().Component,
17 | ),
18 | }
19 | }
20 |
21 | func (group *UnderlyingsGrp) AddEntry(entry *UnderlyingsEntry) *UnderlyingsGrp {
22 | group.Group.AddEntry(entry.Items())
23 |
24 | return group
25 | }
26 |
27 | func (group *UnderlyingsGrp) Entries() []*UnderlyingsEntry {
28 | items := make([]*UnderlyingsEntry, len(group.Group.Entries()))
29 |
30 | for i, item := range group.Group.Entries() {
31 | items[i] = &UnderlyingsEntry{fix.NewComponent(item...)}
32 | }
33 |
34 | return items
35 | }
36 |
37 | type UnderlyingsEntry struct {
38 | *fix.Component
39 | }
40 |
41 | func makeUnderlyingsEntry() *UnderlyingsEntry {
42 | return &UnderlyingsEntry{fix.NewComponent(
43 | makeUnderlyingInstrument().Component,
44 | )}
45 | }
46 |
47 | func NewUnderlyingsEntry() *UnderlyingsEntry {
48 | return makeUnderlyingsEntry()
49 | }
50 |
51 | func (underlyingsEntry *UnderlyingsEntry) UnderlyingInstrument() *UnderlyingInstrument {
52 | component := underlyingsEntry.Get(0).(*fix.Component)
53 |
54 | return &UnderlyingInstrument{component}
55 | }
56 |
57 | func (underlyingsEntry *UnderlyingsEntry) SetUnderlyingInstrument(underlyingInstrument *UnderlyingInstrument) *UnderlyingsEntry {
58 | underlyingsEntry.Set(0, underlyingInstrument.Component)
59 |
60 | return underlyingsEntry
61 | }
62 |
--------------------------------------------------------------------------------
/tests/fix44/underlyingstipsgrp.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type UnderlyingStipsGrp struct {
10 | *fix.Group
11 | }
12 |
13 | func NewUnderlyingStipsGrp() *UnderlyingStipsGrp {
14 | return &UnderlyingStipsGrp{
15 | fix.NewGroup(FieldNoUnderlyingStips,
16 | fix.NewKeyValue(FieldUnderlyingStipType, &fix.String{}),
17 | fix.NewKeyValue(FieldUnderlyingStipValue, &fix.String{}),
18 | ),
19 | }
20 | }
21 |
22 | func (group *UnderlyingStipsGrp) AddEntry(entry *UnderlyingStipsEntry) *UnderlyingStipsGrp {
23 | group.Group.AddEntry(entry.Items())
24 |
25 | return group
26 | }
27 |
28 | func (group *UnderlyingStipsGrp) Entries() []*UnderlyingStipsEntry {
29 | items := make([]*UnderlyingStipsEntry, len(group.Group.Entries()))
30 |
31 | for i, item := range group.Group.Entries() {
32 | items[i] = &UnderlyingStipsEntry{fix.NewComponent(item...)}
33 | }
34 |
35 | return items
36 | }
37 |
38 | type UnderlyingStipsEntry struct {
39 | *fix.Component
40 | }
41 |
42 | func makeUnderlyingStipsEntry() *UnderlyingStipsEntry {
43 | return &UnderlyingStipsEntry{fix.NewComponent(
44 | fix.NewKeyValue(FieldUnderlyingStipType, &fix.String{}),
45 | fix.NewKeyValue(FieldUnderlyingStipValue, &fix.String{}),
46 | )}
47 | }
48 |
49 | func NewUnderlyingStipsEntry() *UnderlyingStipsEntry {
50 | return makeUnderlyingStipsEntry()
51 | }
52 |
53 | func (underlyingStipsEntry *UnderlyingStipsEntry) UnderlyingStipType() string {
54 | kv := underlyingStipsEntry.Get(0)
55 | v := kv.(*fix.KeyValue).Load().Value()
56 | return v.(string)
57 | }
58 |
59 | func (underlyingStipsEntry *UnderlyingStipsEntry) SetUnderlyingStipType(underlyingStipType string) *UnderlyingStipsEntry {
60 | kv := underlyingStipsEntry.Get(0).(*fix.KeyValue)
61 | _ = kv.Load().Set(underlyingStipType)
62 | return underlyingStipsEntry
63 | }
64 |
65 | func (underlyingStipsEntry *UnderlyingStipsEntry) UnderlyingStipValue() string {
66 | kv := underlyingStipsEntry.Get(1)
67 | v := kv.(*fix.KeyValue).Load().Value()
68 | return v.(string)
69 | }
70 |
71 | func (underlyingStipsEntry *UnderlyingStipsEntry) SetUnderlyingStipValue(underlyingStipValue string) *UnderlyingStipsEntry {
72 | kv := underlyingStipsEntry.Get(1).(*fix.KeyValue)
73 | _ = kv.Load().Set(underlyingStipValue)
74 | return underlyingStipsEntry
75 | }
76 |
--------------------------------------------------------------------------------
/tests/fix44/underlyingstipulations.go:
--------------------------------------------------------------------------------
1 | // Code generated by fixgen. DO NOT EDIT.
2 |
3 | package fix44
4 |
5 | import (
6 | "github.com/b2broker/simplefix-go/fix"
7 | )
8 |
9 | type UnderlyingStipulations struct {
10 | *fix.Component
11 | }
12 |
13 | func makeUnderlyingStipulations() *UnderlyingStipulations {
14 | return &UnderlyingStipulations{fix.NewComponent(
15 | NewUnderlyingStipsGrp().Group,
16 | )}
17 | }
18 |
19 | func NewUnderlyingStipulations() *UnderlyingStipulations {
20 | return makeUnderlyingStipulations()
21 | }
22 |
23 | func (underlyingStipulations *UnderlyingStipulations) UnderlyingStipsGrp() *UnderlyingStipsGrp {
24 | group := underlyingStipulations.Get(0).(*fix.Group)
25 |
26 | return &UnderlyingStipsGrp{group}
27 | }
28 |
29 | func (underlyingStipulations *UnderlyingStipulations) SetUnderlyingStipsGrp(noUnderlyingStips *UnderlyingStipsGrp) *UnderlyingStipulations {
30 | underlyingStipulations.Set(0, noUnderlyingStips.Group)
31 |
32 | return underlyingStipulations
33 | }
34 |
--------------------------------------------------------------------------------
/tests/initiator.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "errors"
7 | "fmt"
8 | "github.com/b2broker/simplefix-go/storages/memory"
9 | "net"
10 | "testing"
11 | "time"
12 |
13 | simplefixgo "github.com/b2broker/simplefix-go"
14 | "github.com/b2broker/simplefix-go/fix"
15 | "github.com/b2broker/simplefix-go/session"
16 | fixgen "github.com/b2broker/simplefix-go/tests/fix44"
17 | )
18 |
19 | func RunNewInitiator(addr string, t *testing.T, settings *session.LogonSettings) (s *session.Session, handler *simplefixgo.DefaultHandler) {
20 | conn, err := net.Dial("tcp", addr)
21 | if err != nil {
22 | t.Fatalf("could not dial: %s", err)
23 | }
24 |
25 | handler = simplefixgo.NewInitiatorHandler(context.Background(), fixgen.FieldMsgType, 10)
26 | client := simplefixgo.NewInitiator(conn, handler, 10, time.Minute*50)
27 |
28 | testStorage := memory.NewStorage()
29 |
30 | s, err = session.NewInitiatorSession(
31 | handler,
32 | &pseudoGeneratedOpts,
33 | settings,
34 | testStorage,
35 | testStorage,
36 | )
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | // logging messages:
42 | handler.HandleIncoming(simplefixgo.AllMsgTypes, func(msg []byte) bool {
43 | fmt.Println("incoming:", string(bytes.ReplaceAll(msg, fix.Delimiter, []byte("|"))))
44 | return true
45 | })
46 | handler.HandleOutgoing(simplefixgo.AllMsgTypes, func(msg simplefixgo.SendingMessage) bool {
47 | data, err := msg.ToBytes()
48 | if err != nil {
49 | panic(err)
50 | }
51 | fmt.Println("outgoing:", string(bytes.ReplaceAll(data, fix.Delimiter, []byte("|"))))
52 | return true
53 | })
54 |
55 | // todo move
56 | go func() {
57 | time.Sleep(time.Second * 10)
58 | fmt.Println("resending the request after 10 seconds")
59 | err := s.Send(fixgen.ResendRequest{}.New().SetFieldBeginSeqNo(2).SetFieldEndSeqNo(3))
60 | if err != nil {
61 | panic(err)
62 | }
63 | }()
64 |
65 | err = s.Run()
66 | if err != nil {
67 | t.Fatalf("could not run the session: %s", err)
68 | }
69 |
70 | go func() {
71 | err := client.Serve()
72 | if err != nil && !errors.Is(err, simplefixgo.ErrConnClosed) {
73 | panic(fmt.Errorf("could not serve the client: %s", err))
74 | }
75 | }()
76 |
77 | return s, handler
78 | }
79 |
--------------------------------------------------------------------------------
/tests/opts.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "github.com/b2broker/simplefix-go/session"
5 | "github.com/b2broker/simplefix-go/session/messages"
6 | fixgen "github.com/b2broker/simplefix-go/tests/fix44"
7 | )
8 |
9 | var pseudoGeneratedOpts = session.Opts{
10 | MessageBuilders: session.MessageBuilders{
11 | HeaderBuilder: fixgen.Header{}.New(),
12 | TrailerBuilder: fixgen.Trailer{}.New(),
13 | LogonBuilder: fixgen.Logon{}.New(),
14 | LogoutBuilder: fixgen.Logout{}.New(),
15 | RejectBuilder: fixgen.Reject{}.New(),
16 | HeartbeatBuilder: fixgen.Heartbeat{}.New(),
17 | TestRequestBuilder: fixgen.TestRequest{}.New(),
18 | ResendRequestBuilder: fixgen.ResendRequest{}.New(),
19 | },
20 | Tags: &messages.Tags{
21 | MsgType: mustConvToInt(fixgen.FieldMsgType),
22 | MsgSeqNum: mustConvToInt(fixgen.FieldMsgSeqNum),
23 | HeartBtInt: mustConvToInt(fixgen.FieldHeartBtInt),
24 | EncryptedMethod: mustConvToInt(fixgen.FieldEncryptMethod),
25 | },
26 | AllowedEncryptedMethods: map[string]struct{}{
27 | fixgen.EnumEncryptMethodNoneother: {},
28 | },
29 | SessionErrorCodes: &messages.SessionErrorCodes{
30 | InvalidTagNumber: mustConvToInt(fixgen.EnumSessionRejectReasonInvalidtagnumber),
31 | RequiredTagMissing: mustConvToInt(fixgen.EnumSessionRejectReasonRequiredtagmissing),
32 | //TagNotDefinedForMessageType: mustConvToInt(fixgen.EnumSessionRejectReasonTagNotDefinedForThisMessageType),
33 | UndefinedTag: mustConvToInt(fixgen.EnumSessionRejectReasonUndefinedtag),
34 | TagSpecialWithoutValue: mustConvToInt(fixgen.EnumSessionRejectReasonTagspecifiedwithoutavalue),
35 | IncorrectValue: mustConvToInt(fixgen.EnumSessionRejectReasonValueisincorrectoutofrangeforthistag),
36 | IncorrectDataFormatValue: mustConvToInt(fixgen.EnumSessionRejectReasonIncorrectdataformatforvalue),
37 | DecryptionProblem: mustConvToInt(fixgen.EnumSessionRejectReasonDecryptionproblem),
38 | SignatureProblem: mustConvToInt(fixgen.EnumSessionRejectReasonSignatureproblem),
39 | CompIDProblem: mustConvToInt(fixgen.EnumSessionRejectReasonCompidproblem),
40 | Other: mustConvToInt(fixgen.EnumSessionRejectReasonOther),
41 | },
42 | }
43 |
--------------------------------------------------------------------------------
/tests/utils.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | func mustConvToInt(s string) int {
8 | i, err := strconv.Atoi(s)
9 | if err != nil {
10 | panic(err)
11 | }
12 |
13 | return i
14 | }
15 |
--------------------------------------------------------------------------------
/utils/event_handler_pool.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "sync"
4 |
5 | type Event int
6 |
7 | const (
8 | // EventDisconnect occurs when the connection is down.
9 | EventDisconnect Event = iota
10 |
11 | // EventDisconnect occurs when the connection is up.
12 | EventConnect
13 |
14 | // EventStopped occurs when the handler is stopped.
15 | EventStopped
16 |
17 | // EventLogon occurs upon receiving the Logon message.
18 | EventLogon
19 |
20 | // EventRequest occurs upon sending the Logon message,
21 | // after which the Session awaits an answer from the counterparty.
22 | EventRequest
23 |
24 | // EventLogout occurs upon receiving the Logout message.
25 | EventLogout
26 | )
27 |
28 | // EventHandlerFunc is called when an event occurs.
29 | type EventHandlerFunc func() bool
30 |
31 | // EventHandlerPool is a service required for saving and calling the event handlers.
32 | type EventHandlerPool struct {
33 | mu sync.RWMutex
34 | pool map[Event][]EventHandlerFunc
35 | }
36 |
37 | // NewEventHandlerPool creates a new EventHandlerPool instance.
38 | func NewEventHandlerPool() *EventHandlerPool {
39 | return &EventHandlerPool{pool: make(map[Event][]EventHandlerFunc)}
40 | }
41 |
42 | // Handle adds a new handler for an event.
43 | func (evp *EventHandlerPool) Handle(e Event, handle EventHandlerFunc) {
44 | evp.mu.Lock()
45 | defer evp.mu.Unlock()
46 |
47 | if _, ok := evp.pool[e]; !ok {
48 | evp.pool[e] = []EventHandlerFunc{}
49 | }
50 |
51 | evp.pool[e] = append(evp.pool[e], handle)
52 | }
53 |
54 | // Trigger calls all handlers associated with an occurring event.
55 | func (evp *EventHandlerPool) Trigger(e Event) {
56 | evp.mu.RLock()
57 | defer evp.mu.RUnlock()
58 |
59 | handlers, ok := evp.pool[e]
60 | if !ok {
61 | return
62 | }
63 |
64 | for _, handle := range handlers {
65 | if !handle() {
66 | return
67 | }
68 | }
69 | }
70 |
71 | func (evp *EventHandlerPool) Clean() {
72 | evp.mu.Lock()
73 | defer evp.mu.Unlock()
74 |
75 | evp.pool = nil
76 | }
77 |
--------------------------------------------------------------------------------
/utils/helpers.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/xml"
5 | "fmt"
6 | "io"
7 | "os"
8 | )
9 |
10 | // ParseXML is used to unmarshal an XML schema into a Go structure.
11 | func ParseXML(path string, data interface{}) error {
12 | var err error
13 |
14 | source, err := os.Open(path)
15 | if err != nil {
16 | return fmt.Errorf("could not open the file: %s", path)
17 | }
18 |
19 | sourceData, err := io.ReadAll(source)
20 | if err != nil {
21 | return fmt.Errorf("could not read the file: %s", path)
22 | }
23 |
24 | err = xml.Unmarshal(sourceData, data)
25 | if err != nil {
26 | return fmt.Errorf("could not unmarshal the XML: %s", path)
27 | }
28 |
29 | return nil
30 | }
31 |
--------------------------------------------------------------------------------
/utils/timed_wg.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "sync"
6 | "time"
7 | )
8 |
9 | // ErrWGExpired is returned if the timeout expires before the WaitGroup has received
10 | // a "Done" confirmation from all of the streams.
11 | var ErrWGExpired = errors.New("wait group timeout expired")
12 |
13 | // TimedWaitGroup is a combination of WaitGroup and timeout
14 | type TimedWaitGroup struct {
15 | sync.WaitGroup
16 | }
17 |
18 | // WaitWithTimeout awaits any of the two cases:
19 | // - timeout expires (in which case an error is returned)
20 | // - a WaitGroup receives a "Done" confirmation (in which case nil is returned)
21 | func (wg *TimedWaitGroup) WaitWithTimeout(timeout time.Duration) error {
22 | ch := make(chan struct{})
23 |
24 | go func() {
25 | defer close(ch)
26 | wg.Wait()
27 | }()
28 |
29 | select {
30 | case <-ch:
31 | return nil
32 |
33 | case <-time.After(timeout):
34 | return ErrWGExpired
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/utils/timed_wg_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestTimedWaitGroup_WaitWithTimeoutNegative(t *testing.T) {
10 | wg := TimedWaitGroup{}
11 |
12 | wg.Add(1)
13 |
14 | go func() {
15 | time.Sleep(time.Millisecond * 10)
16 | wg.Done()
17 | }()
18 |
19 | err := wg.WaitWithTimeout(time.Millisecond)
20 | if !errors.Is(err, ErrWGExpired) {
21 | t.Fatalf("unexpected error: %s", err)
22 | }
23 | }
24 |
25 | func TestTimedWaitGroup_WaitWithTimeout(t *testing.T) {
26 | wg := TimedWaitGroup{}
27 |
28 | wg.Add(1)
29 |
30 | go func() {
31 | time.Sleep(time.Millisecond)
32 | wg.Done()
33 | }()
34 |
35 | err := wg.WaitWithTimeout(time.Millisecond * 2)
36 | if err != nil {
37 | t.Fatalf("unexpected error: %s", err)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/utils/timer.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "sync"
7 | "time"
8 | )
9 |
10 | var (
11 | ErrZeroTimeout = errors.New("zero timeout")
12 | ErrTooSmallFrequency = errors.New("the frequency is too small")
13 | )
14 |
15 | const frequency time.Duration = 10
16 | const minFrequency = time.Microsecond
17 |
18 | type Timer struct {
19 | mu sync.RWMutex
20 | lastUpdate time.Time
21 |
22 | timeout time.Duration
23 | checkingTimeout time.Duration
24 |
25 | ctx context.Context
26 | cancel context.CancelFunc
27 | }
28 |
29 | func NewTimer(timeout time.Duration) (*Timer, error) {
30 | if timeout == 0 {
31 | return nil, ErrZeroTimeout
32 | }
33 |
34 | checkingTimeout := timeout / frequency
35 |
36 | if checkingTimeout < minFrequency {
37 | return nil, ErrTooSmallFrequency
38 | }
39 |
40 | ctx, cancel := context.WithCancel(context.Background())
41 |
42 | return &Timer{
43 | checkingTimeout: checkingTimeout,
44 | timeout: timeout,
45 | ctx: ctx,
46 | cancel: cancel,
47 | }, nil
48 | }
49 |
50 | func (t *Timer) Close() {
51 | t.cancel()
52 | }
53 |
54 | func (t *Timer) Refresh() {
55 | t.mu.Lock()
56 | defer t.mu.Unlock()
57 |
58 | t.lastUpdate = time.Now()
59 | }
60 |
61 | // TakeTimeout will be in action until timeout is reached or the Close method is called.
62 | func (t *Timer) TakeTimeout() {
63 | t.Refresh()
64 | ticker := time.NewTicker(t.checkingTimeout)
65 | for {
66 | select {
67 | case <-ticker.C:
68 | t.mu.RLock()
69 | rest := time.Until(t.lastUpdate.Add(t.timeout))
70 | t.mu.RUnlock()
71 |
72 | if rest <= 0 {
73 | return
74 | }
75 |
76 | case <-t.ctx.Done():
77 | return
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/utils/timer_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestTimer_TimeoutZero(t *testing.T) {
10 | _, err := NewTimer(0)
11 | if !errors.Is(err, ErrZeroTimeout) {
12 | t.Fatalf("expected error: %s, returned: %s", ErrZeroTimeout, err)
13 | }
14 | }
15 |
16 | func TestTimer_Timeout(t *testing.T) {
17 | delay := time.Millisecond * 10
18 | tm, err := NewTimer(delay)
19 | if err != nil {
20 | t.Fatalf("unexpected error: %s", err)
21 | }
22 |
23 | now := time.Now()
24 | for {
25 | tm.TakeTimeout()
26 | if time.Until(now.Add(delay)) > 0 {
27 | t.Fatalf("delay was not applied")
28 | } else {
29 | return
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------