You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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数据字典

  1. 复制QuickFix默认的FIX4.4.xml文件,修改其中的SecurityList消息定义:
    在<message name="SecurityList" msgtype="y" msgcat="app">下的<group name="NoRelatedSym" required="Y">节点中,添加Tag110的定义:
    <field name="MinQty" required="N"/>
    
  2. 在FIX配置文件的[default]或[session]节中,添加自定义字典路径:
    DataDictionary=./src/main/resources/FIX44_CUSTOM.xml
    
  3. 重启客户端,让新字典生效,此时客户端会接受Tag110在SecurityList的重复组中。

临时方案:关闭字段验证(不推荐生产环境)

在配置中添加以下参数跳过验证,但会隐藏其他潜在问题:

ValidateFieldsOutOfOrder=N
ValidateFieldsHaveValues=N

内容的提问来源于stack exchange,提问作者Gladiator9120

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 04:20:52