Quick Fix 开发总结
简介
Fix的全称Financial Information eXchange ("FIX") 它是一个定义了一系列电子交易信息格式的协议.
Quick Fix(需要FQ)是一个开源项目,它遵循Fix协议,对服务器与客户端之间的通信进行了封装,提高开发Fix应用程序的效率。
configuration
[DEFAULT] //必须有此行。
[SESSION]//一个配置文件可以有多个session
FileStorePath=.//设置目录
FileLogPath=.//设置log文件目录
BeginString=FIX.4.2 //发送和接收消息起始字符串
SenderCompID=ABCD //发送消息的ID
TargetCompID=CNX//目标ID
SessionQualifier=Currenex
ConnectionType=initiator//发起的连接类型
StartTime=00:00:00//开始时间
EndTime=00:00:00//结束时间
MillisecondsInTimeStamp=Y
UseDataDictionary=Y
DataDictionary=FIX42_New.xml//Fix协议定义文件
ValidateUserDefinedFields=N
CheckCompID=N
CheckLatency=N
ReconnectInterval=30
HeartBtInt=60//发送心跳信息的时间间隔,单位秒
LogonTimeout=30
LogoutTimeout=10
SocketConnectHost=208.89.235.***//服务器IP
SocketConnectPort=***//服务器端口
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y
核心方法
void Application.FromAdmin(Message message, SessionID sessionID)
{
//服务器发送的与连接状态相关的信息(验证失败,密码错误,服务器不可用等等)会回调这个方法
Console.WriteLine("FromAdmin : " + message.ToString() +"\n\n");
//QuickFix.FIX42.TradingSessionStatus
}
void Application.FromApp(Message message, SessionID sessionID)
{
//服务器发送的数据相关的信息会回调这个方法
Console.WriteLine("FromApp: " + message.ToString()+"\n\n");
}
void Application.OnCreate(SessionID sessionID)
{
}
void Application.OnLogon(SessionID sessionID)
{
}
void Application.OnLogout(SessionID sessionID)
{
}
void Application.ToAdmin(Message message, SessionID sessionID)
{
//客户端要发送有关连接状态的信息(登录,登出等)到服务器之前会回调这个方法
Console.WriteLine("ToAdmin :" + message.ToString());
}
void Application.ToApp(Message message, SessionID sessionID)
{
//客户端要发送数据相关的信息给服务器端之前会回调这个方法(如订阅价格等等)
Console.WriteLine("ToApp: " + message.ToString()+"\n\n");
WriteToFile("ToApp: " + message.ToString());
}
{
//服务器发送的与连接状态相关的信息(验证失败,密码错误,服务器不可用等等)会回调这个方法
Console.WriteLine("FromAdmin : " + message.ToString() +"\n\n");
//QuickFix.FIX42.TradingSessionStatus
}
void Application.FromApp(Message message, SessionID sessionID)
{
//服务器发送的数据相关的信息会回调这个方法
Console.WriteLine("FromApp: " + message.ToString()+"\n\n");
}
void Application.OnCreate(SessionID sessionID)
{
}
void Application.OnLogon(SessionID sessionID)
{
}
void Application.OnLogout(SessionID sessionID)
{
}
void Application.ToAdmin(Message message, SessionID sessionID)
{
//客户端要发送有关连接状态的信息(登录,登出等)到服务器之前会回调这个方法
Console.WriteLine("ToAdmin :" + message.ToString());
}
void Application.ToApp(Message message, SessionID sessionID)
{
//客户端要发送数据相关的信息给服务器端之前会回调这个方法(如订阅价格等等)
Console.WriteLine("ToApp: " + message.ToString()+"\n\n");
WriteToFile("ToApp: " + message.ToString());
}
需要注意的地方
开发之前一定要详细阅读接口的API文档,注意每一个细节,登录成功但是发消息得不到预期的返回,大部分是因为消息格式不正确。遇到问题要及时查看API文档还有XML协议定义文件。在集成Currenex接口的时候只是由于一个Field跟API文档不一致导致很长时间无法成功订阅服务器的价格数据
private QuickFix.FIX42.MarketDataRequest QueryMarketDataRequest42()
{
MDReqID mdReqID = new MDReqID();
mdReqID.setValue("ccy1-AUDUSD");
SubscriptionRequestType subType = new SubscriptionRequestType(SubscriptionRequestType.SNAPSHOT_PLUS_UPDATES);
MarketDepth marketDepth = new MarketDepth(1);
//错误写法 这种写法生成的消息里只有269=1(offer)
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup.Set(new MDEntryType(MDEntryType.BID));
marketDataEntryGroup.RepeatedTags.Add(new MDEntryType(MDEntryType.OFFER));
//正确写法 这种写法生成的消息里就会同时有269=0和269=1
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup.Set(new MDEntryType(MDEntryType.BID));
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup1 = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup1.Set(new MDEntryType(MDEntryType.OFFER));
QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup symbolGroup = new QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup();
symbolGroup.Set(new Symbol("AUD/USD"));
QuickFix.FIX42.MarketDataRequest message = new QuickFix.FIX42.MarketDataRequest(mdReqID, subType, marketDepth);
message.AddGroup(marketDataEntryGroup);
message.AddGroup(marketDataEntryGroup1);
message.AddGroup(symbolGroup);
message.Set(new MDUpdateType(1));
message.SetField(new StringField(267,"2"));
message.SetField(new StringField(266, "Y"));
//message.SetField(new StringField(7560, "Y"));
return message;
{
MDReqID mdReqID = new MDReqID();
mdReqID.setValue("ccy1-AUDUSD");
SubscriptionRequestType subType = new SubscriptionRequestType(SubscriptionRequestType.SNAPSHOT_PLUS_UPDATES);
MarketDepth marketDepth = new MarketDepth(1);
//错误写法 这种写法生成的消息里只有269=1(offer)
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup.Set(new MDEntryType(MDEntryType.BID));
marketDataEntryGroup.RepeatedTags.Add(new MDEntryType(MDEntryType.OFFER));
//正确写法 这种写法生成的消息里就会同时有269=0和269=1
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup.Set(new MDEntryType(MDEntryType.BID));
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup1 = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup1.Set(new MDEntryType(MDEntryType.OFFER));
QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup symbolGroup = new QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup();
symbolGroup.Set(new Symbol("AUD/USD"));
QuickFix.FIX42.MarketDataRequest message = new QuickFix.FIX42.MarketDataRequest(mdReqID, subType, marketDepth);
message.AddGroup(marketDataEntryGroup);
message.AddGroup(marketDataEntryGroup1);
message.AddGroup(symbolGroup);
message.Set(new MDUpdateType(1));
message.SetField(new StringField(267,"2"));
message.SetField(new StringField(266, "Y"));
//message.SetField(new StringField(7560, "Y"));
return message;
}
有一些Field在接口的API中要求是必须有的,但是在Fix的XML协议文件里查不到,对于这些字段我们在发送消息前在消息中加上相应的字段编号跟值就可以了,服务器是可以正确识别的。
返回的消息也是正确的。
配置文件跟XML协议定义文件属性要设置成copy if newer.
接收消息
QuickFix对接收的消息也进行了封装,不需要用字符串拆分的方法。
/// <summary>
/// Separate Market Data Incremental Refresh
/// </summary>
/// <param name="marketData"></param>
/// <returns></returns>
public static List<InstrmtLegGrp> GetMarketData(QuickFix42.MarketDataIncrementalRefresh marketData)
{
QuickFix.NoMDEntries noMDEntries = marketData.get(new NoMDEntries());
QuickFix42.MarketDataIncrementalRefresh.NoMDEntries MDEntriesGroup = new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries();
QuickFix.MDUpdateAction mdUpdateAction = new MDUpdateAction();
QuickFix.MDEntryType mdEntryType = new QuickFix.MDEntryType();
QuickFix.MDEntryID mdEntryID = new MDEntryID();
QuickFix.Symbol symbol = new Symbol();
QuickFix.MDEntryPx mdEntryPx = new QuickFix.MDEntryPx();
QuickFix.MDEntrySize mdEntrySize = new QuickFix.MDEntrySize();
int MDEntriesNo = marketData.getNoMDEntries().getValue();
List<InstrmtLegGrp> instrmtEntriesList = new List<InstrmtLegGrp>();
for (uint i = 0; i < MDEntriesNo; i++)
{
marketData.getGroup(i, MDEntriesGroup);
InstrmtLegGrp instrmtEntry = new InstrmtLegGrp();
if (MDEntriesGroup.isSetMDUpdateAction())
{
MDEntriesGroup.get(mdUpdateAction);
instrmtEntry.mdUpateAction = mdUpdateAction.getValue();
}
if (MDEntriesGroup.isSetMDEntryType())
{
MDEntriesGroup.get(mdEntryType);
instrmtEntry.mdEntryType = mdEntryType.getValue();
}
if (MDEntriesGroup.isSetMDEntryID())
{
MDEntriesGroup.get(mdEntryID);
instrmtEntry.mdEntryID = mdEntryID.getValue();
}
if (MDEntriesGroup.isSetSymbol())
{
MDEntriesGroup.get(symbol);
instrmtEntry.symbol = symbol.getValue();
}
if (MDEntriesGroup.isSetMDEntryPx())
{
MDEntriesGroup.get(mdEntryPx);
instrmtEntry.mdEntryPx = mdEntryPx.getValue();
}
if (MDEntriesGroup.isSetMDEntrySize())
{
MDEntriesGroup.get(mdEntrySize);
instrmtEntry.mdEntrySize = mdEntrySize.getValue();
}
instrmtEntriesList.Add(instrmtEntry);
}
return instrmtEntriesList;}
/// Separate Market Data Incremental Refresh
/// </summary>
/// <param name="marketData"></param>
/// <returns></returns>
public static List<InstrmtLegGrp> GetMarketData(QuickFix42.MarketDataIncrementalRefresh marketData)
{
QuickFix.NoMDEntries noMDEntries = marketData.get(new NoMDEntries());
QuickFix42.MarketDataIncrementalRefresh.NoMDEntries MDEntriesGroup = new QuickFix42.MarketDataIncrementalRefresh.NoMDEntries();
QuickFix.MDUpdateAction mdUpdateAction = new MDUpdateAction();
QuickFix.MDEntryType mdEntryType = new QuickFix.MDEntryType();
QuickFix.MDEntryID mdEntryID = new MDEntryID();
QuickFix.Symbol symbol = new Symbol();
QuickFix.MDEntryPx mdEntryPx = new QuickFix.MDEntryPx();
QuickFix.MDEntrySize mdEntrySize = new QuickFix.MDEntrySize();
int MDEntriesNo = marketData.getNoMDEntries().getValue();
List<InstrmtLegGrp> instrmtEntriesList = new List<InstrmtLegGrp>();
for (uint i = 0; i < MDEntriesNo; i++)
{
marketData.getGroup(i, MDEntriesGroup);
InstrmtLegGrp instrmtEntry = new InstrmtLegGrp();
if (MDEntriesGroup.isSetMDUpdateAction())
{
MDEntriesGroup.get(mdUpdateAction);
instrmtEntry.mdUpateAction = mdUpdateAction.getValue();
}
if (MDEntriesGroup.isSetMDEntryType())
{
MDEntriesGroup.get(mdEntryType);
instrmtEntry.mdEntryType = mdEntryType.getValue();
}
if (MDEntriesGroup.isSetMDEntryID())
{
MDEntriesGroup.get(mdEntryID);
instrmtEntry.mdEntryID = mdEntryID.getValue();
}
if (MDEntriesGroup.isSetSymbol())
{
MDEntriesGroup.get(symbol);
instrmtEntry.symbol = symbol.getValue();
}
if (MDEntriesGroup.isSetMDEntryPx())
{
MDEntriesGroup.get(mdEntryPx);
instrmtEntry.mdEntryPx = mdEntryPx.getValue();
}
if (MDEntriesGroup.isSetMDEntrySize())
{
MDEntriesGroup.get(mdEntrySize);
instrmtEntry.mdEntrySize = mdEntrySize.getValue();
}
instrmtEntriesList.Add(instrmtEntry);
}
return instrmtEntriesList;}
}
Demo下载 :QuickLesson1.rar