最近在搞QuickFIX/J,網上的資料不算很多,遇到一些簡單的問題都需要google一陣才能找到解決方法,因此做點記錄:
錯誤:Rejecting invalid message: quickfix.UnsupportedMessageType
這個異常的產生,有兩個條件:
1) UseDataDictionary = N
2) 沒有覆蓋對應的onMessage方法
具體說來,如果沒有將UseDataDictionary設為N,幾時沒有覆蓋對應的onMessage方法,也不會產生這個異常。
至於沒有覆蓋對應的onMessage方法,是指覆蓋的onMessage方法,參數必須是接收的信息的類型:
//Will throws error @Override public void onMessage(Message message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue{ super.onMessage(message, sessionID); System.out.println("--crack--"); } //Correct! @Override public void onMessage(ExecutionReport message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { System.out.println("--crack ER--"); }
如果接收的信息35=8(ExecutionReport),則需用第二個onMessage方法,而非第一個。
在FIX信息中,自定義域(Add new field)
在QuickFIX/J中新增自定義域,最簡單的就是修改對應版本的xml文件,在xml文件里增加自定義域,然后在配置文件中設置如下:
UseDataDictionary=Y
DataDictionary=config/FIX42.xml
用這種方法去指定自定義的DataDictionary文件。
但此方法有兩個缺點:
1) 接收信息,解析自定義域數據時,只能使用類型不安全的解析
@Override public void onMessage(ExecutionReport message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { System.out.println("--crack ER--"); StringField field = new StringField(10041); System.out.println(message.getField(field).toString()); }
假設新增了10041這個域(FIX Tag),由於QuickFIX/J包里默認沒有這個域對應的對象,這能使用這種方法取得這個域的值,而這種方法在官方文檔里描述為類型不安全,並不推薦。
2) 當自定義域的信息含有Repeating Group時,會拋出Out of order repeating group members這個錯誤。暫時還未找到解決方法
最好的解決方法,是修改xml文件,新增自定義域(Field)和消息類型(Message Type)后,重新build一個自定義的.jar文件。
使用這種方法需要注意的是,如果自定義域和自定義消息類型只針對某一個版本的FIX,例如只修改了FIX42.xml,而QuickFIX/J默認是將所有的Field放在“quickfix.field”這個包里,並非如消息類型那樣分開不同版本的包放,因此自定義的Field有可能會被其它版本(如FIX43/44)覆蓋。
看來這個類quickfix.codegen.MessageCodeGenerator.java:
public static void main(String[] args) { MessageCodeGenerator codeGenerator = new MessageCodeGenerator(); try { if (args.length != 3) { String classname = MessageCodeGenerator.class.getName(); System.err.println("usage: " + classname + " specDir xformDir outputBaseDir"); return; } boolean overwrite = getOption(OVERWRITE_OPTION, true); boolean orderedFields = getOption(ORDERED_FIELDS_OPTION, false); boolean useDecimal = getOption(BIGDECIMAL_TYPE_OPTION, false); long start = System.currentTimeMillis(); // final String[] vers = new String[] { "FIXT 1.1", "FIX 5.0", "FIX 4.4", "FIX 4.3", "FIX 4.2", // "FIX 4.1", "FIX 4.0" }; final String[] vers = new String[] {"FIXT 1.1","FIX 4.2"}; for (int i = 0; i < vers.length; ++i) { Task task = new Task(); task.setName(vers[i]); final String temp = stripSpaces(vers[i]); task.setSpecification(args[0] + "/" + temp + ".xml"); task.setTransformDirectory(args[1]); task.setMessagePackage("quickfix." + temp.toLowerCase()); task.setOutputBaseDirectory(args[2]); task.setFieldPackage("quickfix.field"); task.setOverwrite(overwrite); task.setOrderedFields(orderedFields); task.setDecimalGenerated(useDecimal); codeGenerator.generate(task); } double duration = System.currentTimeMillis() - start; DecimalFormat durationFormat = new DecimalFormat("#.###"); codeGenerator.log.info("Time for generation: " + durationFormat.format(duration / 1000L) + " seconds"); } catch (Exception e) { codeGenerator.log.error("error during code generation", e); System.exit(1); } }
留意注釋部分是默認的實現。可以看到setMessagePackage和setFieldPackage之間的不同。
成功build了以后,就可以這樣解析10041(name=BoInstrumentCode2)這個自定義域:
public void onMessage(quickfix.fix42.ExecutionReport message, SessionID sessionID) throws FieldNotFound { System.out.println(message.getBoInstrumentCode2().toString()); }
簡單清晰,面向對象
參考:
http://www.quickfixj.org/confluence/display/qfj/User+FAQ
http://www.quickfixj.org/quickfixj/usermanual/1.5.3/usage/configuration.html#Sample Settings File