Article

如何获取路孚特实时数据里所有RIC的列表?

Umer Nalla
Developer Advocate Developer Advocate

**知识前提* -- 必须了解路孚特streaming API,如EMA,并熟悉如何使用OMM市场价格(Market Price)数据(见文末相关教程链接)。*

        开发人员经常问:"我怎样才能获得所有可用的RIC列表?" ,但他们并没有意识到路孚特实时数据源承载了数百万的RIC(在撰写本文时超过8000万)。RIC过期和新RIC添加的情况一直在发生,如果他们要获取一份所有可用的RIC列表,数量无疑是巨大的。

        然而,路孚特实时边界设备Edge(如果您不熟悉Edge,请参见API概念指南)确实有一种功能,允许开发人员对符合一组指定标准的RIC列表进行基于标准的请求(CBR)-- 也称为BDS(广播数据流)。

        例如,您可能想找到以下信息:

  • 欧洲期货交易所的所有国债期货证券的列表
  • 香港交易所的现金期权和期货期权的所有RIC列表
  •  所有亚太地区交易所的所有RIC列表

        从开发人员的角度来看,您使用CBR/BDS功能的接口是OMM SymbolList域。路孚特的实时API及RFA API里包含了开发者指南或API概念指南,您可以从这些指南里详细了解OMM SymbolList域。本文将会对该部分内容做一个简要介绍。

 

SymbolList域

        SymbolList域是OMM对传统的链式(Chain)机制的替代(更多细节见关于链对象)。在这种情况下,应用程序使用SymbolList名称(代替RIC代码)提出SymbolList域请求,而服务器则以包含相应RIC的映射结构作出回应。

        例如,如果我从路孚特实时数据服务向“.AV.O“(纳斯达克最活跃的25个RIC)提出SymbolList请求,我将收到类似这样的回复:

        如上图所示,返回的是一个映射列表(我在本教程中使用RFA Java OMM Viewer的例子,因为它提供了一个方便的GUI界面)。每个映射条目的关键值是RIC;RANK_POS字段是该RIC对应的排名,它在这个特定的SymbolList(即前25名)的背景下是合理的。对于实际的CBR/BDS响应,您将不会得到这个字段(见下文)。

        需要澄清一点,“.AV.O”不是CBR/BDS,它碰巧是路孚特实时数据服务支持的SymbolList名称。

        在总结了SymbolList之后,下面我将继续解释CBR/BDS机制。

 

制定您的RIC选择准则

        我在前面提到,您可以通过指定一组要匹配的值来定义一个基于标准的请求。您要如何做到这一点?您可以指定哪些类型的标准?

技术上的前提条件

        要定义匹配标准,您需要访问Edge上的RRSC控制台应用程序。您需要做的第一件事是与您的市场数据团队沟通,确认他们能够在贵司的Edge设备上创建BDS术语。

创建BDS术语

        一个BDS术语是由一个或多个SQL术语、其它BDS术语以及File术语组成的。对于本文的主题,即发现与某些选择标准相匹配的RIC列表,SQL术语是最合适的。使用File术语(一个填充有RIC列表的文件)需要事先对RIC有所了解,因此在发现RIC方面没有意义。

创建SQL术语

        SQL术语是用于查询Edge数据库的SQL语句,从查询中获得的RIC列表被用作基于标准的请求的响应。例如,以下几个字段可用于构建SQL查询:

SQL字段 用户输入类型 备注
Exchange ID 数值 该字段的值可以在每个路孚特API自带的enumtype.def文件中找到,它位于文件中的RDN_EXCHD2和RDN_EXCHID的Enum定义中。
Exchange ID (Name) 文本等值 可使用SQL查询编辑器进行选择(见下文)
Record Type 数值 记录类型,如债务、期货、期权等(见以下链接)
Record Type (Name) 文本等值 记录类型,如债务、期货、期权等(见以下链接)
RIC Name 文本 通配符,详见下表

您可以在本网站关于使用FID259(RECORDTYPE)的文章中找到关于记录类型的解释。

 

RIC名称的通配符

        RIC名称匹配标准可以使用以下通配符

描述 字符 例子 含义
单字符 ‘_’ IBM._ 所有以“IBM.”为前缀且后面包含任何单字符的RIC,例如IBM.O,IBM.N,IBM.T。
多字符

‘%’

%.HK 所有来自香港交易所、以“.HK”为后缀的RIC。
范围

[]

