Routing Rules and Session Events: XML Transformation Language
Overview
Routing rules are main elements in FIXEdge configuration file. They contain FIXEdge instructions for:
- Routing of incoming and outgoing FIX messages (via the correspondence between ClientID and FIX Session).
- Filtering of incoming and outgoing FIX messages.
- Transformation of incoming and outgoing FIX messages
More than one rule can be applied to a message. It is also possible to route one message to multiple destinations or to the same destination multiple times. See section 'Cases of Routing Rules' for details. All rules are applied to each FIX message separately (independently) in the order set in the configuration file. Even if a set of transformation actions is applied to messages, the next rule will be applied to the original message. Details are described in the Rule element section.
Session Events are a special kind of business rules that handle events launched during execution. The following FIXEdge events can be processed:
- New session was created. See 'Element CreateSessionEvent'.
- Session was destroyed. See 'Element DestroySessionEvent'.
- Inability to route a message to a specified target. See 'Element OnUndeliveredMessageEvent'.
- Session level reject was received in response to the sent application message. See 'Element OnSessionLevelRejectEvent'.
- Session level reject was received with unexpectedly high sequence numbers. See 'Element OnSessionLevelRejectWithSeqNumTooHighEvent'.
- Session level reject was sent in response to the received application message. See 'Element OnOutgoingSessionLevelRejectEvent'.
- Routing rule failed during execution. See 'Element OnRuleFailEvent'.
Allowed memory usage is reached and exceeded. See 'Element OnNotificationEvent'.
Be careful, while Routing rules mechanism is very flexible, you can even implement loops, and, as a case infinite loops. For example, you create a route message from client "The client" - > "Some another client" - > "The client". And if "The client" is able to process its own message and output is the same or meet the rule above, you will take a risk to get infinite loop. To take more detailed description and ways to avoid such behavior, see the 'BL loops and finite control' section.
Rule element
Element <Rule> is meant to describe one Routing rule. Each <Rule> element consists of 3 sections (XML elements):
Name | Section Description |
---|---|
<Source> | Mandatory section. This section defines instructions for primary filtering of messages. <Source> element defines a source from which the FIX message can come. If the message source coincides with the source defined in the section <Source> then the rule will be applied to the message. See part 'Source element' for details. |
<Condition> | Optional section. This section contains a set of pre-defined criteria. The application checks FIX messages on the criteria and if the criteria is met the instructions are to be executed; otherwise section <Action> is ignored. See part 'Condition element' for details. |
<Action> | Mandatory section. The section denotes the instructions that must be performed when a Rule is being applied to a message. It must contain at least one instruction. See part 'Action element' for details. |
<Rule> element has two attributes:
Attribute | Description |
---|---|
Enabled | Optional. Possible values "true" or "false". <Rule Enabled=”false”> disables rule parsing by Business Layer and the rule is not loaded into the memory. By default the attribute value has a "true" value. |
Description | Optional. Used for naming of rules |
Example:
<Rule Enabled="true" Description="Save order data"> <Source> <!-- Source (FIX session or Transport Client) definition --> </Source> <Condition> <!-- conditions definition --> </Condition> <Action> <!-- actions definition --> </Action> </Rule>
Note: Any XML comments cannot be inside <Rule> structure, only outside the rule. If the formed rules do not meet the syntax requirements, FIXEdge stops loading and records an error message in the log file.
Source element
Source from which the FIX message can come for processing, defines either a FIX Session or a Transport Client (i.e. Transport Adaptor Session) by means of one of 3 variants:
by the element FIXSession
Defines FIX session as a source; FIX session is identified either by the pair of attributes "SenderCompID" and "TargetCompID" or by the set of attributes "SenderCompID", "TargetCompID" and "SessionQualifier".
Example:
<Source> <FixSession SenderCompID="Sender" TargetCompID="Target" SessionQualifier="Q1" /> </Source>
The values of the attributes have to coincide with values defined for properties: FixLayer.FixEngine.Session.<Name>.SenderCompID and FixLayer.FixEngine.Session.<Name>.TargetCompID (and optionally FixLayer.FixEngine.Session.<Name>.SessionQualifierValue) which were configured for FIX Sessions.
by the element Client
Defines Transport Client as source; The attribute "name" of the element <Client> is used
Example:
<Source> <Client Name="TestClient"/> </Source>
See also configuring Transport Adaptors (for example: property TransportLayer.SmtpTA.SMTPSessions)
by the attribute "Name"
Defines the identifier of FIX session or Transport Client as source; The attribute "name" of the element <Source> is used
Example:
<Source Name="SourceID"/>
Note: It is possible to use the rule for all configured sessions. In this case it is necessary to use pair of symbols ".*" in the attributes' values.
For example:
<!-- Messages from any transport adaptors will be processed --> <Source> <Client Name=".*"/> </Source> <!-- Messages from any FIX sessions related to TargetCompID="Target" will be processed --> <Source> <FixSession SenderCompID=".*" TargetCompID="Target"/> </Source>
Note: Source identifier has the following restrictions:
- The name of source identifier must be unique for each FIX session;
- The source identifier of TA client must be represented as Client ID;
- It must contain characters with codes from ASCII [30] to ASCII [128];
- It must begin with a letter, digit or underlining symbol ‘_’ ;
Whitespace characters in the beginning or in the end of name will be ignored by rule parsing.
Condition element
The <Condition> element is not required for the routing rule. But if the element is required, it may contain several criteria described below in this part. In fact, each criterion (specified as an XML element) is a function which can be applied to the whole FIX message as well as to particular fields of the FIX message. The function returns "true" or "false". Input variables of this function are defined via XML element's attributes: 'Field', 'Value', etc. <Condition> is met when all the criteria (functions) return "true". It means that condition criteria are joined by "AND".
Example:
<Rule> <Source Name=".*"/> <Condition> <!-- set of criteria, criteria are joined by "AND" --> <NotExistField Field="41" /> <MatchField Value="G|AC" Field="35"/> <EqualDestination Value="CME1" Strategy="OrderFlow100"/> </Condition> <Action> <!-- any action ... --> </Action> </Rule>
Condition element extensions
Combine conditions by "OR"
If it is necessary to join several criteria by "OR", another <Condition> element has to be added into the business rule.
Example: Condition 1 and Condition 2 are joined by "OR"
<Rule Description="Condition: ORed"> <Source> <FixSession SenderCompID="Target42" TargetCompID="Sender42" /> </Source> <Condition> <!-- Condition 1 --> <!-- set of criteria for Condition 1 --> <MsgType> <Val>F</Val> </MsgType> <MsgType> <Val>D</Val> </MsgType> </Condition> <Condition> <!-- Condition 2 --> <FieldLengthGreaterOrEqual Field="55" Length="3" /> </Condition> <Action> <!-- any set of actions --> </Action> </Rule>
The added <Condition> element can be empty, for example:
<Rule Description="Condition: ORed"> <Source> <FixSession SenderCompID="Target42" TargetCompID="Sender42" /> </Source> <Condition> <!-- Condition 1 --> <!-- set of criteria for Condition 1 --> <MsgType> <Val>F</Val> </MsgType> <MsgType> <Val>D</Val> </MsgType> </Condition> <Condition> <!-- Condition 2 --> <FieldLengthGreaterOrEqual Field="55" Length="3" /> </Condition> <Condition/> <!-- Condition 3 is empty --> <Action> <!-- any set of actions --> </Action> </Rule>
Inclusion and Exclusion blocks within Condition element
There are two XML-elements for grouping of criteria (functions) inside the <Condition>: <Inclusion> and <Exclusion>. XML-element <Exclusion> is used in the <Condition> to invert the result of set of criteria specified in the section <Exclusion>. I.e. <Exclusion> is a logical negation.
The element <Exclusion> can be used separately or in combination with <Inclusion>. The lement <Inclusion> is used for grouping of set of the rest criteria when <Exclusion> is used. Usage of <Inclusion> separately from <Exclusion> is not needed.
<Condition> <Inclusion> <!-- function A "AND" function B --> <A/> <B/> </Inclusion> <Exclusion> <!-- NOT (function C "AND" function D) --> <C/> <D/> </Exclusion> </Condition> <!-- Result = (A AND B) AND NOT (C AND D) -->
Examples:
<Condition> <EqualField Field="35" Value="D"/> </Condition> | Means (35 = “D”) |
<Condition> <Inclusion> <EqualField Field="35" Value="D"/> </Inclusion> </Condition> | Means the same as previous example |
<Condition> <Exclusion> <EqualField Field="55" Value="MSFT"/> <EqualField Field="40" Value="2"/> </Exclusion> </Condition> | Means NOT(55 = “MSFT” AND 40 = “2”) |
<Condition> <Inclusion> <EqualField Field="35" Value="D"/> </Inclusion> <Exclusion> <EqualField Field="55" Value="MSFT"/> <EqualField Field="40" Value="2"/> </Exclusion> </Condition> | Means (35=”D” AND NOT(55 = “MSFT” AND 40 = “2”)) |
Functions of Condition element
EqualField
Compares two values as strings and returns "true" when they are equal:
<EqualField Field="35" Value="D" />
Allowed to use a list of values for comparison. In this case xml-element <Val> has to be used, hereinafter "<Val> list". Returns "true" if given 'Field' is equal one of the value from given <Val> list:
<EqualField Field = "35"> <Val>D</Val> <Val>G</Val> <Val>F</Val> </EqualField>
NotEqualField
Compares two values as strings and returns "true" when they are not equal:
<NotEqualField Field="35" Value="asd"/>
Allowed to use a list of values for comparison. In this case the function returns "true" if the 'Field' is not equal to any value from the given <Val> list:
<NotEqualField Field = "35"> <Val>D</Val> <Val>G</Val> <Val>F</Val> </NotEqualField>
FieldIsLessThan
Compares two values as float numbers and returns "true" when given 'Field' less than 'Value':
<FieldIsLessThan Field="44" Value="100"/>
Absence of the tag defined by “Field” does not cause fail, but returns "false".
FieldIsGreaterThan
Compares two values as float numbers and returns "true" when given 'Field' greater than 'Value':
<FieldIsGreaterThan Field="44" Value="100"/>
Absence of the tag defined by 'Field' does not cause fail, but returns "false".
Note: Both of functions can be used for date comparison. For example:
<FieldIsLessThan Field="60" Value="20121231-10:00:00"/> <FieldIsGreaterThan Field="60" Value="20121231-10:00:00"/>
MatchField
Compares two values treating value as a regular expression and returns "true" when they are equal:
<MatchField Field="35" Value=".*"/>
NotMatchField
Compares two values as a regular expression and returns "true" when they are not equal:
<NotMatchField Field="35" Value="[1-9]$"/>
Note: It is recommended to use EqualField (NotEqualField) rather than MatchField (NotMatchField), when applicable. Although MatchField is more flexible, it may decrease the performance.
FieldContains
Returns "true" if string value in the given 'Field' contains one of the value from given <Val> list:
<FieldContains Field="18"> <Val>R</Val> <Val>T</Val> </FieldContains>
Optional attribute “Delimiter” can be used to treat the given 'Field' as multi value string. The “Delimiter” attribute contains the delimiter used in multi value string.
<FieldContains Field="58" Delimiter="|"> <Val>R</Val> <Val>T</Val> </FieldContains>
MsgType
Checks the FIX message type (tag = "35") , returns "true" when the current FIX message type is equal to one of the value from given <Val> list:
<MsgType> <Val>D</Val> <Val>G</Val> <Val>F</Val> </MsgType>
Note: Actually this function does the same as the functional EqualField but a bit faster.
FieldIsToday
Returns "true" when 'Field' contains date/time value in UTC and equals to current date:
<FieldIsToday Field = "42"/>
FieldBeginsWith
Checks if 'Field' begins with one of the substring from given <Val> list from the position that pointed out 'StartPos', and returns "true" when at least one coincidence is exists.
<FieldBeginsWith Field = "11" StartPos= "6" CaseSensitive= "true" <Val>L</Val> <Val>N</Val> <Val>BC</Val> </FieldBeginsWith>
'Field' and 'StartPos' attributes are required. The “StartPos” attribute refers the position within a string from 0.
'CaseSensitive' attribute is implied, default value = "true".
FieldLengthGreaterOrEqual
Returns "true" if the length of the 'Field' greater or equal to the 'Length' attribute. Both attributes are required:
<FieldLengthGreaterOrEqual Field="6780" Length="7"/>
FieldCheckBits
Treats the 'Field' as integer. Returns "true" if the 'Field' contains the HEX value defined by the 'Value' attribute. Bitwise "AND" operation is used within the check:
<FieldCheckBits Field="34" Value="0x02"/>
ExistField
Checks the 'Field' presence in FIX message and returns "true" if it exists:
<ExistField Field="35"/>
Allowed to use <Val> list for checking. Returns "true" when there is at least one field from given <Val> list in the FIX message:
<ExistField> <Val>11</Val> <Val>41</Val> </ExistField>
NotExistField
Checks the field presence in FIX message, returns "true" if it does not exist:
<NotExistField Field="58"/>
Allowed to use <Val> list for checking. Returns "true" when no any fields from given <Val> list in the FIX message:
<NotExistField> <Val>11</Val> <Val>391</Val> </NotExistField>
MatchMessage
Applies regular expression in attribute 'Value' to FIX message, returns "true" if regular expression is matched:
<MatchMessage Value=".*?55=10.*?"/>
NotMatchMessage
Applies regular expression in attribute 'Value' to FIX message, returns "true" if regular expression is not matched:
<NotMatchMessage Value=".*?55=test_1.*?"/>
IsSessionActive
Checks is the FIX Session Established, returns "true" if FIX Session is established:
<IsSessionActive Name="SESSION_NAME"/>
EqualDestination
Locates FIX message destination session according to the specified automatic routing strategy and returns "true" when the located destination session is equal to the one specified in action:
<EqualDestination Strategy="OrderFlowStrategy100" Value="Client"/>
NotEqualDestination
Locates FIX message destination session according to the specified automatic routing strategy and returns "true" when the located destination session is not equal to the one specified in action:
<NotEqualDestination Strategy="OrderFlowStrategy100" Value="Client"/>
MatchDestination
Locates FIX message destination session according to the specified automatic routing strategy and returns "true" when the located destination session is matched to the specified pattern:
<MatchDestination Strategy="OrderFlowStrategy100" Value="FIXSession.*"/>
NotMatchDestination
Locates FIX message destination session according to the specified automatic routing strategy and returns "true" when the located destination session is not matched to the specified pattern:
<NotMatchDestination Strategy="OrderFlowStrategy100" Value="FIXSession.*"/>
Script
This function is used when the criterion cannot be described via existed functions.
Applies JavaScript condition to the FIX message:
<Script Language="JavaScript" FileName ="testScript.js"/>
<!ELEMENT Script (Param)* > <!ATTLIST Script Language (Javascript | XSLT) #REQUIRED Field CDATA #IMPLIED LengthField CDATA #IMPLIED FileName CDATA #REQUIRED >
Detailed information can be found here: How to use JavaScript as a condition or action in the Business Rule
LDAPAuthenticate
Tries to login on LDAP server with given user/password and returns "true" if succeed otherwise "false". Can be used, for example, to authenticate a session via LDAP.
Available starting FIXEdge 5.10
<Condition> <LDAPAuthenticate URL="ldaps://ldapserver.domain.com:636" TemplateDN="cn=%USER%,dc=my-domain,dc=com" UserField="553" PasswordField="554" /> </Condition>
where
XML Attrubute | Required/ default value | Description |
---|---|---|
URL | required | LDAP server URL |
TemplateDN | required | Template of LDAP Distinguished Name to bind to. The %USER% mark will be replaced with contents of UserField |
UserField | "553" | User tag |
PasswordField | "554" | Password tag |
Action element
The section denotes instructions (as XML elements) that must be performed when a Rule is being applied to a message. <Action> is a required section and must contain at least one of the instructions described below. Each instruction is a command executed with the given message. Before applying the instructions, the FixEdge creates a copy of the initial FIX message, and the instructions are applied to this copy of the FIX message.
The instructions are applied to the copy sequentially, in order set in <Action> element. If the copy of the FIX message was transformed by the one instruction, the next instruction is applied to the changed message. So, the copy of the FIX message can be transformed step by step by applying the instructions, and then the transformed message can be sent to the required destination.
If the instruction (command) cannot be executed then the error will occur.
ActionIfTrue and ActionIfFalse elements
<ActionIfTrue> and <ActionIfFalse> xml-elements can be used instead of <Action> section. These elements allow user to define the processing of incoming message when <Condition> = true as well as when <Condition> = false:
<Rule> <Source> <!-- ... --> </Source> <Condition> <EqualField Field="35" Value="D"/> </Condition> <Action> <SetField Field="50" Value="Updated_message"/> <CopyField SourceField="49" TargetField="9999" IsRequiredField="Y"/> </Action> </Rule> | Using <Action> element: Instructions in <Action> section will be applied to FIX message only when <Condition>=true |
<Rule> <Source> <!-- ... --> </Source> <Condition> <EqualField Field="35" Value="D"/> </Condition> <ActionIfTrue> <SetField Field="50" Value="Updated_message"/> <CopyField SourceField="49" TargetField="9999" IsRequiredField="Y"/> </ActionIfTrue> </Rule> | Using <ActionIfTrue> element: It means the same as using <Action> element in the previous example. |
<Rule> <Source> <!-- ... --> </Source> <Condition> <EqualField Field="35" Value="D"/> </Condition> <ActionIfTrue> <SetField Field="50" Value="Updated_message"/> <CopyField SourceField="49" TargetField="9999" IsRequiredField="Y"/> </ActionIfTrue> <ActionIfFalse> <CopyField SourceField="56" TargetField="49" IsRequiredField="Y"/> <MoveField SourceField="9999" TargetField="56"/> </ActionIfFalse> </Rule> | Using <ActionIfTrue> and <ActionIfFalse> jointly: Instructions in <ActionIfTrue> section will be applied to FIX message when <Condition>=true. When <Condition>=false the instructions in <ActionIfFalse> section will be applied to FIX message |
<Rule> <Source> <!-- ... --> </Source> <Condition> <EqualField Field="35" Value="D"/> </Condition> <ActionIfFalse> <CopyField SourceField="56" TargetField="49" IsRequiredField="Y"/> <MoveField SourceField="9999" TargetField="56"/> </ActionIfFalse> </Rule> | Using <ActionIfFalse> element: Only one <ActionIfFalse> block is also allowed |
Session instructions
StartSession
Executes sync FIX session create/connect by means of one of 3 variants:
by SenderCompID/TargetCompID which are taken from routed message:
<StartSession />
by specified SenderCompID/TargetCompID:
<StartSession SenderCompID="Sender" TargetCompID="Target" />
by specified SenderCompID/TargetCompID/SessionQualifier :
<StartSession SenderCompID="Sender" TargetCompID="Target" SessionQualifier="Q1" />
by the session name:
<StartSession Name="SESSION_NAME" />
DisconnectSession
Executes async FIX session disconnect:
by SenderCompID/TargetCompID which are taken from routed message:
<DisconnectSession Reason="Manual disconnect" />
by specified SenderCompID/TargetCompID:
<DisconnectSession SenderCompID="Sender" TargetCompID="Target" Reason="Manual disconnect" />
by the session name:
<DisconnectSession Name="SESSION_NAME" Reason="Manual disconnect" />
TerminateSession
Executes async FIX session terminate:
by SenderCompID/TargetCompID which are taken from routed message:
<TerminateSession Reason="Manual disconnect" />
by specified SenderCompID/TargetCompID:
<TerminateSession SenderCompID="Sender" TargetCompID="Target" Reason="Manual disconnect" />
by the session name
<TerminateSession Name="SESSION_NAME" Reason="Manual disconnect" />
Note please:
- The session will not be visible in FIXICC or manageable (start, disconnect, etc) from FIXICC/BL/JS/scheduler.
- The termination and the disconnection occur with async way after rule completion. In case of fail <OnRuleFailEvent> will be called.
Sending instructions
Send
Sends a FIX message to a specified destination. Destination, like the <Source> element, can define FIX Session or a Transport Client (i.e. Transport Adaptor Session) by means of one of 3 variants:
- by the element FIXSession
FixSession is identified by SenderCompID and TargetCompID pair or by SenderCompID/TargetCompID/SessionQualifier set. If parameter is not specified, it is taken from corresponding tag of the routed message:
<Send><FixSession SenderCompID="Sender" TargetCompID="Target"/></Send>
<Send><FixSession SenderCompID="Sender" </Send> <!—TargetCompID is taken from Tag56 -->
<Send><FixSession TargetCompID="Target"/></Send> <!—SenderCompID is taken from Tag49 -->
<Send><FixSession /></Send> <!—SenderCompID is taken from Tag49, TargetCompID is taken from Tag56 -->
- by the element Client (for Transport Adaptors only!)
<Send> <Client Name="TestClient"/> </Send>
- by the attribute "Name" (for FIX Sessions only!)
<Send Name="TestClient" />
SystemCommand
Executes system command or script.
Executed only together with next Send action. Receives FIX message as input, output FIX message should be stored to the sysCmdOut__05311141284571.msg file in the bin directory. The file is temporary and will be removed after the action completed.
<SystemCommand Command="runSystemCommand.bat"/> <Send> <!-- ... --> </Send>
StrategySend
Applies a specified automatic routing strategy to the handled message, locates a destination session and sends the message to the required session:
<StrategySend Name="OrderFlowStrategy100"/>
See Routing strategies description for details.
Instructions for modifying Fields
SetField
Assigns the given 'Value' to a specified 'Field'. If the 'Field' is not present in a FIX message, it is created with given value:
<SetField Field="50" Value="Ordinary Sender SubID"/>
CopyField
Copies the value from 'SourceField' to 'TargetField'. If the 'TargetField' is not present in a FIX message, it is created with given value. "CopyField" action can have several attributes:
Attribute | Required | Valid Values | Default Value | Description |
---|---|---|---|---|
IsRequiredField | N | Y / N | N |
|
DefaultValue | N | - | - | To be specified in case "IsRequiredField" is set to "N" |
Method | N |
Precision methods are available since FIXEdge 6.2.0 | - | The optional attribute 'Method' is used for specifying of conversion during copying:
Note that precision methods are intended to be applied to the tags which contain timestamp values (i.e. which have the formats UTCTimestamp , UTCTimeonly , TZTimestamp , TZTimeOnly). It is not recommended to use them with any other type of fields. |
MoveField
Moves the value from 'SourceField' to 'TargetField'. If the 'TargetField' is not present in a message, it is created with given value.
<MoveField SourceField="50" TargetField="53"/>
RemoveField
Removes a specified 'Field' from FIX message:
<RemoveField Field="50"/>
ModifyField
Applies a specified plug-in method to the specified 'Field' from FIX message:
<ModifyField Field="11" Rule="Brass.ClOrdId.Converter.Brass2Source()" />
DecryptField
Decrypts value of a specified 'Field'. Do nothing if the 'Field' is not present in a FIX message.
<DecryptField Field="554"/>
HashField
Calculates SHA256 has for the value of a specified 'Field'. Value represents hex valued string. Do nothing if the 'Field' is not present in a FIX message.
<HashField Field="554"/>
Instructions for work with Histories
SaveToHistory
Stores data from the handled message to history storage:
<SaveToHistory Name="Orders"/>
ExpireDateTime can't be set using the SaveToHistory XML action, therefore <expireDateTime> will be treated (if it is defined in the History definition) as NULL, and the ClearHistory procedure will delete all such records.
ClearHistory
Starts history storage clearing, obsolete records are erased:
<ClearHistory Name="Orders"/>
The records for which <expireDateTime> is either less than or equal to the time of clear procedure start or NULL (not defined) are marked as obsolete.
If <expireDateTime> is not set in the History definition, then the ClearHistory procedure won't be even started and no record will be deleted and the following ERROR message will be logged:
ERROR [BL_Action] 140334600271616 Action 'ClearHistory' for History <History_Name> failed. Reason: History <History_Name> unable to clear: the ExpireDateTime field is not defined!
Instructions for message transformation
ProcessNetStatusRequest
Creates a corresponding NetStatusRespone FIX message for the handled NetStatusRequest message:
<ProcessNetStatusRequest/>
Transform
Makes a new FIX message of a specified message type based on a source FIX message:
<Transform TargetMsgType="9"/>
<!ELEMENT Transform (SetField | CopyField)*> <!ATTLIST Transform TargetMsgType CDATA #REQUIRED TargetProtocol (FIX.4.0 | FIX.4.1 | FIX.4.2 | FIX.4.3 | FIX.4.4 | FIX.5.0) #IMPLIED >
Note: The following commands are supported by the 'Transform' action: SetField | CopyField. For example:
<Transform TargetMsgType="3"> <CopyField SourceField="..." TargetField="..." IsRequiredField="..." /> <SetField Field="..." Value="..."/> </Transform>
Since FIXEdge 5.11.2 the Transform action supports the attribute TargetProtocol in the Session.Version format (examples: FIXT11:FIX50, FIXT11custom:FIX50custom).
Convert
Converts a FIX message to a specified FIX protocol dialect:
<Convert TargetProtocol="FIX.4.3" SourceProtocol="FIX.4.0"/>
Attribute 'TargetProtocol' is mandatory, attribute 'SourceProtocol' is optional.
Both of them can take one of the following values:
- FIX Versions: FIX.4.0, FIX.4.1, FIX.4.2, FIX.4.3, FIX.4.4, FIX.5.0, FIX.5.0.SP1, FIX.5.0.SP2, FIXLatest
- Custom FIX Versions: ARCA_FIX.4.1, AUBREY_FIX.4.2, PHLX_FIX.4.2, PHEQ_FIX.4.2, ISE_FIX.4.2, NYSE_FIX.4.2, SIGSRS_FIX.4.2, CME_FIX.4.2, BLOOMBERG_FIX.4.4, RENC_FIX.4.4, EBS_FIX.5.0
- FIXML Versions: FIXML.4.1, FIXML.4.2, FIXML.4.3, FIXML.4.4, FIXML.5.0, FIXML.5.0.SP2, FIXML.FIXLatest
- Custom FIXML Versions: CME_FIXML.4.4, LCH_FIXML.4.2, OSLO_FIXML.5.0.SP2
FIXEdge C++ has the capability to convert FIXML to FIX, with a FIX version that is compatible with the FIX protocol.
- on reading from FIXML - read FIX version with aliases/heuristics to correctly handle standard, and to maintain backward compatibility.
- on writing to FIXML - write FIX version as defined in standard FIXML Versioning Attributes.
The default converting procedure implies:
- the coinciding fields (in the source and in the target format) are used without changing,
the excess fields in the source will not be used,
the absent fields in the target have to be created and filled by default values.
Note: User can change the default converting procedure by means of adding the following commands: SetField | MoveField | RemoveField | ModifyField. These commands will be executed after the commands of the default converting procedure. For example:
<Convert TargetProtocol="FIX.4.3" SourceProtocol="FIX.4.0"> <SetField Field="..." Value="..."/> <RemoveField Field="..."> </Convert>
Information about composing the FIXML message header can be found here: FIX trading FIXML standard, For example, ApplVerID (1128) tag is mapped to "v" attribute.
Note: MsgType (35) is mapped to FIML root element, for 35=AE it is <TrdCaptRpt />
HandlerAction
Handles a FIX message by the defined handler.
<HandlerAction Name="TestHandler" />
See a description of existing handlers in the 'BL Handlers C++ API'.
CreateReject
Creates a corresponding reject message for the routed FIX message.
<CreateReject RejectType="session"/>
Mandatory attribute 'RejectType' can take the following values: "application" and "session". The attribute defines a level of error that was found in the FIX message and caused a reject message. If 'RejectType'="application", it means that the FIX message has a logical error, otherwise the FIX message has an error at the "session" level.
All subsequent instructions located after <CreateReject> will be applied to the created reject message instead of the routed message, for example:
<Action> <!-- ... --> <CreateReject RejectType="session" CreateBusinessRejectIfFailed="false"/> <Send> <!-- reject message will be sent --> <FixSession SenderCompID="Sender" TargetCompID="Target"/> </Send> </Action>
Note: <CreateReject> has optional attribute 'CreateBusinessRejectIfFailed' that can take the following values: "true" and "false". When 'CreateBusinessRejectIfFailed' = "true", it means that the another special message has to be created if any error is occured during the execution of <CreateReject> instruction.
The special message has the following format: 35=j|372=tag '35' from the incoming message for <CreateReject>|380=0|58=the error description|45=tag '34' from the incoming message for <CreateReject>
ConvertToBusinessReject
Converts SessionLevelReject FIX message into the corresponding BusinessReject FIX message:
<ConvertToBusinessReject/>
Script
Executes instructions from XSLT file with given parameters:
<Script Language="XSLT" Field="213" LengthField="212" FileName="file.xsl"> <Param Name="Acct">JBDFLYX</Param> </Script>
CreateNotification
Creates a notification Email FIX message with specified parameters:
<CreateNotification Category="N" Reason="Test Reason"/>
Attribute 'Reason' contains a body of the message sent by in e-mail, attribute 'Category' contains ... Both of attributes are mandatory.
StopProcessing
The action just stops processing of current FIX message. The rest of the rules will be skip:
<StopProcessing/>
Generate a RuleFailEvent event with given ResultCode and Description:
<StopProcessing ResultCode="94" Description="Required tag missing: Price" />
In this example the <StopProcessing> action from the "FailRule" rule generates an Action Fail. The <SetErrorToField> instruction maps ResultCode and Description from the <StopProcessing> action to OrdRejReason (103) and Text(58) in the generated Reject message. FIXEdge generates RuleFailEvent event in this case too.
<Rule Name="FailRule"> <ActonIfTrue> <StopProcessing ResultCode="94" Description="Required tag missing: Price"/> </ActionIfTrue> <OnActionFail> <CreateReject RejectType="application"/> <SetErrorToField ResultCodeTag="103" DescriptionTag="58"/> <Send> <FixSession SenderCompID="FIXEDGE" TargetCompID="EXCH44"/> </Send> </OnActionFail> </Rule>
FormatSymbol
Modifies the value of Symbol (tag 55) and SymbolSfx (tag 65) tags.
The action is able to:
merge Symbol and SymbolSfx tags into the value of Symbol tag (55):
<FormatSymbol ActionType="Assemble" TrgSeparator="_" />
split the value of Symbol (55) tag into Symbol (55) and SymbolSfx (65) tags:
<FormatSymbol ActionType="Split" SrcSeparator="_" />
convert the separator in merged Symbol (55) tag:
<FormatSymbol ActionType="Convert" SrcSeparator="_" TrgSeparator="." />
Cases of Routing Rules
FIXEdge router provides the user with a possibility to establish a set of Business Rules that enables FIXEdge with:
- Routing of incoming and outgoing FIX messages (via the correspondence between ClientID and FIX Session).
- Filtering of incoming and outgoing FIX messages.
- Transformation of incoming and outgoing FIX messages.
All 3 functions are accomplished through the customizable (user-defined) Rules in XML format, placed in the Business Layer configuration file. This means that more than one rule can be applied to a message, see Rules processing order for details. More information about routing rules can be found in FixEdge_RoutingRules_UserGuide.html.
Routing rules for incoming messages from FIX session
While routing an incoming message the Rule must have <FixSession> source in its structure. Also, there must be at least one instruction in the Action section. On demand, user can choose and specify the condition.
<Rule> <Source> <FixSession SenderCompID="TestSender" TargetCompID="TestTarget"/> </Source> <Condition> <MatchField Field="35" Value=".*"/> </Condition> <Action> <Send> <Client Name="TestClient"/> </Send> </Action> </Rule>
Routing rules for incoming messages from TA client
The routing approach for handling the incoming messages from TA client is the same as for incoming messages from FIX sessions, except for one difference: the source is always a Client ID. FIX messages from TA clients are routed to the requisite FIX sessions based on the SenderCompID and TargetCompID fields.
Example:
<Rule> <Source> <Client Name="TestClient"/> </Source> <Condition> <MatchField Field="35" Value=".*"/> <MatchField Field="571" Value="IST-\\d+"/> </Condition> <Action> <Send> <FixSession SenderCompID="TestSender" TargetCompID="TestTarget"/> </Send> </Action> </Rule>
It is possible to send a message to destinations denoted as the SenderCompID and TargetCompID in the message itself. You can do this by omitting SenderCompID and TargetCompID attributes in FixSession tag.
<Rule> <Source> <Client Name="TestClient"/> </Source> <Action> <Send> <FixSession/> </Send> </Action> </Rule>
Routing with source identifiers
Source identifiers can be used as “Name” attribute of <Source> tag as well as “Name” attribute of <Send> tag. The following example demonstrates the routing of an incoming message from the session identified as “TestSource” to destination with “TestTarget” identifier; only messages that satisfy <MatchField> conditions are included:
<Rule> <Source Name = "TestSource"/> <Condition> <MatchField Field="35" Value="D"/> </Condition> <Action> <Send Name="TestTarget"/> </Action> </Rule> <--! Send reject on back-way if source "TestTarget" are disconnected --> <OnUndeliveredMessageEvent> <Source Name= "TestTarget" /> <Action> <CreateReject RejectType="application"/> <Send Name="TestSource" /> </Action> </OnUndeliveredMessageEvent>
Routing to multiple destinations
The sending rule is able to send a message to multiple destinations by one <Send> action as well. In this case destinations can be enumerated inside the action body as “FixSession”, “Client”:
<Rule> <Source> <Client Name=".*"/> </Source> <Action> <Send> <FixSession SenderCompID="Sender1" TargetCompID="Target1"/> <FixSession SenderCompID="Sender2" TargetCompID="Target2"/> <Client Name="TestClient1"/> <Client Name="TestClient2"/> </Send> </Action> </Rule>
Below you can see the example of routing to multiple destinations identified by source identifiers (e.g. “Name” attribute of <Send> tag):
<Rule> <Source Name=".*"/> <Action> <Send Name="Session1"/> <Send Name="ClientIdentifer"> <Client Name="TestClient1"/> <FixSession SenderCompID="Sender2" TargetCompID="Target2"/> </Send> <Action> </Rule>
Transformation rules
You can use SetField, Convert, MoveField, RemoveField, or Script tags to change a message.
Example:
<Rule> <Source> <Client Name="TestClient"/> </Source> <Condition> <MatchField Field="35" Value=".*"/> <MatchField Field="571" Value="IST-\\d+"/> </Condition> <Action> <MoveField SourceField="32" TargetField="48" /> <Script Language="JavaScript" FileName="sc_2_3.js" /> <Convert TargetProtocol="FIX.4.3" SourceProtocol=""> <SetField Field="35" Value="abc" /> <RemoveField Field="50" /> </Convert> </Action> </Rule>
If more than one transformation is specified the next transformation is applied to the result of the previous transformation.
Mixed rules
There are no restrictions for the number of Action elements (acts) of different types. In this case they are executed in the order of appearance.
Example:
<Rule> <Source> <Client Name="TestClient"/> </Source> <Condition> <MatchField Field="35" Value="C"/> </Condition> <Action> <SetField Field="57" Value="TargetSubID"/> <Send> <FixSession SenderCompID="TestSender" TargetCompID="TestTarget"/> </Send> <SetField Field="57" Value="AdminTargetSubID"/> <Send> <FixSession SenderCompID="TestSender" TargetCompID="AdminTestTarget"/> </Send> </Action> </Rule>
This will result in the scenario below:
- Message is duplicated: SrcMsg → DupMsg
- For DupMsg: Field 57 is set to "TargetSubID"
- DupMsg is set to the defined FIX Session ID
- Message is duplicated: DupMsg → DupDupMsg
- For DupDupMsg: Field 57 is set to "AdminTargetSubID"
- DupDupMsg is set to the defined FIX Session ID
Session Events
Element CreateSessionEvent
FIXEdge uses <CreateSessionEvent> to handle given FIX message (35=A, logon) when a new session is created.
The event should be used only for acceptors.
The XML element CreateSessionEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<CreateSessionActionIfTrue> / <CreateSessionAction> | Mandatory section. The section denotes the instructions that must be performed if condition is true. The node <CreateSessionActionIfTrue> is equivalent to <CreateSessionAction>. |
<CreateSessionActionIfFalse> | Optional section. The section denotes the instructions that must be performed if condition is false. |
Note: In versions of FIXEdge earlier than 6.4.0 incoming session was accepted when CreateSessionEvent finished with errors. Since version 6.4.0 incoming session is not accepted in case of errors in condition or some failed actions, e.g. javascript.
Example:
<CreateSessionEvent Description="Authenticate incoming connections"> <Source> <FixSession SenderCompID=".*" TargetCompID=".*" /> </Source> <Condition> <!-- here can be some condition that can fail, e.g. --> <FieldIsToday Field = "42"/> </Condition> <CreateSessionAction> <!-- here can be some action that can fail (handler, script, common action, etc) --> <Script Language="JavaScript" FileName ="bad_test.js"/> <AcceptSession/> </CreateSessionAction> </CreateSessionEvent>
In this example, the first condition is failed due to the fact that there is no 42 tag in Logon message. Also, javascript can cause a failure of the action. Both of these will end up with unaccepting further incoming sessions.
CreateSessionActionIfTrue(-IfFalse) element
The following instructions are allowed:
<AcceptSession /> | AcceptSessionAccepts session creation. During session acceptance FIXEdge creates new internal FIX message (35=C) and sends this message into business layer to notify about creation of Session. | CreateSessionActionIfTrue (-IfFalse) section must contain either 'AcceptSession' element or 'RejectSession' element and only once. |
<RejectSession>Reason</RejectSession> | RejectSessionDeclines session creation without sending Logout message. Reason will be logged by engine as error. | |
<HandlerAction Name="UserHandler"/> | HandlerActionRoutes message into a handler. | 'CreateSessionActionIfTrue (-IfFalse)' element can contain zero or more occurrences of 'HandlerAction' or 'Script ' elements. |
<Script Language="JavaScript" FileName ="testScript.js"/> | ScriptApplies JavaScript condition to message. | |
<DecryptField Field="554"/> | DecryptFieldDecrypt encrypted Field. Available since FIXEdge 5.10.1 | 'CreateSessionActionIfTrue (-IfFalse)' element can contain zero or more occurrences of 'DecryptField' or 'HashField' elements. |
<HashField Field="925"/> | HashFieldCalculate SHA256 hash for specified field. Values are trasform to hex string. Available since FIXEdge 5.10.1 |
Example:
<FIXEdge> <BusinessLayer> <CreateSessionEvent Description="Event: Session Accept"> <Source> <FixSession SenderCompID="Target" TargetCompID="Sender" /> </Source> <CreateSessionActionIfTrue> <AcceptSession/> </CreateSessionActionIfTrue> <CreateSessionActionIfFalse> <RejectSession>Reason</RejectSession> </CreateSessionActionIfFalse> </CreateSessionEvent> <Rule> <Source> <FixSession SenderCompID=".*" TargetCompID=".*" /> </Source> <Condition> <EqualField Field="35" Value="C"/> <EqualField Field="147" Value="[NOTE] Sender:Target Established"/> </Condition> <Action> <Send> <FixSession SenderCompID="TRGT" TargetCompID="SNDR"/> </Send> </Action> </Rule> <DefaultRule> <Action> <DoNothing/> </Action> </DefaultRule> </BusinessLayer> </FIXEdge>
| 1.) FIX message (type Logon) initiates the new FIX session (SenderCompID="Target" TargetCompID="Sender"). 2.) FIXEdge handles this message via <CreateSessionEvent Description="Event: Session Accept">:
3.) FIX message (35=C) is handled by FIXEdge Business layer as described in <Rule>, i.e. the message is sent to FixSession (SenderCompID="TRGT" TargetCompID="SNDR") |
Element DestroySessionEvent
FIXEdge uses <DestroySessionEvent> to handle given FIX message (35=5, logout) when the session is closed.
The XML element DestroySessionEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<DestroySessionAction> | Mandatory section. The section denotes the instructions that must be performed when session is closed |
DestroySessionAction element
The following instructions are allowed in <DestroySessionAction>:
<HandlerAction Name="UserHandler"/> | HandlerActionRoutes message into a handler: |
<Script Language="JavaScript" FileName ="testScript.js"/> | ScriptApplies JavaScript condition to message: |
'DestroySessionAction' element can contain zero or more occurrences of 'HandlerAction' or 'Script' elements. But at least one instruction has to be described.
Example:
<FIXEdge> <BusinessLayer> <DestroySessionEvent Description="Event: Destruction of the session"> <Source> <FixSession SenderCompID="Target42" TargetCompID="Sender42" /> </Source> <DestroySessionAction> <Script Language="JavaScript" FileName="../FixEdge/conf/test.js" /> </DestroySessionAction> </DestroySessionEvent> <DefaultRule> <Action> <DoNothing /> </Action> </DefaultRule> </BusinessLayer> </FIXEdge>
Element OnNotificationEvent
So far there is only one case when notification event is generated at BL in FIXEdge - if the allowed memory usage is reached and exceeded.
In this case FIXEdge creates a new internal FIX message (35=C | 49=fake | 56=fake ) and sends this message into business layer to notify about exceeding the available memory.
The XML element OnNotificationEvent rule consists of the sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. It is used to catch the newly created internal FIX message (35=C) |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. Contains handling instructions with internal FIX message (35=C) |
Example:
<?xml version="1.0" encoding="UTF-8"?> <FIXEdge> <BusinessLayer> <OnNotificationEvent> <Source> <FixSession SenderCompID=".*" TargetCompID=".*" /> </Source> <Condition> <!-- Catching of new internal FIX message --> <EqualField Field="35" Value="C"/> <EqualField Field="49" Value="fake"/> <EqualField Field="56" Value="fake"/> </Condition> <Action> <!-- Handling of new internal FIX message --> <Send> <Client Name="SMTPClient"/> </Send> </Action> </OnNotificationEvent> <DefaultRule Description="Executed if no other rule is applied"> <Action> <DoNothing/> </Action> </DefaultRule> </BusinessLayer> </FIXEdge>
To provide valid configuration for this functionality MemoryWatch.Limit and MemoryWatch.Interval properties should be specified in FIXEdge.properties file. Note, this functionality is currently available only for Windows version of FIXEdge.
Element OnUndeliveredMessageEvent
OnUndeliveredMessageEvent is triggered when FIXEdge can't deliver the FIX message to a specified destination (FIX session or client):
- The recipient is unknown to FIXEdge (e.g. when the session is not set up in the FIXEdge.properties file);
- The session is set up in the FIXEdge.properties file but is not scheduled to run at the current moment;
- The FIX message is to be delivered to Transport Adapter client, but the client is not logged in.
The XML element <OnUndeliveredMessageEvent> rule consists of the sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. Here the user specifies the selection conditions of the failed message. |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. Here the user specifies the actions for delivering the failed message. |
Example:
<OnUndeliveredMessageEvent> <Source> <FixSession TargetCompID=".*" SenderCompID=".*"/> </Source> <Condition> <EqualField Value="C" Field="35"/> </Condition> <Action> <Send> <Client Name="UndeliveredEmailClient"/> </Send> </Action> </OnUndeliveredMessageEvent> <!-- Routing rule for OnUndeliveredMessage Events for New Orders: used source identifiers both to Fix and Transport Layers, then sends --> <OnUndeliveredMessageEvent> <Source Name=".*"/> <Condition> <MatchField Value="D|E|AB|s" Field="35"/> </Condition> <Action> <Send Name="UndeliveredOrdersExchange"/> </Action> </OnUndeliveredMessageEvent>
To provide valid configuration for this functionality "<Send Name="UndeliveredOrdersExchange" and "Client Name="UndeliveredEmailClient"/" properties should be specified in FIXEdge.properties file.
As of the FEIXEdge C++ 6.11.0 release, the REST Out Transport Adapter can provide information about the error that generated an OnUndeliveredMessageEvent. Inside this event, this information can be accessed using the JavaScript functions getErrorCode and getErrorText to obtain the error values.
Element OnSessionLevelRejectEvent
FIXEdge launches a session-level reject event in case the SessionLevelReject FIX message is received.
The XML element OnSessionLevelRejectEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. |
Example:
<OnSessionLevelRejectEvent> <Source> <FixSession TargetCompID=".*" SenderCompID=".*"/> </Source> <Action> <ConvertToBusinessReject/> <Send> <Client Name="RejectClient"/> </Send> </Action> </OnSessionLevelRejectEvent>
To provide a valid configuration for this functionality Client Name="RejectClient"/>" properties should be specified in the FIXEdge.properties file.
Element OnSessionLevelRejectWithSeqNumTooHighEvent
This event is available since the FIXEdge C++ 6.16.0 version.
FIXEdge C++ launches this event when the Reject(35=3) message is received with the MsgSeqNum(34) tag value higher than expected.
The XML element OnSessionLevelRejectWithSeqNumTooHighEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. |
Example:
<OnSessionLevelRejectWithSeqNumTooHighEvent> <Source> <FixSession SenderCompID=".*" TargetCompID=".*"/> </Source> <Condition> <ExistField Field="5024"/> </Condition> <Action> <Script Language="JavaScript" FileName ="FIXEdge1/conf/cmeCase.js"/> </Action> </OnSessionLevelRejectWithSeqNumTooHighEvent>
Element OnOutgoingSessionLevelRejectEvent
Available since FIXEdge 6.12.0
The FIXEdge launches a session-level reject event in case the SessionLevelReject FIX message is sent.
The XML element OnOutgoingSessionLevelRejectEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. |
Example:
<OnOutgoingSessionLevelRejectEvent> <Source> <FixSession TargetCompID=".*" SenderCompID=".*"/> </Source> <Action> <ConvertToBusinessReject/> <Send> <FixSession SenderCompID="" TargetCompID=""/> </Send> </Action> </OnOutgoingSessionLevelRejectEvent>
To identify sessions with the same SenderCompId / TargetCompId the <SessionQualifier> property should be specified in engine.properties file.
Element OnRuleFailEvent
FIXEdge launches a routing rule failed event in case of BL Rule failure during message handling, when FIXEdge is unable to route message according to BL Rule.
Possible causes include, but are not limited to the following:
- JavaScript fails;
- the BL Rule tries to access file which does not exist;
- database error while working with history tables.
The XML element OnRuleFailEvent rule consists of the following sections:
Name | Section Description |
---|---|
<Source> | Mandatory section. This section has the same structure as the <Source> of the Rule xml-element. |
<Condition> | Optional section. This section has the same structure as the <Condition> of the Rule xml-element. |
<Action> | Mandatory section. This section has the same structure as the <Action> of the Rule xml-element. |
Example:
<OnRuleFailEvent> <Source> <FixSession TargetCompID="UnregisteredTarget" SenderCompID="UnregisteredSender"/> </Source> <Condition> <EqualField Value="A" Field="35"/> </Condition> <Action> <SystemCommand Command="runSystemCommand.bat"/> </Action> </OnRuleFailEvent>
BL loops and finite control
As was said in 'Overview' part, there is possible to make loops, and infinite loops.
Following example is a sample of infinite loop:
<OnUndeliveredMessageEvent> <Source Name=".*"/> <Action> <CreateNotification Category="E" Reason="Undelivered Message Event" /> <Send> <Client Name="TestSMTPClient"/> </Send> </Action> </OnUndeliveredMessageEvent>
In this snippet created rule declares such behaviour: "For each undelivered message create notification and send it to 'TestSMTPClient'". But what will happen if there will be undelivered message from 'TestSMTPClient'? Notification will be send to 'TestSMTPClient', but this client only resends corresponding messages in other format, so if there was not delivered previous message, with great probability next message won't be sent too, and UndeliveredMessageEvent will be fired again, and this sequence will be repeated in a loop, which can be infinite.
The simplest way to avoid such situations is to exclude target client by it's name. So while Source.Name parameter takes regular expression with perl syntax we can do it following way:
<OnUndeliveredMessageEvent> <Source Name="^(?!TestSMTPClient\b)(.*)$"/> <Action> <CreateNotification Category="E" Reason="Undelivered Message Event" /> <Send> <Client Name="TestSMTPClient"/> </Send> </Action> </OnUndeliveredMessageEvent>
Now all undelivered messages from all clients, except 'TestSMTPClient' will be processed by this rule.
<Transform TargetMsgType="9"/>