文章

编写第一个路孚特实时应用前需要了解的10个要点

作者:

Olivier Davant
Product Manager Product Manager

概述

不可否认开发一个路孚特实时发布系统程式比以往更加简单快捷了。这很大程度上是由于路孚特实时SDK中发布的两个API之一企业信息API更简单易用了(了解更多请阅读这篇文章)。然而,即使这个新API比以往的几代API都简单易用,在开始搭建第一个程式之前您还是需要掌握几个概念。这些概念在路孚特实时SDK附带的详尽的说明文档集中有所解释。但众所周知,在开始接触一个新的API时,阅读几百页说明文档并不是一个好选择。因此,我认为在这篇文章里总结这些概念是非常有帮助的。我希望您会对这篇文章感兴趣并且它能帮助你开启路孚特实时应用的开发的旅程。

内容

  1.  路孚特实时服务带来一个全球综合数据流
  2.  路孚特实时服务在路孚特实时数据发布系统和云端传输服务中都可以获取
  3.  路孚特实时生态系统包括发布方,获取方和混合应用
  4.  路孚特实时应用使用发布/订阅模式
  5.  数据项由3个元素(名字,类型和服务)唯一标识
  6.  您可以在多个不同的API中选择来搭建您的应用
  7.  路孚特实时API是面向信息流的
  8.  路孚特实时API是事件驱动的
  9.  字段由数据字典定义
  10.  您并不孤独----也许原力不与你同在,但路孚特开发者社区在

1.   路孚特实时服务带来一个全球综合数据流

路孚特实时服务是一个能传输完整tick数据和深度市场数据的低延时全球综合数据流。这个综合数据流将您的应用和上千个交易所和OTC交易市场连接起来,并且这个覆盖范围还在不断地扩展。路孚特实时服务可以通过客户本地部署的路孚特实时发布系统(原名TREP)获取或者通过云端的路孚特实时优化服务(RRTO)获取。

RRTO云服务现在是一个基于网络带宽优化的服务,但我们正研究将来在云端提供完整的tick服务。

2.   路孚特实时服务在路孚特实时数据发布系统和云传输服务中都可以获取

路孚特实时数据可以通过路孚特实时优化云端服务或者本地部署的路孚特实时发布系统(RTDS)传送到您的应用。RTDS给您提供路孚特实时数据流和其它可选的第三方数据流。

决定使用哪种方式传送(通常由客户市场数据组选择)的准则如下:

  • 使用场景:您需要获取数据还是发布数据或者是都需要?您需要故障容错吗?您需要同时运行多个您的应用实例吗(生产环境,备份环境,开发环境)?
  • 连接到平台的应用和用户的数量
  • 您需要的数据类型(full tick数据,整合数据,延时数据)
  • 您连接到平台的可用带宽
  • ……

3.   路孚特实时生态系统包括发布方,获取方和混合应用

路孚特实时发布系统(RTDS)不仅可以用于获取路孚特实时数据,还可以选择性地发布您自己的数据以便其它人获取。确实,由于RTDS组件是非常高效的实时数据流架构,所以它们高度地使用于中层架构,构建出金融应用的生态系统。数据供应方搭建服务以发布特定的内容,数据消费方使用这些内容用以他们特定的用途(例如:交易显示应用,黑盒交易应用,等)。在一些情况下,单一的应用甚至可以同时作为消费方和供应方以为现有的数据填加额外价值(例如:计算引擎,增值服务器,等)。

在这个概念下,路孚特实时数据和RTDS应用可分为以下3种类型:

  •  消费方应用:订阅平台上提供的数据项和/或贡献到平台上提供的数据项
    注:贡献的概念在以下章节作详述。
  • 供应方应用:发布数据项并且可以选择性地接收消费方贡献的应用。
    注:供应方应用既可以是交互的也可以是非交互的。详情请看相关教程
  • 混合应用:可以从实时平台获取数据,作转换并重新发布以供其它应用消费。

4.   路孚特实时应用使用发布/订阅模式

