How to update entities stored in History with referenced sequence number
Overview
This document describes how to configure the following use case:
A client sends New Orders List (35=E) message to FIXEdge. FIXEdge stores the message content into DataBase and routes the messages to a counterparty.
Assumed that the following tags should be stored into DB:
- ListID(66)
- ClOrdId(11) from a repeating group
- Symbol(55) from a repeating group
- TransactTime(60) from a repeating group
- Text (58) from a repeating group
If the counterparty rejects a message with business reject (35=j), the reject reason will be updated in Text entity.
If the counterparty rejects a message with session level reject (35=3), the record will be removed from the DB.
DB Schema
Each repeating group entry is represented as a row with ListID(66) value as a key field.
BL Configuration
Configure ODBC History
Used ODBC History for storing message into the DB table:
<!-- Orders Table: ("ListID"*; "ClOrdId"*; "Symbol"; "TransactTime"; "Text") -->
<History Name="Orders"
StorageType="ODBC"
TableName="orders"
ConnectionString="DSN=FIXEdgeDB;UID=user;PWD=password;>
<KeyField ColumnName="ListID">66</KeyField>
<KeyField ColumnName="ClOrdId">11</KeyField>
<Field ColumnName="Symbol">55</Field>
<Field ColumnName="TransactTime">60</Field>
<Field ColumnName="Text">58</Field>
</History>
Configure messages routing
<Rule Description="Enrich, store, and route a New Order Single message"> <Source Name="SENDER" /> <Condition> <MatchField Value="E" Field="35"/> </Condition> <Action> <!-- The message can be modified/enriched before routing --> <Script Language="JavaScript" FileName ="FIXEdge1/conf/saveOrders"/> <!-- Save orders linked with ListID (66) tag --> <Send Name="RECIPIENT" /> </Action> </Rule>
Processing rejects
<OnBusinesslRejectEvent> <Source Name="RECIPIENT"/> <Condition> <MatchField Value="E" Field="372"/> </Condition> <Action> <Script Language="JavaScript" FileName ="FIXEdge1/conf/updateOrders"/> </Action> </OnBusinesslRejectEvent> <OnSessionLevelRejectEvent> <Source Name="RECIPIENT"/> <Condition> <MatchField Value="E" Field="372"/> </Condition> <Action> <Script Language="JavaScript" FileName ="FIXEdge1/conf/removeOrders"/> </Action> </OnSessionLevelRejectEvent>
It is user responsibility to insert and remove records to/from DB.
Scripts saveOrders and updateOrders should be in sync with DB structure.
Full BL_Config.xml example
Scripts descriptions
Saving Orders to DB
The script does the following:
- Checks if message has required tags
- Searches for records in DB
- Prepares a new record
- Inserts or Updates the record in DB
listID = getStringField(66);
noOrders = getNumField(73);
ordersGrp = getGroup(73)
if ( (listID != null) && (noOrders > 0) && isGroupValid(ordersGrp) ) // check if message is valid
{
for (i=0; i < noOrders; ++i)
{
clOrdId = getStringField(ordersGrp, i, 11);
symbol = getStringField(ordersGrp, i, 55);
transactTime = getStringField(ordersGrp, i, 60);
text = getStringField(ordersGrp, i, 58);
// ListID, ClOrdId are key fields and shouldn't be null
if (clOrdId != null)
{
orderKeys = new Array( noOrders, clOrdId );
// convert missing values to empty string.
if (symbol == null) {symbol="";}
if (transactTime == null) {transactTime="";}
if (text == null) {text="";}
orderCommonValues = new Array(symbol, transactTime, text);
//print("[DEBUG] getting data for listID=" + listID + " and clOrdId=" + clOrdId + " from 'Orders' table.");
// get data from history
records = getRecordFromHistory("Orders", orderKeys); // records array is {ListID*, ClOrdId*, Symbol, TransactTime, Text}
if (records == null) // if there are no records - add new one
{
//print("[DEBUG] Saving order with ListID=" + listID + " and clOrdId=" + clOrdId + " to 'Orders' table.");
saveToHistory("Orders", orderKeys, orderCommonValues, "");
}
else //or update stored record
{
//print("[DEBUG] record was found. Updating order with ListID=" + listID + " and clOrdId=" + clOrdId + " to 'Orders' table.");
updateHistory("Orders", orderKeys, orderCommonValues, "");
}
}
}
}
Updating Orders in DB
The script does the following:
- Prepares a record for update
- Uses RefSeqNum (45) tag value for getting data from the rejected message
- Checks if the rejected message has required tags
- Gets the records of the rejected message from DB
- Updates the record with prepared values
- Updates the records in DB.
rejectedText = "Rejected: " + getNumField(58); // Reject reason will be updated for rejected orders.
//getting rejected message from the storage.
rejectedSeqNum = getNumField(45);
sender = getStringField(56); // Get SenderCompId as TragetCompId in Reject message
target = getStringField(49); // Get TragetCompId as SenderCompId in Reject message
//print("[DEBUG] getting message with " + rejectedSeqNum + " sequence number from storage for session " + sender + "-" + target + ".");
getMsgBySeqNum(sender, target, Number(rejectedSeqNum));
listID = getStringField(66);
noOrders = getNumField(73);
ordersGrp = getGroup(73)
if ( (listID != null) && (noOrders > 0) && isGroupValid(ordersGrp) ) // check if message is valid
{
for (i=0; i < noOrders; ++i)
{
clOrdId = getStringField(ordersGrp, i, 11);
// ListID, ClOrdId are key fields. clOrdId should be not null.
if (clOrdId != null)
{
orderKeys = new Array( noOrders, clOrdId );
//print("[DEBUG] getting data for listID=" + listID + " and clOrdId=" + clOrdId + " from 'Orders' table.");
// get data from history
records = getRecordFromHistory("Orders", orderKeys); // records array is {ListID*, ClOrdId*, Symbol, TransactTime, Text}
// records length should be 5
if ( (records != null) && (records.length == 5) ) {
// updating Text field with reject field.
records[4] = rejectedText;
// remove first 2 elemets as key fields from record array.
records.slice(0, 2); // Left data {Symbol, TransactTime, Text}
updateHistory("Orders", orderKeys, records, ""); // Update data.
}
}
}
}
Remove Orders from DB
The script does the following:
- Prepares a record for update
- Uses RefSeqNum (45) tag value for getting data from the rejected message
- Checks if the rejected message has required tags
- Removes records related to the rejected messages from DB
//getting rejected message from the storage.
rejectedSeqNum = getNumField(45);
sender = getStringField(56); // Get SenderCompId as TragetCompId in Reject message
target = getStringField(49); // Get TragetCompId as SenderCompId in Reject message
//print("[DEBUG] removeOrders.js: getting message with " + rejectedSeqNum + " sequence number from storage for session " + sender + "-" + target + ".");
getMsgBySeqNum(sender, target, Number(rejectedSeqNum));
listID = getStringField(66);
noOrders = getNumField(73);
ordersGrp = getGroup(73)
if ( (listID != null) && (noOrders > 0) && isGroupValid(ordersGrp) ) // check if message is valid
{
for (i=0; i < noOrders; ++i)
{
clOrdId = getStringField(ordersGrp, i, 11);
// ListID, ClOrdId are key fields. clOrdId should be not null.
if (clOrdId != null)
{
orderKeys = new Array( noOrders, clOrdId );
//print("[DEBUG] Removing record with listID=" + listID + " and clOrdId=" + clOrdId + " from 'Orders' table.");
// get data from history
records = removeRecordFromHistory ("Orders", orderKeys);
}
}
}
.png?version=1&modificationDate=1506704268075&cacheVersion=1&api=v2)