Skip to content

Commit

Permalink
Doc patch
Browse files Browse the repository at this point in the history
update doc
  • Loading branch information
shinny-chengzhi committed Jun 11, 2018
1 parent 8181576 commit dce16a1
Showing 1 changed file with 115 additions and 74 deletions.
189 changes: 115 additions & 74 deletions general.rst
Original file line number Diff line number Diff line change
@@ -1,74 +1,115 @@
协议设计要点
==================================================

Differential Information Flow for Finance (DIFF) 被设计用于


设计目标
--------------------------------------------------
本协议被设计用于... 在这些方面

* 传输安全
* 简化终端编码
* 带宽自适应
* 扩展灵活性


传输协议
--------------------------------------------------
* 使用 websocket 作为传输协议

传输压缩
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
可以选择使用 websocket 的标准协议进行传输压缩

传输安全
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在 websocket 通道上使用 ssl 实现传输层安全加密及端对端验证


数据包
--------------------------------------------------
* 每个数据包均为 json 编码的 object, UTF-8 编码
* 每个数据包中均有一个 aid 字段,此字段值即为数据包名
* 允许数据包中携带协议中未写出的额外字段,接收方可以忽略这些字段的信息
* 当数值型字段的值为 NAN 时,在json中以字符串 "NAN" 表示。


异步通讯
--------------------------------------------------
协议按双向异步结构设计,任何一方都可以随时向对方发送数据包,也应该随时准备接收对方发来的数据包。发出的包之间无需等待对方回应。


完整数据截面的差分传输
--------------------------------------------------
当从主进程向扩展进程发送数据时, 与其它很多协议不同, 天勤的通讯协议并不会按照 "类别" 或 "请求", 将数据分解为多个包发送.
相反, 天勤只使用一种包 :ref:`rtn_data`, 来将主程序内存中的全部数据 (实时行情, 历史行情, 交易数据等) 送往扩展进程.
为减少数据量, 主程序每次发送 :ref:`rtn_data` 时, 通常只包含与前次相比有差异的数据. ( **注意: 协议并不承诺无差异的数据一定不会发送** )

基于这一协议, 我们强烈建议 **扩展进程应设法维护一个完整的业务信息存储区** (在内存,数据库或文件中), 并在每次收到 :ref:`rtn_data` 时更新此存储区中的内容.
扩展进程中的业务代码, 应从这一存储区中获取业务信息, 而 **不要直接让业务逻辑处理接收到的每个:ref:`rtn_data`包**


带宽自适应机制
--------------------------------------------------



协议实现
--------------------------------------------------
基于这一协议, 我们强烈建议 **扩展进程应设法维护一个完整的业务信息存储区** (在内存,数据库或文件中), 并在每次收到 :ref:`rtn_data` 时更新此存储区中的内容.
扩展进程中的业务代码, 应从这一存储区中获取业务信息, 而 **不要直接让业务逻辑处理接收到的每个:ref:`rtn_data`包**


按功能集扩展
--------------------------------------------------
上行包:
增加新的 aid

rtn_data 包:
增加新的字段




Differential Information Flow for Finance (DIFF) 协议
=======================================================================
目前市面上的接口通常以事件回调的方式进行信息交互,导致业务层对当前状况的全景缺乏了解,不便于编写复杂业务逻辑。而 DIFF 协议将异步的事件回调转为同步的数据访问,使得业务层能简单同步的访问业务数据,简化了编码复杂度。

DIFF 协议分为两部分: **数据访问** 和 **数据传输**

数据访问
------------------------------------------------------
DIFF 协议要求服务端维护一个业务信息截面,例如:

::

{
"account_id": "41007684", # 账号
"static_balance": 9954306.319000003, # 静态权益
"balance": 9963216.550000003, # 账户资金
"available": 9480176.150000002, # 可用资金
"float_profit": 8910.231, # 浮动盈亏
"risk_ratio": 0.048482375, # 风险度
"using": 11232.23, # 占用资金
"position_volume": 12, # 持仓总手数
"ins_list": "cu1609,...." # 行情订阅的合约列表
"quotes":{ # 所有订阅的实时行情
"SHFE.cu1612": {
"instrument_id": "cu1612",
"datetime": "2016-12-30 13:21:32.500000",
"ask_priceN": 36590.0, #卖N价
"ask_volumeN": 121, #卖N量
"bid_priceN": 36580.0, #买N价
"bid_volumeN": 3, #买N量
"last_price": 36580.0, # 最新价
"highest": 36580.0, # 最高价
"lowest": 36580.0, # 最低价
"amount": 213445312.5, # 成交额
"volume": 23344, # 成交量
"open_interest": 23344, # 持仓量
"pre_open_interest": 23344, # 昨持
"pre_close": 36170.0, # 昨收
"open": 36270.0, # 今开
"close" : "-", # 收盘
"lower_limit": 34160.0, #跌停
"upper_limit": 38530.0, #涨停
"average": 36270.1 #均价
"pre_settlement": 36270.0, # 昨结
"settlement": "-", # 结算价
},
...
}
}