实时数据在RTDS和路孚特实时应用之间的交换依赖于发布/订阅模式。在路孚特实时系统中,这个模式由数据贡献的概念完成:

  •  发布:供应方应用发布数据给消费方应用。一旦数据内容变动,供应方发布新数值到实时平台以保证消费方应用获得最新数据。
  •  订阅:消费方应用从一个或多个供应方订阅一个或多个数据项。当消费方订阅了一个数据项,平台和应用之间会打开一个为这个数据项专用的通讯流,接下来,平台使用这个通讯流保证消费方接收到供应方发布的最新数值。
  • 贡献/分发:消费方应用可选择性地发出Post消息,消息包含对某个供应方发布的数据项的数据贡献。当供应方接收到贡献,它可以选择确认(ACK)或者拒绝(NAK)这个贡献。然后,基于供应方的内部逻辑,供应方会把贡献数据转发到一个外部系统,或重新发布,又或者用于其它任何用途。
    注:供应方应用必须在程序上随时准备接收贡献(Post消息)。并非所有的供应方都实现了这个功能。

这3个概念如下图所示。下图的贡献中,展现了供应方重新发布数据。但这只是其中一个例子。如上所述,供应方的行为完全取决于供应方的应用实现。

注:路孚特实时发布系统提供了其它更复杂的方法以在应用之间交换数据(例如:私有流,通用消息)。这些高级概念在这篇文章中不会覆盖。关于这些高级概念的更多详情请查阅Real-Time SDK文档

 

5.   数据项由3个元素(名称,类型和服务)唯一标识

平台上的一个数据项代表着真实世界中的一个金融对象,比如一支股票的报价或者一支股票的订单簿。由于真实世界中的这些对象有不同的类型(报价,订单簿……),代表它们的数据项使用不同数据结构并且有不同的行为表现。例如:

  •  在Euronext Paris交易的AIRBUS股票报价(1级数据)由一个叫AIR.PA的数据项表示。AIR.PA由一列字段组成,这些字段包含着这只股票报价的不同数值(出价,要价……)。
  • AIRBUS股票的订单簿( 2级数据)由另一个也叫AIR.PA的数据项表示。这个AIR.PA由几列字段构成。每一列字段代表了订单簿中一个的订单。一列中的所有字段包含了这个订单的不同数值(报价,数量,方向)。

为了区分数据项,我们使用它们的名称(如AIR.PA)。但如上例所见,真实世界关联于同一股票的两个不同对象所对应的两个数据项也许会有同样的名称。因此,我们需要一个另外的元素来区分平台上的数据项。这第二个元素是数据项类型,例如当代表1级市场价格数据时它可以是MMT_MARKET_PRICE,当代表完整订单簿时它可以是MMT_MARKET_BY_ORDER。由于相同名称和类型的数据项也有可能由不同的平台服务(也就是来源)发布, 第三个元素也是必要的。比如,您的平台也许提供了由不同供应商发布的几个服务。因此,为了完全识别平台上的一个数据项,发布的服务也需要标识。

这3个元素(名称/类型/服务)唯一地识别实时平台上的每个数据项。当您订阅或发布任何数据项时都需要把它们标识出来。

在以上例子中:

  • AIRBUS 1级市场价格由如下识别:
    • 名称:AIR.PA
    • 类型:MMT_MARKET_PRICE
    • 服务:ELEKTRON_DD
  • AIRBUS 2级完整订单簿由如下识别:
    • 名称:AIR.PA
    • 类型:MMT_MARKET_BY_ORDER
    • 服务:ELEKTRON_DD

关于数据项名称

金融世界使用不同的识别码来命名不同的金融工具。使用哪个识别码系统系统取决于交易所在的地理位置。它可以是美国和加拿大使用的CUSIP数字,或者英国使用的SEDOL码,又或者是德国使用的Wertpapierkennnummer(WKN)编码。显然,像路孚特实时数据流这样的全球数据流需要一个统一的代码系统以识别全球的金融工具。

