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

问题描述 投票:4回答:4

我想以某种解析模式记录QuickFix消息,例如标记名,值

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

我正在考虑提供某种方法,该方法将遍历所有呈现的标签并使用数据字典对其进行解析。

c# quickfix
4个回答
3
投票

Quickfix中没有方法可以解析人类可读格式的消息。另一个选择是,当您在onMessage中处理传入的FIX消息时,无论如何都要对其进行解析以读取该消息。在这里,您可以列出标记名和值到文件或数据库中。但是这样做的任何操作都可能会使您的Quickfix引擎变慢,因为写入文件或DB总是很慢。所以当心!

[另一种选择是将消息记录到数据库而不是文件中,然后在数据库中完成所有工作,但这意味着对消息进行两次解析,即在引擎和数据库中。但是,对于想要阅读和不想要阅读的内容,您将获得更大的灵活性。


4
投票

有一个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

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>

0
投票

我接受了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;
}
© www.soinside.com 2019 - 2024. All rights reserved.