You must open the sackfixexamples project in IntelliJ as a new project.

The code is all on GitHub: PendaRed /sackfixexamples

Running the Acceptor

Look in application.conf - make sure you are happy with the path for the persistent files - they hold messages, sequence numbers and a file to determine which day it last ran.

Then right click on the acceptor class in IntelliJ to run it:

org.sackfix.server.SackFixServer

registration timeout

Should you see something in the logs like

12:10:39.842 DEBUG akka.io.TcpIncomingConnection - Configured registration timeout of [5000 milliseconds] expired, stopping

Then you are probably running on some rubbish old piece of hardware (like my ancient Linux server). Change the default timeout in application.conf as below.

akka.io.tcp.register-timeout = 10000ms

Running the Initiator

Once the acceptor has started you should start the initiator, again right click on it and run.

org.sackfix.client.SackFixClient

You may get a warning about logging - see below. You can then open the logs dir and take a look. Kill them both and set the log levels as below:

  <!-- set level to ERROR to turn this off -->
  <logger name="fixmessages" level="DEBUG" additivity="false">
      <appender-ref ref="FIXMESSAGE_FILE"/>
  </logger>
  <!-- Adds 500 microseconds to each msg process -->
  <logger name="fixVerboseMessages" level="DEBUG" additivity="false">
      <appender-ref ref="FIXVERBOSEMESSAGE_FILE"/>
  </logger>
  <root level="DEBUG">
      <appender-ref ref="FILE" />
  </root>

Start the acceptor and the initiator again and you now have detailed logs of the raw fix and also human readable dumps of the fix messages. Turning on detailed fix logging will slow down SackFix by up to hundreds of microseconds per message - but since this is not for low latency trading that will be fine.

Doing a run from SBT without IntelliJ

use git clone to download the project, and cd into the project

sbt
>compile
>exit

sbt sfexampleacceptor/run org.sackfix.server.SackFixServer

or

sbt
>compile
>exit

sbt sfexampleinitiator/run org.sackfix.client.SackFixClient

If you mess up the sequence numbers will be out of kilter, so you will have to edit them in the initiator application.conf.

Example logs

Fix log called: sfinitiator.2017-04-03.0.fix.log

07:00:48.527 OUT 8=FIX.4.49=9235=A49=ExampleFixClient56=ExampleFixServer34=10005952=20170403-07:00:48.49898=0108=2010=195
07:00:49.705 IN  8=FIX.4.49=9235=A49=ExampleFixServer56=ExampleFixClient34=10005852=20170403-07:00:49.68398=0108=2010=191
07:00:49.768 OUT 8=FIX.4.49=9935=249=ExampleFixClient56=ExampleFixServer34=10006052=20170403-07:00:49.7677=10005716=10005810=016
07:00:50.219 IN  8=FIX.4.49=17435=849=ExampleFixServer56=ExampleFixClient34=10005743=Y52=20170403-07:00:50.102122=20170403-06:58:36.04437=117=exec1150=039=055=JPG.GB54=2151=100.014=0.06=0.010=000
07:00:51.082 IN  8=FIX.4.49=9635=449=ExampleFixServer56=ExampleFixClient34=10005852=20170403-07:00:50.220123=Y36=10005910=140
07:00:52.297 OUT 8=FIX.4.49=13935=D49=ExampleFixClient56=ExampleFixServer34=10006152=20170403-07:00:52.23811=155=JPG.GB54=260=20170403-07:00:52.19638=100.040=110=243
07:00:52.394 IN  8=FIX.4.49=14335=849=ExampleFixServer56=ExampleFixClient34=10005952=20170403-07:00:52.39137=117=exec1150=039=055=JPG.GB54=2151=100.014=0.06=0.010=005
07:00:52.398 OUT 8=FIX.4.49=13935=D49=ExampleFixClient56=ExampleFixServer34=10006252=20170403-07:00:52.39711=255=JPG.GB54=160=20170403-07:00:52.39738=100.040=110=253
07:00:52.404 IN  8=FIX.4.49=14335=849=ExampleFixServer56=ExampleFixClient34=10006052=20170403-07:00:52.40237=117=exec1150=039=055=JPG.GB54=1151=100.014=0.06=0.010=245
  

The same messages in the human readable log are below, for file:

