Running the examples
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.