一个金融工具可以在多个证券交易所交易(如苹果股票在纳斯达克,纽交所,德交所……),因此这个代码必须能唯一地识别某个交易所的某个金融工具。并不是每个代码系统都能提供这样的特性。例如,ISIN码作为一个国际代码,并不能区分在不同交易所交易的工具。因此,仅凭ISIN码是不足以识别一个工具的。它必须补充一个交易所代码以达到准确性。

为了解决这个全球命名唯一性的问题,路透定义了它自己的基于RICs(Reuters Instrument Codes)的代码系统。这是个国际代码系统并且可以唯一地识别在不同交易所交易的工具。例如:RACE.MI这个RIC代表在米兰证券交易所交易的FERRARI,而RACE.N代表在纽约证券交易所交易的同一股票。

值得注意的是路孚特实时数据平台和它的应用程序接口都是对识别码无要求的。也就是说您的应用(消费方和供应方)可以使用任何命名系统(只要它们使用相同的命名系统)。然而,值得提出的是RICs广泛的应用于路孚特实时平台。例如所有的路孚特实时服务都使用RIC。

关于数据项类型

一个数据项的域类型定义了数据项的数据构造,语义和行为,它是一个唯一的数值。因为相同金融工具的不同的数据项可以有相同的名称,因此在应用发布或者订阅一个数据项时标识出域类型非常重要。例如,域类型为MMT_MARKET_PRICE (6)时RIC “6758.T”代表索尼股票报价1级数据,然而域类型为MMT_MARKET_BY_ORDER (7)时RIC “6758.T”代表相同股票的完整订单簿2级数据。

关于服务

供应方应用通过各种服务发布数据项到RTDS。每个服务有一个唯一的名称和唯一的ID。一个服务可以给一个或多个域类型提供数据项。值得注意的是不同的服务可以 提供拥有相同名称和相同域类型的数据项,并且数据值可以不相同。因此,由不同服务发布的拥有相同名称和相同域类型的数据项应看作不同的数据项。例如:两个不同的供应方可以同时发布一个域类型为MMT_MARKET_PRICE (6)且拥有相同RIC的数据项,然而数据值由不同的算法计算。

6.   您可以在多个不同的API中选择来搭建您的应用

为了发布数据或者从路孚特实时系统获取数据到您应用,您需要使用路孚特实时API中的一个。取决于您搭建应用使用的语言和技术,有几个不同的API可供选择。

您可选择使用实时SDK和相应的软件库,对应以下两个API:

  • 企业传输API/ The Enterprise Transport API(ETA)
    这是一个支持Java和C/C++的下层API。它为有非常高吞吐量和低延时要求的应用而设。
  • 企业信息API/ The Enterprise Message API(EMA)
    这是一个支持Java和C++的相对上层API。EMA基于ETA而建,兼顾易用性和高性能。

或者您也可以选择使用由平台提供的WebSocket API:

  • WebSocket API为价格流和实时服务而设

这个API和EMA一样精简,但通过WebSockets与平台交换JSON请求/应答消息。归功于WebSocket技术,这个API可以简便的整合到多个客户技术环境中,如scripting和web。

注:这个API从RTDS3.1及以上版本开始提供。但不支持publication功能。

战略性APIs vs 传统APIs

过去曾使用传统路孚特实时APIs的开发者也许想要了解哪些战略性API和哪些传统API大致对应。以下图表提供一个概况:

注:

  • UPA(the Ultra Performance API/高性能API)重命名ETA(企业传输API/ The Enterprise Transport API), UPA和ETA为相同API。
  • 此图中,SFC和RFA传统API与EMA置于同层,但它们并不提供完全相同的功能。同理,SSL API和RV API与ETA也是如此。

7.   路孚特实时API是面向消息的

路孚特实时API是面向消息的API,它依靠数据模型和消息收发数据项。路孚特实时文档中常提及这些模型,消息和格式。它们通常由缩略词DMM, RDM, OMM和RWF指代。这个章节会给您一个关于这些缩略词和相关概念的概述。

  • 域消息模型/Domain Message Model(DMM)

