使用QuickFix4J获取FIX Server证券列表遇Tag 110未定义错误求助
问题:QuickFix4J处理SecurityList响应时触发Tag 110未定义的拒绝错误
使用QuickFix4J向FIX Server请求证券列表,收到响应时客户端发送了拒绝消息,提示Tag 110在该消息类型中未定义。
错误详情
<20230201-05:21:05, FIX.4.4:targetId->senderId, error> (Reject sent for message 2: Tag not defined for this message type, field=110)
请求代码
private void getSecurityList(SessionID sessionID){ String id = UUID.randomUUID().toString(); System.out.println("Sending security list request : >>>>>>>>>>> "); SecurityListRequest request = new SecurityListRequest(); request.getHeader().setField(new MsgType(MsgType.SECURITY_LIST_REQUEST)); request.getHeader().setField(new SenderCompID(senderId)); request.getHeader().setField(new TargetCompID(targetCompanyId)); request.set(new SecurityReqID(id)); request.set(new SecurityListRequestType(SecurityListRequestType.ALL_SECURITIES)); try { Session.sendToTarget(request); } catch (SessionNotFound e) { throw new RuntimeException(e); } }
FIX配置
[default] FileStorePath=target/data/company ConnectionType=initiator SenderCompID=senderId TargetCompID=targetId SocketConnectHost=someurl.com StartTime=00:00:00 EndTime=00:00:00 HeartBtInt=30 ReconnectInterval=60 FileLogPath=logs-client ResetOnLogon=Y ForceResync=Y [session] BeginString=FIX.4.4 SocketConnectPort=62481 LogonTag=553=something LogonTag1=554=something
响应处理代码
@EventListener public void handleFromApp(FromApp fromApp) throws FieldNotFound { System.out.println("From App : "); Message message = (Message) fromApp.getMessage(); System.out.println("From App Message Type : " + message.getHeader().getString(MsgType.FIELD)); // SessionID sessionID = fromApp.getSessionId(); if (message instanceof MarketDataSnapshotFullRefresh){ MarketDataSnapshotFullRefresh marketData = (MarketDataSnapshotFullRefresh) message; orderBookService.getOrderBookFromFix(marketData); } if (message instanceof ExecutionReport){ ExecutionReport executionReport = (ExecutionReport) message; newOrderService.handleOrderStatus(executionReport); } try { String msgType = message.getHeader().getString(MsgType.FIELD); System.out.println("Message Type : " + msgType); if (MsgType.SECURITY_LIST.equals(msgType)){ System.out.println("Received security list response >>>> mapping now >>>>>>> "); securityListResponseMapper(message); } } catch (FieldNotFound e) { throw new RuntimeException(e); } }
原始响应内容
(8=FIX.4.4#9=2153#35=y#49=senderId#56=targetId#34=2#52=20230201-05:06:22.877#320=9b578bc4-9d00-4a3b-898d-3d649bf52612#322=111#560=0#146=60#55=ETH/USD_CFD#110=0.00000001#1208=0.01#55=BTC/USD_CFD#110=0.0001#1208=0.01#55=LTC/BTC#110=0.0001#1208=0.000001#55=WAXP/USC#110=0.000001#1208=0.000001#55=ETH/BTC#110=0.0001#1208=0.000001#55=TUSD/USC#110=0.01#1208=0.00001#55=LTC/USD_CFD#110=0.00000001#1208=0.01#55=DOGE/USC#110=0.1#1208=0.0001#55=LTC/USC#110=0.0001#1208=0.01#55=BCH/USC#110=0.0001#1208=0.01#55=XRP/JPY_CFD#110=1#1208=0.001#55=XRP/JPY#110=1#1208=0.001#55=XRP/USD_CFD#110=0.1#1208=0.0001#55=LTC/JPY_CFD#110=0.00000001#1208=1#55=LTC/JPY#110=0.00000001#1208=1#55=BCH/JPY#110=0.00000001#1208=1#55=BCH/USD_CFD#110=0.00000001#1208=0.01#55=ETH/JPY_CFD#110=0.00000001#1208=1#55=ETH/JPY#110=0.00000001#1208=1#55=DASH/USDC#110=0.00001#1208=0.01#55=ETH/USDC#110=0.01#1208=0.01#55=LTC/USD#110=0.00000001#1208=0.01#55=BCH/USDC#110=0.0001#1208=0.01#55=BCH/JPY_CFD#110=0.00000001#1208=1#55=ETH/USD#110=0.00000001#1208=0.01#55=DASH/USD#110=0.00001#1208=0.01#55=PAX/USDC#110=0.01#1208=0.00001#55=PAXG/USDC#110=0.01#1208=0.00001#55=XRP/USDC#110=0.0001#1208=0.01#55=DOGE/USDC#110=0.1#1208=0.0001#55=BTC/UST#110=0.01#1208=0.01#55=TUSD/USDC#110=0.01#1208=0.00001#55=BCH/UST#110=0.01#1208=0.01#55=WAXP/USD#110=0.000001#1208=0.000001#55=DASH/USDT#110=0.00001#1208=0.01#55=USDT/USD#110=0.01#1208=0.00001#55=PAXG/USC#110=0.01#1208=0.00001#55=XRP/USDT#110=100#1208=0.00001#55=LTC/USDC#110=0.0001#1208=0.01#55=USDC/USD#110=0.01#1208=0.01#55=DOGE/USDT#110=0.1#1208=0.0001#55=ALGO/USD#110=0.1#1208=0.0001#55=BTC/USD#110=0.00000001#1208=0.01#55=BTC/JPY#110=0.00000001#1208=1#55=LTC/UST#110=0.01#1208=0.01#55=DOGE/USD#110=0.1#1208=0.0001#55=BTC/USDT#110=0.01#1208=0.01#55=BTC/USDC#110=0.00001#1208=0.01#55=BCH/USD#110=0.00000001#1208=0.01#55=ETH/USDT#110=0.01#1208=0.01#55=ETH/UST#110=0.01#1208=0.01#55=BCH/USDT#110=0.01#1208=0.01#55=PAX/USC#110=0.01#1208=0.00001#55=DASH/USC#110=0.00001#1208=0.01#55=BTC/USC#110=0.00001#1208=0.01#55=XRP/UST#110=100#1208=0.00001#55=LTC/USDT#110=0.01#1208=0.01#55=ETH/USC#110=0.01#1208=0.01#55=XRP/USC#110=0.0001#1208=0.01#55=BTC/JPY_CFD#110=0.00000001#1208=1#10=249#)
Broker提供的响应结构说明
Broker文档显示,SecurityList(MsgType=y)响应的每个证券条目包含以下字段:
- 55(Symbol):证券代码
- 110(MinQty):最小下单量
- 1208(MinNotional):最小名义金额
解决方案
问题根源是QuickFix4J默认的FIX4.4数据字典中,SecurityList消息(MsgType=y)的重复组内未定义Tag110(MinQty),但Broker的响应在每个Symbol后都携带了该字段,导致客户端字段验证失败触发拒绝。
推荐方案:自定义FIX数据字典
- 复制QuickFix默认的
FIX4.4.xml文件,修改其中的SecurityList消息定义:
在<message name="SecurityList" msgtype="y" msgcat="app">下的<group name="NoRelatedSym" required="Y">节点中,添加Tag110的定义:<field name="MinQty" required="N"/> - 在FIX配置文件的
[default]或[session]节中,添加自定义字典路径:DataDictionary=./src/main/resources/FIX44_CUSTOM.xml - 重启客户端,让新字典生效,此时客户端会接受Tag110在SecurityList的重复组中。
临时方案:关闭字段验证(不推荐生产环境)
在配置中添加以下参数跳过验证,但会隐藏其他潜在问题:
ValidateFieldsOutOfOrder=N ValidateFieldsHaveValues=N
内容的提问来源于stack exchange,提问作者Gladiator9120
相关产品推荐
相关产品推荐