07:00:48.569 OUT (8)BeginString=(FIX.4.4),(9)BodyLength=(92),(35)MsgType=(A)LOGON,(49)SenderCompID=(ExampleFixClient),(56)TargetCompID=(ExampleFixServer),(34)MsgSeqNum=(100059),(52)SendingTime=(2017-04-03T07:00:48.498),(98)EncryptMethod=(0)NONE_OTHER,(108)HeartBtInt=(20),(10)CheckSum=(195)
07:00:49.749 IN  (8)BeginString=(FIX.4.4),(9)BodyLength=(92),(35)MsgType=(A)LOGON,(49)SenderCompID=(ExampleFixServer),(56)TargetCompID=(ExampleFixClient),(34)MsgSeqNum=(100058),(52)SendingTime=(2017-04-03T07:00:49.683),(98)EncryptMethod=(0)NONE_OTHER,(108)HeartBtInt=(20),(10)CheckSum=(191)
07:00:49.770 OUT (8)BeginString=(FIX.4.4),(9)BodyLength=(99),(35)MsgType=(2)RESEND_REQUEST,(49)SenderCompID=(ExampleFixClient),(56)TargetCompID=(ExampleFixServer),(34)MsgSeqNum=(100060),(52)SendingTime=(2017-04-03T07:00:49.767),(7)BeginSeqNo=(100057),(16)EndSeqNo=(100058),(10)CheckSum=(016)
07:00:51.073 IN  (8)BeginString=(FIX.4.4),(9)BodyLength=(174),(35)MsgType=(8)EXECUTION_REPORT,(49)SenderCompID=(ExampleFixServer),(56)TargetCompID=(ExampleFixClient),(34)MsgSeqNum=(100057),(43)PossDupFlag=(Y),(52)SendingTime=(2017-04-03T07:00:50.102),(122)OrigSendingTime=(2017-04-03T06:58:36.044),(37)OrderID=(1),(17)ExecID=(exec1),(150)ExecType=(0)NEW,(39)OrdStatus=(0)NEW,(55)Symbol=(JPG.GB),(54)Side=(2)SELL,(151)LeavesQty=(100.0),(14)CumQty=(0.0),(6)AvgPx=(0.0),(10)CheckSum=(000)
07:00:51.166 IN  (8)BeginString=(FIX.4.4),(9)BodyLength=(96),(35)MsgType=(4)SEQUENCE_RESET,(49)SenderCompID=(ExampleFixServer),(56)TargetCompID=(ExampleFixClient),(34)MsgSeqNum=(100058),(52)SendingTime=(2017-04-03T07:00:50.220),(123)GapFillFlag=(Y),(36)NewSeqNo=(100059),(10)CheckSum=(140)
07:00:52.336 OUT (8)BeginString=(FIX.4.4),(9)BodyLength=(139),(35)MsgType=(D)ORDER_SINGLE,(49)SenderCompID=(ExampleFixClient),(56)TargetCompID=(ExampleFixServer),(34)MsgSeqNum=(100061),(52)SendingTime=(2017-04-03T07:00:52.238),(11)ClOrdID=(1),(55)Symbol=(JPG.GB),(54)Side=(2)SELL,(60)TransactTime=(2017-04-03T07:00:52.196),(38)OrderQty=(100.0),(40)OrdType=(1)MARKET,(10)CheckSum=(243)
07:00:52.395 IN  (8)BeginString=(FIX.4.4),(9)BodyLength=(143),(35)MsgType=(8)EXECUTION_REPORT,(49)SenderCompID=(ExampleFixServer),(56)TargetCompID=(ExampleFixClient),(34)MsgSeqNum=(100059),(52)SendingTime=(2017-04-03T07:00:52.391),(37)OrderID=(1),(17)ExecID=(exec1),(150)ExecType=(0)NEW,(39)OrdStatus=(0)NEW,(55)Symbol=(JPG.GB),(54)Side=(2)SELL,(151)LeavesQty=(100.0),(14)CumQty=(0.0),(6)AvgPx=(0.0),(10)CheckSum=(005)
07:00:52.399 OUT (8)BeginString=(FIX.4.4),(9)BodyLength=(139),(35)MsgType=(D)ORDER_SINGLE,(49)SenderCompID=(ExampleFixClient),(56)TargetCompID=(ExampleFixServer),(34)MsgSeqNum=(100062),(52)SendingTime=(2017-04-03T07:00:52.397),(11)ClOrdID=(2),(55)Symbol=(JPG.GB),(54)Side=(1)BUY,(60)TransactTime=(2017-04-03T07:00:52.397),(38)OrderQty=(100.0),(40)OrdType=(1)MARKET,(10)CheckSum=(253)
07:00:52.406 IN  (8)BeginString=(FIX.4.4),(9)BodyLength=(143),(35)MsgType=(8)EXECUTION_REPORT,(49)SenderCompID=(ExampleFixServer),(56)TargetCompID=(ExampleFixClient),(34)MsgSeqNum=(100060),(52)SendingTime=(2017-04-03T07:00:52.402),(37)OrderID=(1),(17)ExecID=(exec1),(150)ExecType=(0)NEW,(39)OrdStatus=(0)NEW,(55)Symbol=(JPG.GB),(54)Side=(1)BUY,(151)LeavesQty=(100.0),(14)CumQty=(0.0),(6)AvgPx=(0.0),(10)CheckSum=(245)
  

A note on Akka

Akka is asynchronous which allows it to scale performance with the number of cores. Look at the example OMS classes in the acceptor and initiator and you can see right away there is an internal ack message for every send. There is no synchronous pipe line within the code, but collaborating actors.

The stores

The other files of note are the sequence number, message stores and session today files, their location is configured in application.conf. For example:

dir sackfixexamples\sessmsgstore\acceptor
fix.4.4-examplefixserver-examplefixclient.firstSessionOpened
fix.4.4-examplefixserver-examplefixclient.messages
fix.4.4-examplefixserver-examplefixclient.messagesindex
fix.4.4-examplefixserver-examplefixclient.senderseqnums
fix.4.4-examplefixserver-examplefixclient.targetseqnums
  

The firstSessionOpened is used to detect if this is the first session of the day and if so reset the sequence number to 1. The messages and messagesIndex are for the message store and resend, and the senderseqnums and targetseqnums hold the values of the sequence numbers. These files are updated as message arrive and as they are sent. You can provide your own implementation of the message store rather than using the default one.

These data store files are archived to a dated version on sequence reset and you should have a housekeeping job to ensure old files are removed.

Wierd log error

SackFix uses Slf4j at runtime, which binds to logback - there is a logback.xml in the resources project directory. Should you see this in IntelliJ:

log4j:WARN No appenders could be found for logger (akka.event.slf4j.Slf4jLogger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
  

Then you have let log4j into your classpath. Open Module Settings and bump logback to the top of the dependencies and do a refesh in SBT and full rebuild. That worked for me.

The problem is that Slf4j is finding Log4j when I want it to find logback.