DMM描述了一系列特定的数据构造,语义和行为以定义真实世界中的数据对象。例如:某个DMM被定义为描述1级市场价格和相关的行为。某三个不同的DMM被定义为描述不同类型的2级数据(完整订单簿,市场深度信息,做市商报价和交易信息)。还有许多其它DMM,包括一个描述收益率曲线的DMM。

即使现已有大量域消息模型存在,您还是可以描述新的DMM来指代您应用域中的其它对象。每个DMM都有一个唯一的域类型数值以作辨认。需要注意的是我们的所有实时API都不会去处理DMM(例外:EMA会处理“登陆”,“源目录”和“字典”模型)。DMM的数据结构,语义和行为被记录并且需要由应用的更高层管理。我们的API只会传输域类型数值,以便应用知道它接收的是什么类型的数据和如何处理这些数据。

  • 路孚特域模型/Refinitiv Domain Models(RDM)

RDM是路孚特定义的DMM。与路孚特实时SDK文档一起提供的RDM使用指南中定义了一系列RDM和它们的数据构造,语义和行为。所有的RDM的域类型数值都小于128.

  • 用户定义域模型/User Defined Domain Models

用户定义域模型是由除路孚特以外的第三方定义的域模型。它们可以是为了解决RDM无法解决的特定用户需求而定义的。用户定义域模型的域类型数值必须在128-255之间。如有需要,域模型设计者可以和路孚特一起把他们的模型定义为RDM。这最大限度地允许了未来RDM的定义以及其它路孚特实时产品的通用性。

  • 开放消息模型/Open Message Model(OMM)

OMM是讯息头和数据构造的合集,这些讯息头和数据构造代表着数据容器(字段列表,映射,向量)和原类型。DMM和RDM依赖OMM定义它们的相关数据结构。

  • 路透有线格式/Reuters Wire Format(RWF)

RWF是OMM的编码体现。RWF是为了减少传统有线格式的数据分发成本而设的高度优化的二进制格式。

路孚特WebSocket API使用JSON来实现OMM和它的“预显示”数据值。这些JSON消息传送和其它实时SDK API相似的DMM和RDM。作为实时SDK应用,Websocket应用必须依赖域类型来正确处理接收到的消息。

8.   路孚特实时API是事件驱动的

路孚特实时API是异步的,事件驱动的API,它们依赖于发布/订阅模式运行。这意味着您不能仅使用简单的函数调用获取数据项的值。使用实时API,您必须:

  1. 调用函数以订阅您想要的数据项。
  2. 等待API异步回复您定义的函数和您请求的数据项的值。

如果您标明需要更新,API会在数据项有最新值发布时重新调用您的函数以保持您的数据更新。

以下两个范例进一步解释这个流程,一个是1级数据项(即股票),另一个是2级数据项(即完整订单簿)。请保证您对以下5个概念熟悉以保证您能理解这个流程。

注:这个章节中以下的解释和例子是从消费方应用的角度给出的,因为它更简洁易懂并且是最广泛应用的。同样的概念,或关于供应方和混合应用的反面角度的概念可以从这些解释推演。

重要概念

订阅

这是一个表明您想要接收某个数据项值的行为。订阅行为由调用一个实时API完成。例如,当使用企业消息API(EMA)时,您调用OmmConsumer类中的registerClient函数以订阅一个数据项。当您订阅一个数据项时您希望API给您发送包含数据项的值和状态的事件和消息。

如果您不再想要接收一个数据项的事件,您可以关闭这个订阅(即取消订阅)。在EMA中这由调用OmmConsumer类中的unregister函数完成。

数据项流

订阅数据项的动作引发一个数据流的开启,在数据流中您会接收到包含订阅数据项的值和状态的事件和消息。在EMA中这个数据流由一个数字处理器识别,这个数字处理器由您订阅时使用的registerClient函数返回。

影像

