├── .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 | --------------------------------------------------------------------------------