How to log QuickFix message in human-readable format
I want to log QuickFix messages in sort of parsed mode like tagname,Value
I cannot find existing functionality. I am using QuickFix.Net.
I was thinking about providing some sort of metho开发者_C百科d that would iterate through all presenting tags and parse them using data dictionary.
There is a Java sample http://www.quickfixj.org/confluence/display/qfj/Using+Message+Metadata that can be converted to .NET and that may prove to be useful:
public class MetadataExample {
public static void main(String[] args) throws Exception {
DataDictionary dd = new DataDictionary("FIX44.xml");
Message m = new Message("8=FIX.4.4\0019=247\00135=s\00134=5\001"
+ "49=sender\00152=20060319-09:08:20.881\001"
+ "56=target\00122=8\00140=2\00144=9\00148=ABC\00155=ABC\001"
+ "60=20060319-09:08:19\001548=184214\001549=2\001"
+ "550=0\001552=2\00154=1\001453=2\001448=8\001447=D\001"
+ "452=4\001448=AAA35777\001447=D\001452=3\00138=9\00154=2\001"
+ "453=2\001448=8\001447=D\001452=4\001448=aaa\001447=D\001"
+ "452=3\00138=9\00110=056\001", dd);
MessagePrinter printer = new MessagePrinter();
printer.print(System.out, dd, m);
}
}
public class MessagePrinter {
public void print(DataDictionary dd, Message message) throws FieldNotFound {
String msgType = message.getHeader().getString(MsgType.FIELD);
printFieldMap("", dd, msgType, message.getHeader());
printFieldMap("", dd, msgType, message);
printFieldMap("", dd, msgType, message.getTrailer());
}
private void printFieldMap(String prefix, DataDictionary dd, String msgType, FieldMap fieldMap)
throws FieldNotFound {
Iterator fieldIterator = fieldMap.iterator();
while (fieldIterator.hasNext()) {
Field field = (Field) fieldIterator.next();
if (!isGroupCountField(dd, field)) {
String value = fieldMap.getString(field.getTag());
if (dd.hasFieldValue(field.getTag())) {
value = dd.getValueName(field.getTag(), fieldMap.getString(field.getTag())) + " (" + value + ")";
}
System.out.println(prefix + dd.getFieldName(field.getTag()) + ": " + value);
}
}
Iterator groupsKeys = fieldMap.groupKeyIterator();
while (groupsKeys.hasNext()) {
int groupCountTag = ((Integer) groupsKeys.next()).intValue();
System.out.println(prefix + dd.getFieldName(groupCountTag) + ": count = "
+ fieldMap.getInt(groupCountTag));
Group g = new Group(groupCountTag, 0);
int i = 1;
while (fieldMap.hasGroup(i, groupCountTag)) {
if (i > 1) {
System.out.println(prefix + " ----");
}
fieldMap.getGroup(i, g);
printFieldMap(prefix + " ", dd, msgType, g);
i++;
}
}
}
private boolean isGroupCountField(DataDictionary dd, Field field) {
return dd.getFieldTypeEnum(field.getTag()) == FieldType.NumInGroup;
}
}
Output:
BeginString: FIX.4.4
BodyLength: 247
MsgSeqNum: 5
MsgType: NewOrderCross (s)
SenderCompID: sender
SendingTime: 20060319-09:08:20.881
TargetCompID: target
SecurityIDSource: EXCHANGE_SYMBOL (8)
OrdType: LIMIT (2)
Price: 9
SecurityID: ABC
Symbol: ABC
TransactTime: 20060319-09:08:19
CrossID: 184214
CrossType: CROSS_TRADE_WHICH_IS_EXECUTED_PARTIALLY_AND_THE_REST_IS_CANCELLED (2)
CrossPrioritization: NONE (0)
NoSides: count = 2
OrderQty: 9
Side: BUY (1)
NoPartyIDs: count = 2
PartyIDSource: PROPRIETARY_CUSTOM_CODE (D)
PartyID: 8
PartyRole: CLEARING_FIRM (4)
----
PartyIDSource: PROPRIETARY_CUSTOM_CODE (D)
PartyID: AAA35777
PartyRole: CLIENT_ID (3)
----
OrderQty: 9
Side: SELL (2)
NoPartyIDs: count = 2
PartyIDSource: PROPRIETARY_CUSTOM_CODE (D)
PartyID: 8
PartyRole: CLEARING_FIRM (4)
----
PartyIDSource: PROPRIETARY_CUSTOM_CODE (D)
PartyID: aaa
PartyRole: CLIENT_ID (3)
CheckSum: 056
There is no method in quickfix to parse the message in human readable format. Another option is when you handle an incoming FIX message in onMessage, you would anyway parse it to read the message. Here you can list down the tagname and values to a file or DB. But any operation to do so may slow down your Quickfix engine, because writing to a file or DB is always slow. So BEWARE !!
Another option is log your messages to your DB instead of a file and then do all your stuff in the DB, but that would mean parsing a message twice i.e. in your engine and DB. But you would get a lot more flexibilty about what you want to read and what not to.
You can get the QuickFix.Message.ToXML() to output tags of a certain dictionary by using the QuickFix.Message.InitializeXML method.
eg.
QuickFix.Message msg = new QuickFix.Message();
// Create your message
QuickFix.Message.InitializeXML(@"c:\quickfix\spec\FIX44.xml");
Console.WriteLine(msg.ToXML());
It will give you the tags for each field, but not what the content of the field means if it is an enumeration value.
This is an exmple of what I see in my output window:
<message>
<header>
<field name="BeginString" number="8"><![CDATA[FIX.4.4]]></field>
<field name="MsgType" number="35" enum="Heartbeat"><![CDATA[0]]></field>
<field name="MsgSeqNum" number="34"><![CDATA[10]]></field>
<field name="SenderCompID" number="49"><![CDATA[VCMVCON]]></field>
<field name="SendingTime" number="52"><![CDATA[20110815-09:35:33.782]]></field>
<field name="TargetCompID" number="56"><![CDATA[BLPVCON]]></field>
</header>
<body>
<field name="TestReqID" number="112"><![CDATA[R.0001.0010.000A.093519]]></field>
</body>
<trailer>
</trailer>
</message>
I took Martin Vseticka's Java answer and made my own version in C#, the key differences being that I chose to use a StringBuilder
instead of printing out each line, and I added an extension method. I chose a format similar to JSON, and it wouldn't take much work at all to make it produce actual valid JSON.
public static class MessageDecoder
{
public static string Decode(this Message message, DataDictionary dataDictionary)
{
return DecodeMessage(message, dataDictionary);
}
public static string DecodeMessage(Message message, DataDictionary dataDictionary)
{
var messageStr = new StringBuilder("FIX {\n");
var msgType = message.Header.GetString(Tags.MsgType);
DecodeFieldMap(" ", dataDictionary, messageStr, msgType, message.Header);
DecodeFieldMap(" ", dataDictionary, messageStr, msgType, message);
DecodeFieldMap(" ", dataDictionary, messageStr, msgType, message.Trailer);
messageStr.Append("}");
return messageStr.ToString();
}
private static void DecodeFieldMap(string prefix, DataDictionary dd, StringBuilder str, string msgType, FieldMap fieldMap)
{
foreach (var kvp in fieldMap)
{
if (dd.IsGroup(msgType, kvp.Key)) continue;
var field = dd.FieldsByTag[kvp.Key];
var value = fieldMap.GetString(field.Tag);
if (dd.FieldHasValue(field.Tag, value))
{
value = $"{field.EnumDict[value]} ({value})";
}
str.AppendFormat("{0}{1} = {2};\n", prefix, field.Name, value);
}
foreach (var groupTag in fieldMap.GetGroupTags())
{
var groupField = dd.FieldsByTag[groupTag];
str.AppendFormat("{0}{1} (count {2}) {{\n", prefix, groupField.Name, fieldMap.GetInt(groupTag));
for (var i = 1; i <= fieldMap.GetInt(groupTag); i++)
{
var group = fieldMap.GetGroup(i, groupTag);
var groupPrefix = prefix + " ";
str.AppendFormat("{0}{{\n", groupPrefix);
DecodeFieldMap(groupPrefix + " ", dd, str, msgType, group);
str.AppendFormat("{0}}},\n", groupPrefix);
}
str.Remove(str.Length - 2, 1); // Remove last ","
str.AppendFormat("{0}}};\n", prefix);
}
}
}
Output looks like this (I trimmed a bunch of fields):
FIX {
BeginString = FIX.4.4;
BodyLength = 821;
MsgSeqNum = 123;
MsgType = EXECUTION_REPORT (8);
PossDupFlag = YES (Y);
SendingTime = 20200125-02:26:17.405;
ExecID = 76615:3975388;
ExecInst = WORK (2);
LastMkt = XCEC;
OrdStatus = EXPIRED (C);
OrdType = LIMIT (2);
OpenClose = OPEN (O);
SecurityDesc = Copper;
MaturityDate = 20200129;
CustOrderCapacity = ALL_OTHER (4);
ManualOrderIndicator = MANUAL (Y);
NoPartyIDs (count 2) {
{
PartyIDSource = PROPRIETARY (D);
PartyID = 0;
PartyRole = ACCOUNT_TYPE (205);
},
{
PartyIDSource = PROPRIETARY (D);
PartyID = FIX_UAT;
PartyRole = CLEARING_ACCOUNT (83);
}
};
NoSecurityAltID (count 2) {
{
SecurityAltID = 1HGF0;
SecurityAltIDSource = RIC_CODE (5);
},
{
SecurityAltID = 170831;
SecurityAltIDSource = EXCHANGE_SECURITY_ID (8);
}
};
CheckSum = 077;
}
Here is an answer in Python. Depending on the user's need there are three solutions presented here.
- Marked-up XML
- List of fields (groups embedded).
- JSON-like output.
In the quickfix Python library, the FieldMap field and group key iterators are not exposed. So the approach is to first generate the XML and iterate over the tree. There is also no access to the getFieldType method of the DataDictionary, so the dictionary must be pre-processed to store the field types for conversion and handling of groups.
import quickfix as fix
import xml.etree.ElementTree as ET
from collections import OrderedDict
import json
string = "8=FIX.4.4\0019=247\00135=s\00134=5\00149=sender\00152=20060319-09:08:20.881\00156=target\00122=8\00140=2\00144=9\00148=ABC\00155=ABC\00160=20060319-09:08:19\001548=184214\001549=2\001550=0\001552=2\00154=1\001453=2\001448=8\001447=D\001452=4\001448=AAA35777\001447=D\001452=3\00138=9\00154=2\001453=2\001448=8\001447=D\001452=4\001448=aaa\001447=D\001452=3\00138=9\00110=056\001"
# Load data dictionary
data_dictionary_xml = "FIX44.xml"
data_dictionary = fix.DataDictionary(data_dictionary_xml)
fix.Message().InitializeXML(data_dictionary_xml)
# String as fix message according to dictionary
message = fix.Message(string, data_dictionary, True)
# Marked-up XML
xml = message.toXML()
print(xml)
def get_field_type_map(data_dictionary_xml):
"""Preprocess DataDictionary to get field types."""
field_type_map = {}
with open(data_dictionary_xml, "r") as f:
xml = f.read()
tree = ET.fromstring(xml)
fields = tree.find("fields")
for field in fields.iter("field"):
field_type_map[field.attrib["number"]] = field.attrib["type"]
return field_type_map
field_type_map = get_field_type_map(data_dictionary_xml)
INT_TYPES = ["INT", "LENGTH", "NUMINGROUP", "QTY", "SEQNUM"]
FLOAT_TYPES = ["FLOAT", "PERCENTAGE", "PRICE", "PRICEOFFSET"]
BOOL_TYPES = ["BOOLEAN"]
DATETIME_TYPES = ["LOCALMKTDATE", "MONTHYEAR", "UTCDATEONLY", "UTCTIMEONLY", "UTCTIMESTAMP"]
STRING_TYPES = ["AMT", "CHAR", "COUNTRY", "CURRENCY", "DATA", "EXCHANGE", "MULTIPLEVALUESTRING", "STRING"]
def field_map_to_list(field_map, field_type_map):
fields = []
field_iter = iter([el for el in field_map if el.tag == "field"])
group_iter = iter([el for el in field_map if el.tag == "group"])
for field in field_iter:
# Extract raw value
raw = field.text
# Type the raw value
field_type = field_type_map.get(field.attrib["number"])
if field_type in INT_TYPES:
value = int(raw)
elif field_type in FLOAT_TYPES:
value = float(raw)
elif field_type in BOOL_TYPES:
value = bool(int(raw))
elif field_type in DATETIME_TYPES:
value = str(raw)
elif field_type in STRING_TYPES:
value = str(raw)
else:
value = str(raw)
# field.attrib should contain "name", "number", "enum"
_field = {
**field.attrib,
"type": field_type,
"raw": raw,
"value": value,
}
# If NUMINGROUP type then iterate groups the number indicated
# This assumes groups are in the same order as their field keys
if field_type == "NUMINGROUP":
groups = []
for _ in range(value):
group = next(group_iter)
# Parse group as field map
group_fields = field_map_to_list(group, field_type_map)
groups.append(group_fields)
_field["groups"] = groups
fields.append(_field)
return fields
def field_map_to_dict(field_map, field_type_map):
fields = OrderedDict()
field_iter = iter([el for el in field_map if el.tag == "field"])
group_iter = iter([el for el in field_map if el.tag == "group"])
for field in field_iter:
# Define key
# field.attrib should contain "name", "number", "enum"
key = field.attrib.get("name") or field.attrib.get("number")
# Extract raw value
raw = field.text
# Type the raw value
field_type = field_type_map.get(field.attrib["number"])
if field_type in INT_TYPES:
value = int(raw)
elif field_type in FLOAT_TYPES:
value = float(raw)
elif field_type in BOOL_TYPES:
value = bool(int(raw))
elif field_type in DATETIME_TYPES:
value = str(raw)
elif field_type in STRING_TYPES:
value = str(raw)
else:
value = str(raw)
# If NUMINGROUP type then iterate groups the number indicated
# This assumes groups are in the same order as their field keys
if field_type == "NUMINGROUP":
groups = []
for _ in range(value):
group = next(group_iter)
# Parse group as field map
group_fields = field_map_to_dict(group, field_type_map)
groups.append(group_fields)
fields[key] = groups
else:
# Preference enum above value
fields[key] = field.attrib.get("enum") or value
return fields
def parse_message_xml(xml, field_type_map, as_dict=False):
parsed = OrderedDict()
tree = ET.fromstring(xml)
for field_map in tree:
if not as_dict:
parsed[field_map.tag] = field_map_to_list(field_map, field_type_map)
else:
parsed[field_map.tag] = field_map_to_dict(field_map, field_type_map)
return parsed
# List of fields (groups embedded)
parsed = parse_message_xml(xml, field_type_map, as_dict=False)
print(json.dumps(parsed, indent=True))
# JSON-like output
parsed = parse_message_xml(xml, field_type_map, as_dict=True)
print(json.dumps(parsed, indent=True))
Outputs:
<message>
<header>
<field name="BeginString" number="8"><![CDATA[FIX.4.4]]></field>
<field name="BodyLength" number="9"><![CDATA[247]]></field>
<field name="MsgType" number="35" enum="NewOrderCross"><![CDATA[s]]></field>
<field name="MsgSeqNum" number="34"><![CDATA[5]]></field>
<field name="SenderCompID" number="49"><![CDATA[sender]]></field>
<field name="SendingTime" number="52"><![CDATA[20060319-09:08:20.881]]></field>
<field name="TargetCompID" number="56"><![CDATA[target]]></field>
</header>
<body>
<field name="SecurityIDSource" number="22" enum="EXCHANGE_SYMBOL"><![CDATA[8]]></field>
<field name="OrdType" number="40" enum="LIMIT"><![CDATA[2]]></field>
<field name="Price" number="44"><![CDATA[9]]></field>
<field name="SecurityID" number="48"><![CDATA[ABC]]></field>
<field name="Symbol" number="55"><![CDATA[ABC]]></field>
<field name="TransactTime" number="60"><![CDATA[20060319-09:08:19]]></field>
<field name="CrossID" number="548"><![CDATA[184214]]></field>
<field name="CrossType" number="549" enum="CROSS_TRADE_WHICH_IS_EXECUTED_PARTIALLY_AND_THE_REST_IS_CANCELLED_ONE_SIDE_IS_FULLY_EXECUTED_THE_OTHER_SIDE_IS_PARTIALLY_EXECUTED_WITH_THE_REMAINDER_BEING_CANCELLED_THIS_IS_EQUIVALENT_TO_AN_IMMEDIATE_OR_CANCEL_ON_THE_OTHER_SIDE_NOTE_THE_CROSSPRIORITZATION"><![CDATA[2]]></field>
<field name="CrossPrioritization" number="550" enum="NONE"><![CDATA[0]]></field>
<field name="NoSides" number="552" enum="BOTH_SIDES"><![CDATA[2]]></field>
<group>
<field name="Side" number="54" enum="BUY"><![CDATA[1]]></field>
<field name="NoPartyIDs" number="453"><![CDATA[2]]></field>
<field name="OrderQty" number="38"><![CDATA[9]]></field>
<group>
<field name="PartyID" number="448"><![CDATA[8]]></field>
<field name="PartyIDSource" number="447" enum="PROPRIETARY_CUSTOM_CODE"><![CDATA[D]]></field>
<field name="PartyRole" number="452" enum="CLEARING_FIRM"><![CDATA[4]]></field>
</group>
<group>
<field name="PartyID" number="448"><![CDATA[AAA35777]]></field>
<field name="PartyIDSource" number="447" enum="PROPRIETARY_CUSTOM_CODE"><![CDATA[D]]></field>
<field name="PartyRole" number="452" enum="CLIENT_ID"><![CDATA[3]]></field>
</group>
</group>
<group>
<field name="Side" number="54" enum="SELL"><![CDATA[2]]></field>
<field name="NoPartyIDs" number="453"><![CDATA[2]]></field>
<field name="OrderQty" number="38"><![CDATA[9]]></field>
<group>
<field name="PartyID" number="448"><![CDATA[8]]></field>
<field name="PartyIDSource" number="447" enum="PROPRIETARY_CUSTOM_CODE"><![CDATA[D]]></field>
<field name="PartyRole" number="452" enum="CLEARING_FIRM"><![CDATA[4]]></field>
</group>
<group>
<field name="PartyID" number="448"><![CDATA[aaa]]></field>
<field name="PartyIDSource" number="447" enum="PROPRIETARY_CUSTOM_CODE"><![CDATA[D]]></field>
<field name="PartyRole" number="452" enum="CLIENT_ID"><![CDATA[3]]></field>
</group>
</group>
</body>
<trailer>
<field name="CheckSum" number="10"><![CDATA[056]]></field>
</trailer>
</message>
{
"header": [
{
"number": "8",
"name": "BeginString",
"value": "FIX.4.4",
"raw": "FIX.4.4",
"type": "STRING"
},
{
"number": "9",
"name": "BodyLength",
"value": 247,
"raw": "247",
"type": "LENGTH"
},
{
"number": "35",
"name": "MsgType",
"enum": "NewOrderCross",
"value": "s",
"raw": "s",
"type": "STRING"
},
{
"number": "34",
"name": "MsgSeqNum",
"value": 5,
"raw": "5",
"type": "SEQNUM"
},
{
"number": "49",
"name": "SenderCompID",
"value": "sender",
"raw": "sender",
"type": "STRING"
},
{
"number": "52",
"name": "SendingTime",
"value": "20060319-09:08:20.881",
"raw": "20060319-09:08:20.881",
"type": "UTCTIMESTAMP"
},
{
"number": "56",
"name": "TargetCompID",
"value": "target",
"raw": "target",
"type": "STRING"
}
],
"body": [
{
"number": "22",
"name": "SecurityIDSource",
"enum": "EXCHANGE_SYMBOL",
"value": "8",
"raw": "8",
"type": "STRING"
},
{
"number": "40",
"name": "OrdType",
"enum": "LIMIT",
"value": "2",
"raw": "2",
"type": "CHAR"
},
{
"number": "44",
"name": "Price",
"value": 9.0,
"raw": "9",
"type": "PRICE"
},
{
"number": "48",
"name": "SecurityID",
"value": "ABC",
"raw": "ABC",
"type": "STRING"
},
{
"number": "55",
"name": "Symbol",
"value": "ABC",
"raw": "ABC",
"type": "STRING"
},
{
"number": "60",
"name": "TransactTime",
"value": "20060319-09:08:19",
"raw": "20060319-09:08:19",
"type": "UTCTIMESTAMP"
},
{
"number": "548",
"name": "CrossID",
"value": "184214",
"raw": "184214",
"type": "STRING"
},
{
"number": "549",
"name": "CrossType",
"enum": "CROSS_TRADE_WHICH_IS_EXECUTED_PARTIALLY_AND_THE_REST_IS_CANCELLED_ONE_SIDE_IS_FULLY_EXECUTED_THE_OTHER_SIDE_IS_PARTIALLY_EXECUTED_WITH_THE_REMAINDER_BEING_CANCELLED_THIS_IS_EQUIVALENT_TO_AN_IMMEDIATE_OR_CANCEL_ON_THE_OTHER_SIDE_NOTE_THE_CROSSPRIORITZATION",
"value": 2,
"raw": "2",
"type": "INT"
},
{
"number": "550",
"name": "CrossPrioritization",
"enum": "NONE",
"value": 0,
"raw": "0",
"type": "INT"
},
{
"number": "552",
"name": "NoSides",
"enum": "BOTH_SIDES",
"value": 2,
"raw": "2",
"type": "NUMINGROUP",
"groups": [
[
{
"number": "54",
"name": "Side",
"enum": "BUY",
"value": "1",
"raw": "1",
"type": "CHAR"
},
{
"number": "453",
"name": "NoPartyIDs",
"groups": [
[
{
"number": "448",
"name": "PartyID",
"value": "8",
"raw": "8",
"type": "STRING"
},
{
"number": "447",
"name": "PartyIDSource",
"enum": "PROPRIETARY_CUSTOM_CODE",
"value": "D",
"raw": "D",
"type": "CHAR"
},
{
"number": "452",
"name": "PartyRole",
"enum": "CLEARING_FIRM",
"value": 4,
"raw": "4",
"type": "INT"
}
],
[
{
"number": "448",
"name": "PartyID",
"value": "AAA35777",
"raw": "AAA35777",
"type": "STRING"
},
{
"number": "447",
"name": "PartyIDSource",
"enum": "PROPRIETARY_CUSTOM_CODE",
"value": "D",
"raw": "D",
"type": "CHAR"
},
{
"number": "452",
"name": "PartyRole",
"enum": "CLIENT_ID",
"value": 3,
"raw": "3",
"type": "INT"
}
]
],
"value": 2,
"raw": "2",
"type": "NUMINGROUP"
},
{
"number": "38",
"name": "OrderQty",
"value": 9,
"raw": "9",
"type": "QTY"
}
],
[
{
"number": "54",
"name": "Side",
"enum": "SELL",
"value": "2",
"raw": "2",
"type": "CHAR"
},
{
"number": "453",
"name": "NoPartyIDs",
"groups": [
[
{
"number": "448",
"name": "PartyID",
"value": "8",
"raw": "8",
"type": "STRING"
},
{
"number": "447",
"name": "PartyIDSource",
"enum": "PROPRIETARY_CUSTOM_CODE",
"value": "D",
"raw": "D",
"type": "CHAR"
},
{
"number": "452",
"name": "PartyRole",
"enum": "CLEARING_FIRM",
"value": 4,
"raw": "4",
"type": "INT"
}
],
[
{
"number": "448",
"name": "PartyID",
"value": "aaa",
"raw": "aaa",
"type": "STRING"
},
{
"number": "447",
"name": "PartyIDSource",
"enum": "PROPRIETARY_CUSTOM_CODE",
"value": "D",
"raw": "D",
"type": "CHAR"
},
{
"number": "452",
"name": "PartyRole",
"enum": "CLIENT_ID",
"value": 3,
"raw": "3",
"type": "INT"
}
]
],
"value": 2,
"raw": "2",
"type": "NUMINGROUP"
},
{
"number": "38",
"name": "OrderQty",
"value": 9,
"raw": "9",
"type": "QTY"
}
]
]
}
],
"trailer": [
{
"number": "10",
"name": "CheckSum",
"value": "056",
"raw": "056",
"type": "STRING"
}
]
}
{
"header": {
"BeginString": "FIX.4.4",
"BodyLength": 247,
"MsgType": "NewOrderCross",
"MsgSeqNum": 5,
"SenderCompID": "sender",
"SendingTime": "20060319-09:08:20.881",
"TargetCompID": "target"
},
"body": {
"SecurityIDSource": "EXCHANGE_SYMBOL",
"OrdType": "LIMIT",
"Price": 9.0,
"SecurityID": "ABC",
"Symbol": "ABC",
"TransactTime": "20060319-09:08:19",
"CrossID": "184214",
"CrossType": "CROSS_TRADE_WHICH_IS_EXECUTED_PARTIALLY_AND_THE_REST_IS_CANCELLED_ONE_SIDE_IS_FULLY_EXECUTED_THE_OTHER_SIDE_IS_PARTIALLY_EXECUTED_WITH_THE_REMAINDER_BEING_CANCELLED_THIS_IS_EQUIVALENT_TO_AN_IMMEDIATE_OR_CANCEL_ON_THE_OTHER_SIDE_NOTE_THE_CROSSPRIORITZATION",
"CrossPrioritization": "NONE",
"NoSides": [
{
"Side": "BUY",
"NoPartyIDs": [
{
"PartyID": "8",
"PartyIDSource": "PROPRIETARY_CUSTOM_CODE",
"PartyRole": "CLEARING_FIRM"
},
{
"PartyID": "AAA35777",
"PartyIDSource": "PROPRIETARY_CUSTOM_CODE",
"PartyRole": "CLIENT_ID"
}
],
"OrderQty": 9
},
{
"Side": "SELL",
"NoPartyIDs": [
{
"PartyID": "8",
"PartyIDSource": "PROPRIETARY_CUSTOM_CODE",
"PartyRole": "CLEARING_FIRM"
},
{
"PartyID": "aaa",
"PartyIDSource": "PROPRIETARY_CUSTOM_CODE",
"PartyRole": "CLIENT_ID"
}
],
"OrderQty": 9
}
]
},
"trailer": {
"CheckSum": "056"
}
}
精彩评论