一个数据项的影像是代表这个数据项的所有值的集合。这个集合由您订阅的数据项的模型类型定义。应用常需要在当地(即内存)保留一份数据项影像。这个影像缓存并不由我们的实时API管理,而必须由应用实现并使用从API接收的消息保持影像更新。

影像的值由API发送的刷新消息而得。在EMA中这些消息经由订阅时注册的OmmCosumerClient接口中的onRefreshMsg回调函数发送。对于1级数据项,刷新消息包含整个影像的值的完整集合。相反,对于2级数据项,刷新消息可以不是完整的,而只包含影像的一部分。因为2级影像可以是非常庞大的,所以它们由一系列刷新消息和更新消息传输,这些消息由应用整合。

更新

更新是一个数据项的值的子集,这些子集中的值刚发生改变。更新并不包含整个完整的影像,而只包含那些发生改变的值。将这些值施加到最新的影像的数据项中,可保持影像更新。

更新由API通过更新消息发送。在EMA中这些消息由订阅时注册的OmmCosumerClient接口中的onUpdateMsg回调函数发送。更新消息总是包含应用已接收过的数据片段的值。

状态事件

状态事件指出数据项值的状态或者相关事件流的状态。状态由API经由状态消息发送。在EMA中,这些消息由订阅时注册的OmmCosumerClient接口中的onStatusMsg回调函数发送。

流程范例

1级数据项范例

这个范例解释了当订阅1级数据项(如股票报价)时,应用和企业消息API之间的可能应答。这包含了订阅,接收数据(几个影像和更新)和状态,订阅结束。

  • 步骤1:应用调用registerClient以订阅1级数据项并注册一个客户回调对象。这个对象会被API用于向应用发送消息。在其它参数中,应用标明数据项的名称,发布的服务和模型类型(即APPL.O, ELEKTRON_DD, MMT_MARKET_PRICE (6))。然后,registerClient函数返回,应用等待EMA发送与订阅数据项相关的消息。
  • 步骤2:EMA使过客户调用的onRefreshMsg发送一个刷新消息给应用。由于这个刷新消息是关于1级数据项的,它包含了数据项的完整的一整列的字段。应用可以选择性地缓存这些数据,创建一个当地的数据项影像。
  • 步骤3&4:EMA使用客户调用的onUpdateMsg发送几个更新消息给应用。每个更新消息包含有变动的数据项字段子集。如果应用保存了数据项的影像,应用可以用这些新值更新影像。
  • 步骤5:EMA使用客户调用的onStatusMsg发送一个状态消息给应用。这个消息包含一个可疑数据状态,表示接收到的这个数据项数据不再可信任。其中一个可能性是,比如供应方应用从数据源断开连接。当应用接收到这种状态,取决于它们的内部逻辑和目的,应用会有不同的表现。例如,一个计算引擎也许会决定中止计算并提出一个警报,而一个类似Eikon这样的桌面应用也许会决定继续显示数据,但会显示一个特殊的背景颜色(红色)以告知用户显示的数据不再可靠。
  • 步骤6:EMA发送一个新的刷新消息和一个Ok数据状态。这表示导致可疑数据状态的问题已解决并且数据已恢复可信任了。这个刷新消息同时也包含一个新的完整的字段列,应用可用以刷新数据项影像。
  • 步骤7,8&9: EMA定期地发送更新消息以保证应用使用最新的数据项改动进行更新。
  • 步骤10:应用调用unregister以关闭订阅。当调用unregister时,应用标明步骤1中registerClient函数返回的流处理器。unregister返回后,EMA不会再发送任何这个数据流的新消息给应用。

2级数据项范例

