我想以某种解析模式记录QuickFix消息,例如标记名,值
我找不到现有功能。我正在使用QuickFix.Net。
我正在考虑提供某种方法,该方法将遍历所有呈现的标签并使用数据字典对其进行解析。
Quickfix中没有方法可以解析人类可读格式的消息。另一个选择是,当您在onMessage中处理传入的FIX消息时,无论如何都要对其进行解析以读取该消息。在这里,您可以列出标记名和值到文件或数据库中。但是这样做的任何操作都可能会使您的Quickfix引擎变慢,因为写入文件或DB总是很慢。所以当心!
[另一种选择是将消息记录到数据库而不是文件中,然后在数据库中完成所有工作,但这意味着对消息进行两次解析,即在引擎和数据库中。但是,对于想要阅读和不想要阅读的内容,您将获得更大的灵活性。
有一个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
您可以使用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>
我接受了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;
}