如何以可读格式记录QuickFix消息
本文关键字:记录 QuickFix 消息 格式 | 更新日期: 2023-09-27 18:00:01
我想以某种解析模式记录QuickFix消息,比如标记名,值
我找不到现有的功能。我正在使用QuickFix.Net.
我正在考虑提供某种方法,可以迭代所有呈现的标签,并使用数据字典对其进行解析。
有一个Java示例http://www.quickfixj.org/confluence/display/qfj/Using+消息+元数据,可以转换为.NET并且可能被证明是有用的:
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;
}
}
输出:
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
quickfix中没有以可读格式解析消息的方法。另一种选择是,当你在onMessage中处理传入的FIX消息时,你无论如何都会解析它来读取消息。在这里,您可以列出文件或数据库的标记名和值。但是,任何这样做的操作都可能会降低Quickfix引擎的速度,因为写入文件或DB总是很慢。所以要小心!!
另一种选择是将消息记录到数据库而不是文件中,然后在数据库中完成所有工作,但这意味着要对消息进行两次解析,即在引擎和数据库中。但是你会在想读什么和不想读什么方面有更多的灵活性。
您可以使用QuickFix.Message.InitializeXML方法获得QuickFix.MMessage.ToXML()来输出某个字典的标记。
例如。
QuickFix.Message msg = new QuickFix.Message();
// Create your message
QuickFix.Message.InitializeXML(@"c:'quickfix'spec'FIX44.xml");
Console.WriteLine(msg.ToXML());
它将为您提供每个字段的标记,但如果字段是枚举值,则不会提供字段内容的含义。
这是我在输出窗口中看到的示例:
<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>
我接受了Martin Vsettika的Java答案,并用C#制作了自己的版本,关键的区别在于我选择使用StringBuilder
而不是打印出每一行,并且我添加了一个扩展方法。我选择了一种类似于JSON的格式,使其生成实际有效的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);
}
}
}
输出如下(我修剪了一堆字段):
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;
}
下面是Python中的一个答案。根据用户的需要,这里提供了三种解决方案。
- 标记的XML
- 字段列表(嵌入的组)
- 类似JSON的输出
在快速修复Python库中,FieldMap字段和组键迭代器不会公开。因此,方法是首先生成XML并在树上进行迭代。也无法访问DataDictionary的getFieldType方法,因此必须对字典进行预处理以存储字段类型,以便转换和处理组。
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))
输出:
<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"
}
}