这个范例解释了当订阅类似订单簿这样的2级数据项时应用和企业消息API之间可能的交互。这包含订阅,数据接收(通过几个消息发送的影像和更新)和订阅结束。

  • 步骤1:应用调用registerClient以订阅2级数据项,并注册一个客户回调对象。这个对象会被API用以发送消息给应用。在其它参数中,应用标明数据项的名称,发布的服务和模型类型(即APPL.O, ELEKTRON_DD, MMT_MARKET_BY_ORDER (7))。随后,registerClient函数返回,应用等待EMA发送与订阅的数据项相关的消息。
  • 步骤2:EMA通过客户调用的onRefreshMsg发送一个刷新消息给应用。这是一个2级数据项刷新消息,因为2级数据项影像可能非常庞大,因此这个刷新消息也许并不包含完整的订单簿。在这种情况下刷新消息会包含一个标注,表明数据不包含完整的影像信息。如果应用要搭建一个当地的订单簿影像,它必须把接收到订单缓存,并在通过接下来的刷新消息和更新消息接收到的其它订单时整合成影像。
  • 步骤3: EMA发送第二个刷新消息,这个消息包含其它的订单。应用可以用它来整合出订单簿影像。
  • 步骤4: EMA发送包含已接收订单新值的更新消息。应用可以使用这些消息整合出订单簿的影像。需要注意的是这时订单簿影像仍不是完整的。
  • 步骤5: EMA发送第三个刷新消息,这个刷新消息标示着已完成。消息包含完成影像所需的最后剩余的订单。应用可使用它们整合出订单簿的影像。这时候影像是完整的。
  • 步骤6,7&8: EMA定期的发送刷新消息以保持应用有订单簿最新的改变以作更新。这些更新消息可用于更新订单簿影像。
  • 步骤9: 应用调用unregister以关闭订阅。当调用unregister时,应用标示出步骤1中registerClient函数返回的流处理器。unregister返回后,EMA不会再向应用发送任何这个数据项流的新消息。

9.   字段由数据字典定义

大多数在路孚特实时平台分发的数据项都使用字段列表的数据机构。这个数据结构由一列字段/值构成,这列字段/值传输了几个数据项的值。1级数据项或者2级订单簿的其中一个订单都是字段列构成数据项的范例。

字段列的每一个元素都有一个唯一的字段ID(FID)和一个值。包含字段列的OMM消息只会传输这两个元素。其它信息如字段名称或者类型都定义在一个字段字典中,这个字典或从实时平台下载,或在应用中以文件的形式提供。我们的实时API很大程度地使用这个字典以译出从平台接收的字段。

在任何事件中,您都需要字段字典,字典或是人类易读格式以让开发者可以查询出在应用中应如何编码,或者是编程格式以让您的应用可以读取字段的元数据。

从您的实时平台管理员处您可获取一份人类易读版本的字典文件。或者,您可以使用实时SDK提供的字典范例,但最好是使用平台本身的字典。字典范例文件(“RDMFieldDictionary”)可在实时SDK的”Cpp-c\etc”(C++版本)或”Java\etc”(Java 版本)文件夹中找到。

在编程上,字段信息可通过DataDictionary对象访问。在ETA中,这个对象必须由从文件或者从平台下载的字典初始化。在EMA中,使用EMA DictionaryUtility类,可以从一个字段列获取这个DataDictionary对象。

注:字段字典由另一个用于列出枚举值的字典完善。这个叫“enumtype.def”的字典定义了每个枚举字段的所有可能的值。这个字典和字段字典(“RDMFieldDictionary”)一样有文件版本。编程上,这个数据字典对象包含了枚举值信息。

10.   您并不孤独 ---- 原力也许不与您同在,但路孚特开发者社区与您同在

如果您是一名曾使用类似RFA的传统路孚特实时API的熟练开发者,您可能会发现转移到EMA非常轻松。然而,如果您过去从未使用这些API,或者您正开始学习低层企业传输API(ETA),您有时也许需要帮助。

如果您处于这种状况,您并不孤独。一个由路孚特员工和客户组成的活跃的开发者社区可以帮助您。这个社区可以在路孚特开发者社区平台通过开发者入口加入。在那里您可以找到文章,教程和大量的其它路孚特API相关的学习资料。您也可以在研讨论坛向社区提出问题并找到需要的答案。

加入路孚特开发者论坛,不要犹豫。它是免费访问的并对所有人开放。在这里注册并访问关于路孚特API的最好的学习内容。