How to route session level rejects through the FIXEdge

Solution overview

The diagram shows a configuration in which FIXEdge is a router between Client and Exchange. 

FIXEdge works with two sessions:

  1. Client <-> FIXEdge (FIX session 1) 
  2. FIXEdge <-> Exchange (FIX session 2)

Expected message flow: messages from the Client are routed to the Exchange and messages from the Exchange are routed back to the Client.
Sequences in such sessions FIX session 1 and FIX session 2 don't match. If the Exchange rejects a message from the client with session Reject (35=3), the Client often wants to get information about the rejected message. However, the value from RefSeqNum (45) tag doesn't refer to the original message from FIX Session 1.
As session-level messages aren't routed by BL rules, additional processing of session level Reject (35=3) is required to pass information about a rejected message back to the sender.

The following example shows how to use BL Rules and JavaScript to achieve it.

BL Rules configuration

Session level Rejects (35=3) trigger OnSessionLevelRejectEvent action on BL.

In OnSessionLevelRejectEvent action can be called a JavaScript preparing a reject message for sending to the required session. In this case, it is SENDER

BL_Config.xml
<FIXEdge>
 <BusinessLayer>
  <!-- Rules for messages routing -->
  <Rule Description="Route messages from SENDER to REPORTER">
   <Source>
    <FixSession SenderCompID="SENDER" TargetCompID=".*"/>
   </Source>
   <Action>
    <Send>
     <FixSession SenderCompID="FIXEDGE" TargetCompID="REPORTER"/>
    </Send>
   </Action>
  </Rule>
  <Rule Description="Route messages from REPORTER to SENDER">
   <Source>
    <FixSession SenderCompID="REPORTER" TargetCompID=".*"/>
   </Source>
   <Action>
    <Send>
     <FixSession SenderCompID="FIXEDGE" TargetCompID="SENDER"/>
    </Send>
   </Action>
  </Rule>


  <!-- Rules for rejects routing -->
  <OnSessionLevelRejectEvent>
   <Source>
    <FixSession SenderCompID="REPORTER" TargetCompID=".*"/>
   </Source>
   <Action>
    <Script Language="JavaScript" FileName ="../FIXEdge1/conf/enrichReject.js"/>
    <Send>
     <FixSession SenderCompID="FIXEDGE" TargetCompID="SENDER"/>
    </Send>    
   </Action>
  </OnSessionLevelRejectEvent>

  <DefaultRule>
   <Action>
    <DoNothing/>
   </Action>
  </DefaultRule>
 </BusinessLayer>
</FIXEdge>   

JavaScript 

The JavaScript does the following:

1. Stores all data from the original reject to some variables

2. Gets rejected message from the session logs with getMsgBySeqNum(<SenderCompId>, <TargetCompId> , <SeqNum>) function.

3. Serializes to the text serializeMessage(<tagDelimiter>)

4. Creates new reject message again with transform(<target protocol>, <target message type> )

5. Fills stored data from #1

6. Adds information about the rejected message. In this example Text (58) tag is used for it.

The message is ready. It will be sent to the session later.

enrichReject.js
// getting information from reject;
var RefSeqNum = new Number(getNumField(45));
RefMsgType = getStringField(372);
Text = getStringField(58);
RefTagID = getNumField(371); // Session Reject (35=3) only
SessionRejectReason = getNumField(373); // Session Reject (35=3) only
//getting rejected message from the storage.
sender = getStringField(56); // Get SenderCompId as TragetCompId in Reject message
target = getStringField(49); // Get TragetCompId as SenderCompId in Reject message
n = Number(RefSeqNum); // convering RefSeqNum to integer from JSVAL
getMsgBySeqNum(sender, target, n);
msg = serializeMessage("|"); // convert rejected message to plaintext.
// prepare reject and fill it with information from original one
transform("FIX.4.4", "3");
if (RefSeqNum != null) 
    setNumField(45, Number(RefSeqNum));
if (RefMsgType != null) 
    setStringField(372, RefMsgType);
if (RefTagID != null) 
    setNumField(371, Number(RefTagID));
if (SessionRejectReason != null) 
    setNumField(373, Number(SessionRejectReason));

// add information about rejected message to Text (58) field.
if (Text == null) {
    Text = "";
}
Text = Text + "   Original message:" + msg;
setStringField(58, Text);

SENDER session can get information about the rejected message from the Text (58) field:

Message example

Sent message: 

8=FIX.4.4|9=209|35=D|49=FIXEdge|56=FIXCLIENT|34=4|52=20180412-09:49:37.737|11=E2017000000000000000001|453=3|448=TestValue1|447=D|452=11|448=TestValue2|447=D|452=122|448=XYZT|447=D|452=17|1=EPAM-TEST|55=USD/CNH|15=USD|58=TEXT|10=078|


Received SessionLevelReject message:

8=FIX.4.4|9=245|35=3|49=FIXCLIENT|56=FIXEdge|34=4|52=20180412-09:49:37.738|45=4|371=452|372=D|373=6|58=Field value '122' does not meet ValBlock dictionary conditions in tag 452[Group tag=453, Entry #=1] in message New Order - Single (D) with sequence number 4.|10=210|