对应的客户端也维护了一个该截面的镜像,因此业务层可以简单同步的访问到全部业务数据。

* 业务截面的内容由各业务模块定义,例如 ref:`quote` ref:`trade` ref:`mdhis`
* 除非由业务模块的文档另行说明,否则业务截面中的数据应是自恰的。例如:任何时刻的业务截面都应包含 balance, static_balance 和 float_profit 字段,并且满足 balance = static_balance + float_profit
* 业务截面可能会包含协议中未写出的额外字段,使用方应忽略这些字段的信息,因此在扩展或新增业务模块时可以保持向后兼容性
* 部分字段可能会有多种数据类型,例如上述例子中的收盘和结算价在收盘前是字符串,在收盘后会更新为对应的数值

数据传输
------------------------------------------------------
DIFF 协议使用 json 编码通过 websocket 传输,因此可以使用 ssl 实现传输层安全加密,permessage-deflate 实现数据压缩。协议通讯为全双工模式,任何一方都可以随时向对方发送数据包,也应随时准备接收对方发来的数据包,发出的包之间无需等待对方回应。每个数据包中均有一个 aid 字段,此字段值即为数据包类型。

数据包类型
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* 业务信息截面更新:
DIFF 协议要求服务端将业务信息的变化以 JSON Merge Patch (https://tools.ietf.org/html/rfc7386) 的格式推送给客户端,例如:

::
{
"aid": "rtn_data", # 业务信息截面更新
"data": [ # 数据更新数组
{
"balance": 10237421.1, # 账户资金
},
{
"float_profit": 283114.780999997, # 浮动盈亏
},
{
"quotes":{
"SHFE.cu1612": {
"datetime": "2016-12-30 14:31:02.000000",
"last_price": 36605.0, # 最新价
"volume": 25431, # 成交量
"pre_close": 36170.0, # 昨收
}
}
}
]
}
- 以上数据包中的 ``"aid": "rtn_data"`` 表示该包的类型为业务信息截面更新包
- 整个 data 数组相当于一个事务,其中的每一个元素都是一个 JSON Merge Patch,处理完整个数组后业务截面即完成了从上一个时间截面推进到下一个时间截面。
- 处理过程中业务截面可能处于内部不一致的状态,例如上述例子中的 balance 更新后,float_profit 更新前,并不满足 balance = static_balance + float_profit,因此除非有特殊需求,否则业务层应等整个 data 数组都处理完成后再从业务截面中提取所需的数据
- 没有变化的字段服务端也可能发送,例如上述例子中的 pre_close。
- 服务端可以自行决定 data 数组的元素个数及每个元素中包含哪些更新,例如客户端不能假定更新行情最新价和成交量一定是在一个 JSON Merge Patch 中。
- 如果在处理完一个 JSON Merge Patch 后,某个 object 下的所有字段都被删除则也应将该 object 删除

* 业务信息截面更新请求:
DIFF 协议要求客户端发送 peek_message 数据包以获得业务信息截面更新

::
{
"aid": "peek_message"
}
- 服务端在收到 peek_message 数据包后会检查是否有数据更新,如果有则会将更新内容立即发送给客户端,如果没有则会等到有更新发生时再回应客户端。
- 当服务端发送 rtn_data 数据包后需要再收到 peek_message 才会发送下一个 rtn_data 数据包。
- 一个简单的客户端实现可以在连接成功后及每收到一个 rtn_data 数据包后发送一个 peek_message 数据包,这样当客户端带宽不足时会自动降低业务信息截面的更新频率以适应低带宽

* 指令包:
当数据包中的 aid 字段不是 rtn_data 或 peek_message 则表示该包为一个指令包,具体指令由各业务模块定义,例如 subscribe_quote 表示订阅行情,insert_order 表示下单

- 由于客户端和服务端存在网络通讯延迟,客户端的指令需要过一段时间才会影响到截面中的业务数据,为了使客户端能分辨出服务端是否处理了该指令,通常服务端会将客户端的请求以某种方式体现在截面中(具体方式由各业务模块定义)。例如 subscribe_quote 订阅行情时服务端会将业务截面中的 ins_list 字段更新为客户端订阅的合约列表,这样当客户端检查业务截面时如果 ins_list 包含了客户端订阅的某个合约,但是 quotes 没有该合约则说明该合约不存在

0 comments on commit dce16a1

Please sign in to comment.