Sackfix is designed to use strongly typed messages and fields, all of which are immutable. Having said that, fix requires the calculation of body length and checksum prior to sending, so these fields are generated internally when the message is rendered to the fix string prior to sending.

The code is all on GitHub: PendaRed /sackfix

Fields

Show me a field….

package org.sackfix.field

import org.sackfix.common.validated.fields.SfFieldString

/**
  * Generated by SackFix code generator on 20170401
  */
case class SideField(override val value: String) extends SfFieldString(54, value) {
  override def toString = appendStringBuilder().toString
  override def appendStringBuilder(b:StringBuilder = new StringBuilder()) = b.append("(54)Side=(").append(value).append(")").append(SideField.fixDescriptionByValue.getOrElse(value,""))
}

object SideField {
  val TagId = 54
  val Buy="1"
  val Sell="2"
  val BuyMinus="3"
  val SellPlus="4"
  val SellShort="5"
  val SellShortExempt="6"
  val Undisclosed="7"
  val Cross="8"
  val CrossShort="9"
  val CrossShortExempt="A"
  val AsDefined="B"
  val Opposite="C"
  val Subscribe="D"
  val Redeem="E"
  val Lend="F"
  val Borrow="G"
  lazy val fixDescriptionByValue = Map("1"->"BUY","2"->"SELL",
    "3"->"BUY_MINUS","4"->"SELL_PLUS","5"->"SELL_SHORT",
    "6"->"SELL_SHORT_EXEMPT","7"->"UNDISCLOSED","8"->"CROSS",
    "9"->"CROSS_SHORT","A"->"CROSS_SHORT_EXEMPT","B"->"AS_DEFINED",
    "C"->"OPPOSITE","D"->"SUBSCRIBE","E"->"REDEEM",
    "F"->"LEND","G"->"BORROW")

  def decode(a: Option[Any]) : Option[SideField] = a match {
    case Some(v) => decode(v)
    case _ =>  scala.Option.empty[SideField]
  }

  def decode(a: Any) : Option[SideField] = a match {
    case v: String => Some(SideField(v))
    case v: Char => Some(SideField(v.toString))
    case v: SideField => Some(v)
    case _ => scala.Option.empty[SideField]
  }
}

As you can see, no implicites and the decoder function. Hmmm. What of Scala type classes? This code is autogenerated, so I kept it simple. If you want to change to use implicits and ad-hoc polymorphism then please change the code generator.

Show me a message!

Messages are pretty similar, decoders, some traits to pull in common formatting and some other useful stuff. The main message is immutable. As you can see it is actually the message body, there is a wrapper class and a header which you use to create the full message prior to sending - look at the example projects.

Example of how to create a fix message

As mentioned here you create the message body and send it to the sackfix session actor to format into the session message which is sent down the wire. When it has been sent the fully formed SfMessage is returned to you in an Ack.

fixSessionActor ! BusinessFixMsgOut(
  ExecutionReportMessage(orderIDField = OrderIDField("1"),
    execIDField = ExecIDField("exec1"),
    execTypeField = ExecTypeField(ExecTypeField.New),
    ordStatusField = OrdStatusField(OrdStatusField.New),
    instrumentComponent = InstrumentComponent(symbolField = symbol),
    sideField = side,
    leavesQtyField = LeavesQtyField(quantity),
    cumQtyField = CumQtyField(0),
    avgPxField = AvgPxField(0)), correlationId)

Example generated fix message

package org.sackfix.fix44

import org.sackfix.field._
import org.sackfix.common.validated.fields.{SfFixMessageBody, SfFixMessageDecoder, SfFixFieldsToAscii, SfFixRenderable}
import org.sackfix.common.message.SfRepeatingGroupCountException
import scala.annotation.tailrec
import scala.collection.immutable.HashSet
import scala.collection.mutable.ArrayBuffer


/**
  * Generated by SackFix code generator on 20170401
  * Source specification was read from:
  *   /quickfixj1.6.0/FIX44.xml
  */
