如何以人类可读的格式记录QuickFix消息

时间:2011-06-23 12:20:36

标签: c# quickfix

我想以类似于解析模式的方式记录QuickFix消息 标记名,值

我找不到现有的功能。我正在使用QuickFix.Net。

我正在考虑提供某种方法来迭代所有呈现标记并使用数据字典解析它们。

5 个答案:

答案 0 :(得分:3)

quickfix中没有方法以人类可读的格式解析消息。另一种选择是当您在onMessage中处理传入的FIX消息时,无论如何您将解析它以读取消息。在这里,您可以将标记名和值列入文件或数据库。但是这样做的任何操作都可能会降低Quickfix引擎的速度,因为写入文件或数据库总是很慢。所以要小心!!

另一种选择是将您的消息记录到您的数据库而不是文件,然后在数据库中执行所有操作,但这意味着解析消息两次,即在您的引擎和数据库中。但是你会对你想要阅读的内容以及不想阅读的内容有更多的灵活性。

答案 1 :(得分:2)

您可以使用QuickFix.Message.InitializeXML方法获取QuickFix.Message.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>

答案 2 :(得分:2)

有一个Java示例http://www.quickfixj.org/confluence/display/qfj/Using+Message+Metadata可以转换为.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

答案 3 :(得分:1)

我接受了Martin Vseticka的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;
}

答案 4 :(得分:0)

这是Python的答案。根据用户的需求,这里提供了三种解决方案。

  1. 标记的XML
  2. 字段列表(嵌入的组)。
  3. 类似JSON的输出。

在quickfix 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"
 }
}