800[2-6].HK 代表以下这些RIC:8002.HK,8003.HK,8004.HK,8005.HK和8006.HK。
否定 ‘^’ _[^#]% 所有不是链式记录的RIC(即不是0#GC:,0#.FTSE,1#.HSI等)。

使用Edge控制台

        对Edge控制台的访问通常只限于贵司的市场数据管理团队。但是,如果他们不熟悉它的使用,下面的内容应该会有帮助。

        BDS术语和SQL术语可以通过Edge控制台应用程序的任务菜单创建。

由于您的BDS术语是由一个或多个SQL术语组成的,所以首先需要创建SQL术语

上图显示了在构建SQL术语时可以匹配的更完整的标准/字段集。现在,让我们来创建一个SQL术语。

        上面的例子中,SQL查询将从阿姆斯特丹交易所(77)、洲际交易所(758)和斯德哥尔摩交易所(70)中选择所有的RIC。

        接下来这个SQL术语将被保存为”SQL_EUROPE_V1”:

SQL术语创建好之后,我可以继续创建BDS术语来使用这个SQL术语。

我可以选择一些术语来构建我的新BDS术语,在这里我会选择上面创建的SQL_EUROPE_V1术语。然而,正如您所看到的,您可以选择多个SQL术语以及其它现有的BDS术语,这将允许您根据需求使您的选择变得简单或复杂。

最后,我将这个BDS术语保存为BDS_EUROPE_V1

现在我有了SQL术语和一个引用SQL术语的BDS术语。

我演示得很简单,但是,正如之前所解释的,您可以在一个BDS术语中使用多个SQL术语。例如,下面的BDS术语是由3个SQL术语组成的。

测试BDS术语

        为了测试新的BDS术语,我们需要一个能够请求和处理SymbolList域的应用程序。路孚特所有具有OMM功能的API包(包括企业消息API即EMA)都包含了一个简单的SymbolList例子,就代码行数而言,这应该是最短的。为了简洁起见,我将使用EMA的Java例子ex270_SymbolList

        在继续演示之前,需要指出的是,如果我对上述BDS_EUROPE_V1术语进行请求,它将返回大约740000个RIC,这对于测试来说并不理想。有鉴于此,我创建了另一个SQL术语,其匹配范围小得多,如下所示。

请求BDS

        如上所述,我将使用ex270_SymbolList,它可以在Java RT-SDK软件包的Java\Ema\Examples\src\main\java\com\refinitiv\ema\examples\training\consumer\series200\文件夹中找到(C++版本的RT-SDK中也有一个类似的270_SymbolList例子)。

        当请求更常用的市场价格(Market Price)数据时,您需要在请求消息中指定一个MARKET_PRICE域,比如:

    	
            

RFA : ommmsg.setMsgModelType(RDMMsgTypes.MARKET_PRICE)

EMA : reqMsg.domainType(EmaRdm.MMT_MARKET_PRICE) // default domain in EMA - so not explicitly required

然而,如果您看一下example270的main()方法,您会发现在创建ReqMsg时,请求消息的域被指定为MMT_SYMBOL_LIST。

    	
            

consumer.registerClient(EmaFactory.createReqMsg().domainType(EmaRdm.MMT_SYMBOL_LIST)

    .serviceName("ELEKTRON_DD").name("BDS_VOD"), appClient, 0);

您也会注意到,我已经将我所请求的SymbolList的名称改为BDS_VOD

        MarketPrice数据和SymbolList之间的另一个关键区别是,SymbolList响应是以映射(Map)的形式出现的,而MarketPrice响应则使用更简单的FieldList。

        如果您熟悉OMM Map,您可以跳过下面的部分,转到结果部分。

        您可以在我们的RT-SDK和RFA API软件包中的《开发者指南》或《API概念指南》中更详细地了解OMM Map。以下内容可以提供一个简要介绍。

下面的例子截取了SymbolList Refresh响应的一部分,它有助于更好地理解Map的结构。

    	
            

<REFRESH domainType="SYMBOL_LIST" streamId="5" containerType="MAP" flags="0x1F8

(HAS_MSG_KEY|SOLICITED|REFRESH_COMPLETE)" State: Open/Ok/None - text: "All is well">

<key serviceId="257" name="BDS_VOD"/>

    <dataBody>

        <map countHint="30" containerType="FIELD_LIST" >

            <summaryData>

                <fieldList>

                    <fieldEntry fieldId="6456" data="06"/>

                </fieldList>

            </summaryData>

            <mapEntry flags="0x00" action="ADD" key="VOD.BN" >

                <fieldList>

                    <fieldEntry fieldId="1" data="0D42"/>

                </fieldList>

            </mapEntry>

            <mapEntry flags="0x00" action="ADD" key="VOD.MW" >

                <fieldList>

                    <fieldEntry fieldId="1" data="19A1"/>

                </fieldList>

            </mapEntry>

            <mapEntry flags="0x00" action="ADD" key="VOD.SId" >

                <fieldList>

                    <fieldEntry fieldId="1" data="1AD0"/>

                </fieldList>

            </mapEntry>

            ....

            ....

            <mapEntry flags="0x00" action="ADD" key="VOD.SI" >

                <fieldList>

                    <fieldEntry fieldId="1" data="0BEB"/>

                </fieldList>

            </mapEntry>

        </map>

    </dataBody>

</REFRESH>

上述Refresh消息片段的分解如下:

  • 首先“head”部分确认了Refresh消息的各种属性,包括域的类型、唯一的streamID、状态信息、名称和容器类型。
  • 其次Map 有效载荷(Payload)包括一个摘要部分,它只是再次重复域的类型,这在BDS响应的情况下不是特别有用。然而,对于像二级交易委托账本(即OrderBook,也使用Map)之类,摘要部分将包含货币、交易单位、交易所ID等细节,即交易委托账本中所有交易的共同值。
  • 接下来是单独的Map条目:
    • 添加(Add)动作表明这是一个需要添加到本地缓存的新条目,也就是你在内存中对Map的本地表示。
    • 如果这是一条更新(Update)消息,它可能包含带有删除动作的条目,表示该条目已经过期,应该从本地缓存中删除(对于一个OrderBook,您也可以收到Update的动作,例如当一条委托交易的价格或数量发生了变化)。
    • BDS SymbolList条目的Map键是实际的RIC本身(对于一个OrderBook,键值可以是交易价格+交易方)。
    • fieldList包含一个或多个与Map条目相关的字段。在这里,我们只有RIC的一个许可代码(对于一个OrderBook,它将是价格、交易方、数量、交易时间等)。

 

BDS SymbolList请求的结果

        一旦我定义了SQL_VOD术语并将其链接到BDS_VOD术语,我就会使用一个消费者应用程序样本来请求它。我从ELEKTRON_DD服务中为BDS_VOD做了一个SymbolList请求,结果如下所示:

贵司使用的服务名称可能有所不同,它需要是一个提供路孚特实时数据的服务,通常被命名为IDN_RDF, ELEKTRON_DD或类似的名称。

由于上述请求返回的RIC数量较少,完整的Map以单个RefreshMsg的形式到达。更实际的BDS查询很可能包含更多的RIC,因此这些消息将以多个RefreshMsg的形式交付,其中每个RefreshMsg包含一部分的RIC。最后一个RefreshMsg将把Complete标志设置为”true”,表明所有部分都已发送,OrderBook交付已经完成。

 您将注意到上述REFRESH响应中的REFRESH_COMPLETE标志,这是一个自包含的RefreshMsg。您可以在代码中通过测试Complete标志来测试最后一个RefreshMsg,例如:

    	
            

public void onRefreshMsg(RefreshMsg refreshMsg, OmmConsumerEvent event)

{

...

    if (refreshMsg.complete())

        System.out.println("Final RefreshMsg received");

...     

}

        注意:您可以对一个BDS提出快照(Snapshot)请求并收到当前匹配的RIC列表,之后数据流将被关闭。但是,如果您提出流式(Streaming)请求,该数据流将保持开放直到您关闭它。您将收到包含Map条目的更新(Update)消息,其中包括:

  • 当符合您的标准的任何新的RIC可用时,进行添加(Add)操作。
  • 当任何以前匹配的RIC过期时,进行删除(Delete)操作。
  • 当现有RIC的权限代码发生变化时,进行更新(Update)操作(这种情况很少发生)。

        一天当中,可能有一些RIC过期,也可能有一些新的RIC被创建。所以通常情况下,更新(Update)消息的Map里有对不同条目添加(Add)和删除(Delete)的混合操作。

 

使用案例

        我遇到了两个关于BDS/CBR功能的实际使用案例:

  • 维护黄金源:一个SymbolList消费者在快照模式下运行数次。这些运行发生在午夜后不久,每次都调用不同的BDS以涵盖各种交易所;之后应用程序与现有的存储库进行delta从而得出新的和过期的RIC列表;然后新的和过期的列表被用来更新黄金源。此时并不要求RIC本身的市场数据,黄金源在以后的市场开放时间内用于请求所需条目的市场数据。
  •  获取一天中的快照:一个SymbolList消费者在一天中的两个时间点以快照模式运行。一旦获得了完整的RIC列表,应用程序就会对每个RIC提出MarketPrice快照请求,并捕获市场数据。

        第一个用例在非交易时间提出BDS请求,主要是因为收到的RIC总数达到几百万,处理它们需要时间。此外,日间的需求是只为黄金源中的一部分RIC请求市场数据。

        相比之下,第二个用例只涉及几千个RIC,需求是捕捉SymbolList中返回的所有RIC的市场数据。

 

索取RIC的市场价格数据

        如果您的用例涉及到为SymbolList中返回的所有RIC请求市场价格(Market Price)域的数据,您可以利用SymbolList域的一个功能来自动请求所有RIC。

        RDM使用指南(每个API都有)的SymbolList部分更详细地描述了SymbolList的行为。下面是该文件的一个片段:

元素名称 类型 默认值 描述
SymbolListBehaviors ElementList ElementList里包含:DataStreams的ElekmentEntry,设置为0.

该元素表示从SymbolList中打开的单个RIC的任何预期数据行为。如果没有这个元素,单个数据流将不会被打开。

:DataStreams元素可以是以下值之一:

结果
0x0 消费者只对获得名称感兴趣,而不关心SymbolList中各个RIC的数据。这是默认值。
0x1 消费者感兴趣的是将SymbolList中的每个RIC打开为流式(Streaming)。
0x2 消费者感兴趣的是将SymbolList中的每个RIC打开为快照(Snapshot)。

例如,如果我修改上述例子的main()方法,加入以下内容,该例子将请求SymbolList,同时收到列表中每个RIC的MarketPrice快照数据。

    	
            

public static void main(String[] args)

{

....    

    ElementList symbolListBehaviors= EmaFactory.createElementList();

    ElementList dataStream= EmaFactory.createElementList();

    dataStream.add(EmaFactory.createElementEntry().uintValue(EmaRdm.ENAME_DATA_STREAMS,

        SymbolListDataStreamRequestFlags.SYMBOL_LIST_DATA_SNAPSHOTS));

    symbolListBehaviors.add(EmaFactory.createElementEntry()

        .elementList(EmaRdm.ENAME_SYMBOL_LIST_BEHAVIORS, dataStream));

...

...

    consumer.registerClient(EmaFactory.createReqMsg().domainType(EmaRdm.MMT_SYMBOL_LIST)

        .serviceName("ELEKTRON_DD").name("BDS_VOD").payload(symbolListBehaviors),

            appClient, 0);

...

}

 如您所见,我正在创建一个带有DataStream条目的ElementList,指定Data Snapshots为值,将其添加到SymbolListBehaviors ElementList中,然后将其作为BDS SymbolList请求消息的有效载荷(Payload)。

在处理MAP Payload之外,我还修改了onRefreshMsg()程序以处理FIELD_LIST,这样在收到市场价格数据时也会输出到控制台。

    	
            

public void onRefreshMsg(RefreshMsg refreshMsg, OmmConsumerEvent event)

{

...

    if (DataType.DataTypes.MAP == refreshMsg.payload().dataType())

        decode(refreshMsg.payload().map());

    if (DataType.DataTypes.FIELD_LIST == refreshMsg.payload().dataType())

        decode(refreshMsg.payload().fieldList(), false);

...

}

当我重新运行修改后的例子时,我收到了BDS SymbolList响应和SymbolList中每个RIC的快照。

这种数据自动请求功能不是BDS特有的,因此可以普遍用于SymbolList请求。

 

结束语

总结一下上述信息:

  • 路孚特实时Edge设备提供实时的RIC发现功能。
  • 要使用该功能,您需要访问Edge 控制台应用程序RRSC。
  • 该功能被称为广播数据流 - BDS(也称为基于标准的请求 - CBR)。
  • BDS术语可以由SQL术语、File术语及其它BDS术语组成。
  • OMM消费者应用程序需要使用定义的BDS术语名称作为RIC名称,提出一个SymbolList域请求。
  • 响应以一个或多个Refresh消息的形式发送(最后一个Refresh消息的Complete标志设置为”true”)。
  •  有效载荷(Payload)由Map组成,其中每个Map条目的关键值是实际的RIC本身,每个条目也有一个动作:添加(Add)、删除(Delete)或更新(Update)。
  • 快照(Snapshot)请求将提供在该时间点上符合SQL术语标准的RIC列表。
  •  当RIC过期或创建新的RIC时,流式(Streaming)请求将继续接收更新(Update)消息。
  •  通过使用SymbolListBehaviors,还可以为SymbolList中的每个RIC自动请求快照(Snapshot)或流数据(Streaming data)。

 

补充资源

您可以在链接面板中找到API、教程、咨询台和问答论坛的链接。