case class LogonMessage(encryptMethodField:EncryptMethodField,
                        heartBtIntField:HeartBtIntField,
                        rawDataLengthField:Option[RawDataLengthField]=None,
                        rawDataField:Option[RawDataField]=None,
                        resetSeqNumFlagField:Option[ResetSeqNumFlagField]=None,
                        nextExpectedMsgSeqNumField:Option[NextExpectedMsgSeqNumField]=None,
                        maxMessageSizeField:Option[MaxMessageSizeField]=None,
                        noMsgTypesField:Option[NoMsgTypesField]=None,
                        msgTypesGroups: Option[List[MsgTypesGroup]]=None,
                        testMessageIndicatorField:Option[TestMessageIndicatorField]=None,
                        usernameField:Option[UsernameField]=None,
                        passwordField:Option[PasswordField]=None) extends SfFixMessageBody("A")  with SfFixRenderable with SfFixFieldsToAscii {
  if (noMsgTypesField.map(_.value).getOrElse(0) != msgTypesGroups.map(_.size).getOrElse(0))
    throw SfRepeatingGroupCountException(NoMsgTypesField.TagId,noMsgTypesField.map(_.value).getOrElse(0), msgTypesGroups.map(_.size).getOrElse(0))

  override lazy val fixStr : String = appendFixStr().toString
  override def appendFixStr(b:StringBuilder = new StringBuilder): StringBuilder = format(formatForFix, b)

  override def toString():String = appendStringBuilder().toString()
  def appendStringBuilder(b:StringBuilder = new StringBuilder) : StringBuilder = format(formatForToString, b)

  def format( fmt: ((StringBuilder,SfFixRenderable)=>Unit), b:StringBuilder = new StringBuilder()): StringBuilder = {
    fmt(b,encryptMethodField)
    fmt(b,heartBtIntField)
    rawDataLengthField.foreach(fmt(b,_))
    rawDataField.foreach(fmt(b,_))
    resetSeqNumFlagField.foreach(fmt(b,_))
    nextExpectedMsgSeqNumField.foreach(fmt(b,_))
    maxMessageSizeField.foreach(fmt(b,_))
    noMsgTypesField.foreach(fmt(b,_))
    msgTypesGroups.getOrElse(List.empty).foreach(fmt(b,_))
    testMessageIndicatorField.foreach(fmt(b,_))
    usernameField.foreach(fmt(b,_))
    passwordField.foreach(fmt(b,_))
    b
  }

}

object LogonMessage extends SfFixMessageDecoder {
  override val MandatoryFields = HashSet[Int](
    EncryptMethodField.TagId, HeartBtIntField.TagId)

  override def isMandatoryField(tagId:Int) : Boolean = {
    MandatoryFields.contains(tagId)  ||
    MsgTypesGroup.isMandatoryField(tagId)
  }

  override val OptionalFields = HashSet[Int](
    RawDataLengthField.TagId, RawDataField.TagId, ResetSeqNumFlagField.TagId, NextExpectedMsgSeqNumField.TagId, MaxMessageSizeField.TagId,
    NoMsgTypesField.TagId, TestMessageIndicatorField.TagId, UsernameField.TagId, PasswordField.TagId)

  override def isOptionalField(tagId:Int) : Boolean = {
    OptionalFields.contains(tagId)  ||
    MsgTypesGroup.isOptionalField(tagId)
  }

  override def isFieldOf(tagId:Int) : Boolean = isMandatoryField(tagId) || isOptionalField(tagId)  ||
    MsgTypesGroup.isFieldOf(tagId)

   override lazy val RepeatingGroupsTags = HashSet[Int](
    NoMsgTypesField.TagId)


  override def isFirstField(tagId:Int) : Boolean = tagId==EncryptMethodField.TagId

  override def decode(flds: Seq[Tuple2[Int, Any]], startPos:Int = 0):Option[SfFixMessageBody] = {
    val (pos, myFields, nextTagPosLookup) = extractMyFieldsAndPopulatePositions(false, flds, startPos)
    validateMandatoryFieldsPresent(myFields)

    if (MandatoryFields.isEmpty || myFields.nonEmpty) {
      Some(LogonMessage(EncryptMethodField.decode(myFields.get(EncryptMethodField.TagId)).get,
        HeartBtIntField.decode(myFields.get(HeartBtIntField.TagId)).get,
        myFields.get(RawDataLengthField.TagId).flatMap(f=>RawDataLengthField.decode(f)),
        myFields.get(RawDataField.TagId).flatMap(f=>RawDataField.decode(f)),
        myFields.get(ResetSeqNumFlagField.TagId).flatMap(f=>ResetSeqNumFlagField.decode(f)),
        myFields.get(NextExpectedMsgSeqNumField.TagId).flatMap(f=>NextExpectedMsgSeqNumField.decode(f)),
        myFields.get(MaxMessageSizeField.TagId).flatMap(f=>MaxMessageSizeField.decode(f)),
        myFields.get(NoMsgTypesField.TagId).flatMap(f=>NoMsgTypesField.decode(f)),
        if (nextTagPosLookup.contains(NoMsgTypesField.TagId)) MsgTypesGroup.decode(flds, nextTagPosLookup(NoMsgTypesField.TagId)) else None,
        myFields.get(TestMessageIndicatorField.TagId).flatMap(f=>TestMessageIndicatorField.decode(f)),
        myFields.get(UsernameField.TagId).flatMap(f=>UsernameField.decode(f)),
        myFields.get(PasswordField.TagId).flatMap(f=>PasswordField.decode(f))))
    } else None
  }


}

Common classes

If you read the above code you will see it references SfFixMessageDecoder, SfFixRenderable and so on. These are classes defined in the sackfix common project. There is no need for you to understand any of this. Just code your business objects.