From edac5d6cc4fcfb2c9e8eab0c4965693c312e130b Mon Sep 17 00:00:00 2001 From: shinny-hongyan Date: Tue, 2 Jun 2020 23:33:45 +0800 Subject: [PATCH] =?UTF-8?q?[BE-324]=20DIFF=20=E5=8D=8F=E8=AE=AE=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- funcset/mdhis.rst | 8 +- funcset/notify.rst | 51 ++++++----- funcset/quote.rst | 37 ++++++-- funcset/trade.rst | 223 +++++++++++++++++++++++---------------------- general.rst | 216 ++++++++++++++++++++++++------------------- index.rst | 2 +- 6 files changed, 301 insertions(+), 236 deletions(-) diff --git a/funcset/mdhis.rst b/funcset/mdhis.rst index cffa989..ffebf45 100644 --- a/funcset/mdhis.rst +++ b/funcset/mdhis.rst @@ -5,7 +5,7 @@ 请求订阅图表数据 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -终端通过发送 set_chart 包实现订阅图表数据 +终端通过发送 `set_chart` 包实现订阅图表数据 .. code-block:: javascript @@ -20,13 +20,13 @@ 需要注意几点: -* chart_id 为一个任意字符串,当多次发送的set_chart包中的chart_id重复时,后一次的请求将覆盖前一次。chart_id不相同则视为不同的订阅 -* 历史数据服务是订阅式而非查询式的。只要发送过一次 set_chart 请求,每当行情变化时,都会通过 rtn_data 包推送新的K线,不需要多次发送 set_chart 包 +* `chart_id` 为一个任意字符串,当多次发送的 `set_chart` 包中的 `chart_id` 重复时,后一次的请求将覆盖前一次。`chart_id` 不相同则视为不同的订阅 +* 历史数据服务是订阅式而非查询式的。只要发送过一次 `set_chart` 请求,每当行情变化时,都会通过 `rtn_data` 包推送新的K线,不需要多次发送 `set_chart` 包 图表数据同步 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -图表数据通过 `rtn_data` 包的 klines 字段和 ticks 字段进行差分发送, 如下所示: +图表数据通过 `rtn_data` 包的 `klines` 字段和 `ticks` 字段进行差分发送, 如下所示: .. code-block:: javascript diff --git a/funcset/notify.rst b/funcset/notify.rst index 90bf88c..00f172e 100644 --- a/funcset/notify.rst +++ b/funcset/notify.rst @@ -2,39 +2,40 @@ 通知 ================================================== -通知通过 `rtn_data` 包中的 notify 字段发送, 如下所示 +通知通过 `rtn_data` 包中的 `notify` 字段发送, 下面的 DIFF协议包为客户发送登录指令 ``{"aid": "req_login" ... }`` 后收到的通知。 .. code-block:: javascript { - "aid": "rtn_data", //数据推送 - "data": [ //diff数据数组, 一次推送中可能含有多个数据包 + "aid": "rtn_data", //业务信息截面更新推送 + "data": [ //diff数据数组, 一次推送中可能含有多个数据包 { - "notify": { //通知信息 - "2010": { - "type": "MESSAGE", //消息类型 - "level": "INFO", //消息等级 - "code": 1000, //消息代码 - "content": "abcd", //消息正文 + "notify": { //通知信息 + "2020": { + "type": "MESSAGE", //消息类型 + "level": "INFO", //消息等级 + "code": 401, //消息代码 + "content": "登录成功" //消息正文 } - }, + } } ] } +其中,消息类型 `type` 和 消息等级 `level` 字典定义如下表所示。消息代码 `code` 和 消息正文 `content` 由业务层确定。 -================= ================================================ -type 说明 -================= ================================================ -MESSAGE content是一个短字符串,通常只有1行 -TEXT content是一个长文本,通常有多行内容 -HTML content为HTML格式 -================= ================================================ - -================= ================================================ -level 信息等级 -================= ================================================ -INFO 普通消息 -WARNING 警告 -ERROR 错误 -================= ================================================ +================= ================= ================================================ +key 字典 说明 +================= ================= ================================================ +type MESSAGE content是一个短字符串,通常只有1行 + + TEXT content是一个长文本,通常有多行内容 + + HTML content为HTML格式 +----------------- ----------------- ------------------------------------------------ +level INFO 普通消息 + + WARNING 警告 + + ERROR 错误 +================= ================= ================================================ \ No newline at end of file diff --git a/funcset/quote.rst b/funcset/quote.rst index 8f3c234..4da989c 100644 --- a/funcset/quote.rst +++ b/funcset/quote.rst @@ -3,29 +3,52 @@ 行情报价数据 ================================================== +:: + + client server + + + + | {"aid": "subscribe_quote","ins_list": ..} | + +-------------------------------------------->+ + | | + | {"aid": "peek_message"} | + +-------------------------------------------->+ + | | + | | + | {"aid":"rtn_data", | + | "data":[{"quotes":{...}]} | + +<--------------------------------------------+ + | | + | | + + + + +行情报价订阅流程如上 + +1. 客户端发起请求订阅行情报价 +2. 客户端发送截面更新请求 +3. 服务端推送行情报价更新截面 + 请求订阅行情报价 -------------------------------------------------- -终端通过发送 subscribe_quote 包实现订阅行情报价 +终端通过发送 `subscribe_quote` 指令包实现行情报价的订阅 .. code-block:: javascript { - "aid": "subscribe_quote", //必填, 请求订阅实时报价数据 + "aid": "subscribe_quote", //必填, 请求订阅实时报价数据 "ins_list": "SHFE.cu1612,CFFEX.IF1701", //必填, 需要订阅的合约列表,以逗号分隔 } 需要注意几点: -* 合约代码必须带交易所代码, 例如cu1801应该写作 SHFE.cu1801. 目前支持的交易所为 CFFEX, SHFE, DCE, CZCE, INE -* 用户自定义的组合, 交易所代码都为 USER +* 合约代码必须带交易所代码,例如 `cu1801` 应该写作 `SHFE.cu1801`。目前支持的交易所为 ``CFFEX``, ``SHFE``, ``DCE``, ``CZCE``, ``INE`` +* 用户自定义的组合,交易所代码都为 `USER` * 合约代码及交易所代码都是大小写敏感的 -* 每次发送 subscribe_quote 时,应在 ins_list 中列出所有需要订阅的合约代码。多次发送 subscribe_quote,后一次的订阅列表会覆盖前一次的 - +* 每次发送 `subscribe_quote` 时,应在 `ins_list` 中列出所有需要订阅的合约代码。多次发送 `subscribe_quote`,后一次的订阅列表会覆盖前一次的 行情报价数据同步 -------------------------------------------------- -行情报价数据通过 `rtn_data` 包的 quotes 字段进行差分发送, 如下所示: +行情报价数据通过 `rtn_data` 包的 `quotes` 字段进行差分发送, 如下所示: .. code-block:: javascript diff --git a/funcset/trade.rst b/funcset/trade.rst index 5d5e48b..b2d0aff 100644 --- a/funcset/trade.rst +++ b/funcset/trade.rst @@ -5,29 +5,46 @@ 交易账户结构 -------------------------------------------------- +交易业务信息截面典型结构如下所示 + +.. code-block:: javascript + + { + "trade": { + "022000": { + "user_id": "022000", //登录用户名 + "trading_day": "20200604", //交易日 + "accounts": { ... }, //资金账户 + "positions": { ... }, //持仓 + "orders": { ... }, //委托 + "trades": { ... }, //成交 + "banks": { ... }, //签约银行 + "transfers": { ... } //银期流水 + } + } + } 用户(USER) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -一个用户由一个唯一的 USER_ID 标识. 每个用户的账户信息互相独立. - -在任一时刻, 一个用户的交易账户可以由以下信息完整描述 +一个用户由一个唯一的 `USER_ID` 标识,每个用户的账户信息互相独立。在任一时刻, 一个用户的交易账户可以由以下信息完整描述 -* 1-N个资金账户(ACCOUNT) -* 0-N个持仓记录(POSITION) -* 0-N个委托单(ORDER) +* 1-N个资金账户(ACCOUNTS) +* 0-N个持仓记录(POSITIONS) +* 0-N个委托单(ORDERS) -这些信息完整的描述了用户交易账户的[当前状态]. 需要注意的是, 用户过往的交易记录, 转账记录等并不在其中, 那些信息对于用户的交易动作没有任何影响. +这些信息完整的描述了用户交易账户的[当前状态]。需要注意的是, 用户过往的交易记录, 转账记录等并不在其中, 那些信息对于用户的交易动作没有任何影响。 -资金账户(ACCOUNT) +资金账户(ACCOUNTS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -每个资金账户由一个 ACCOUNT_ID 标识. 一个USER可以同时拥有多个ACCOUNT. 每个 ACCOUNT 中的各字段都使用同一币种. +一个用户 `USER` 可以同时拥有多个资金账户,每个资金账户可以由一个 `account_id` 标识,通常以币种(`currency`) +作为 `KEY`。 -下面是一个ACCOUNT的内容示例: +下面是一个 `ACCOUNT` 的内容示例: .. code-block:: javascript - "CNY": { + "CNY": { //account_id=currency //账号及币种 "user_id": "423423", //用户ID "currency": "CNY", //币种 @@ -62,11 +79,11 @@ } -持仓(POSITION) +持仓(POSITIONS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -每个持仓项描述一个合约的当前持仓情况. 通常以相应的合约代码(SYMBOL)作为KEY +每个持仓项描述一个合约的当前持仓情况,通常以相应的合约代码(`SYMBOL`)作为 `KEY` -下面是一个 POSITION 的内容示例: +下面是一个 `POSITIONS` 的内容示例: .. code-block:: javascript @@ -112,20 +129,19 @@ } -委托单(ORDER) +委托单(ORDERS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -委托单的单号: +委托单以相应的委托单单号(`order_id`)作为 `KEY`。`DIFF` 协议要求 -* 每个委托单都必须有一个单号, 单号可以是不超过128个字节长的任意中英文字符和数字组合. -* 单号由发出下单指令的终端负责设定. 它必须保证, 对于同一个USER, 每个单号都是不重复的. +* 每个委托单项必须有一个唯一的单号,单号支持任意中英文字符和数字组合,最大长度不超过256个字节 +* 委托单号由发出下单指令的终端负责设定,它必须保证同一个 `USER` 委托单号的唯一性 -委托单状态: +任何一个委托单的状态只会是这两种之一:`FINISHED` 或 `ALIVE` -* 任何一个委托单的状态只会是这两种之一: FINISHED 或 ALIVE -* FINISHED: 已经可以确定, 这个委托单以后不会再产生任何新的成交 -* ALIVE: 除上一种情况外的其它任何情况, 委托单状态都标记为 ALIVE, 即这个委托单还有可能产生新的成交 +* **FINISHED** 已经可以确定,这个委托单以后不会再产生任何新的成交 +* **ALIVE** 除上一种情况外的其它任何情况, 委托单状态都标记为 `ALIVE`,即这个委托单还有可能产生新的成交 -下面是一个 ORDER 的内容示例: +下面是一个 `ORDER` 的内容示例 .. code-block:: javascript @@ -159,17 +175,19 @@ } -成交记录(TRADE) +成交记录(TRADES) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -下面是一个 TRADE 的内容示例: +成交记录以相应的成交单号(`trade_id`)作为 `KEY` + +下面是一个 `TRADE` 的内容示例 .. code-block:: javascript "123": { //trade_key, 用于唯一标识一条成交记录. 对于一个USER, trade_key 是永远不重复的 "user_id": "423423", //用户ID - "order_id": "434214", //交易所单号 - "trade_id": "123", //委托单ID, 对于一个USER, trade_id 是永远不重复的 + "order_id": "434214", //委托单ID + "trade_id": "123", //成交单ID, 对于一个USER, trade_id 是永远不重复的 "exchange_id": "SHFE", //交易所 "instrument_id": "cu1801", //在交易所中的合约代码 "exchange_trade_id": "434214", //交易所单号 @@ -185,7 +203,7 @@ 交易账户信息同步 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -交易账户信息通过 `rtn_data` 包的 trade 字段进行差分发送, 如下所示: +交易账户信息通过 `rtn_data` 包的 `trade` 字段进行差分发送,如下所示 .. code-block:: javascript @@ -297,15 +315,15 @@ 终端登录鉴权 -------------------------------------------------- -我们使用 aid = "req_login" 的包作为登录请求包. 此包的结构由具体的实现定义. 以 `Open Trade Gateway `_ 项目为例, req_login 包结构如下: +我们使用 `req_login` 指令包作为登录请求包。此包的结构由具体的实现定义,以 `Open Trade Gateway `_ 项目为例, req_login 包结构如下: .. code-block:: javascript { - "aid": "req_login", - "bid": "aaa", - "user_name": "43214", - "password": "abcd123", + "aid": "req_login", //DIFF 协议包类型:登录指令 + "bid": "aaa", //broker_id + "user_name": "43214", //交易账号 + "password": "abcd123", //交易密码 } 登录成功或失败的信息, 通过 `notify` 发送 @@ -316,14 +334,14 @@ 下单 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -终端通过发送 insert_order 包实现下单 +终端通过发送 `insert_order` 包实现下单 .. code-block:: javascript { "aid": "insert_order", //必填, 下单请求 "user_id": "user1", //必填, 需要与登录用户名一致, 或为登录用户的子账户(例如登录用户为user1, 则报单 user_id 应当为 user1 或 user1.some_unit) - "order_id": "SomeStrategy.Instance1.001", //必填, 委托单号, 需确保在一个账号中不重复, 限长512字节 + "order_id": "SomeStrategy.Instance1.001", //必填, 委托单号, 需确保在一个账号中不重复, 限长256字节 "exchange_id": "SHFE", //必填, 下单到哪个交易所 "instrument_id": "cu1803", //必填, 下单合约代码 "direction": "BUY", //必填, 下单买卖方向 @@ -338,7 +356,7 @@ 撤单 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -终端通过发送 cancel_order 包实现撤单 +终端通过发送 `cancel_order` 包实现撤单 .. code-block:: javascript @@ -353,7 +371,7 @@ -------------------------------------------------- 签约银行和转账记录 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -签约银行和转账记录信息由 rtn_data 包中 trade 部分的 banks 和 transfers 发送, 如下所示 +签约银行和转账记录信息由 `rtn_data` 包中 `trade` 部分的 `banks` 和 `transfers` 发送, 如下所示 .. code-block:: javascript @@ -385,7 +403,7 @@ } -请求银期转账 +银期转账指令 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: javascript @@ -405,75 +423,66 @@ 字段常量表 ------------------------------------------------ -order_type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -TRADE 交易指令 -SWAP 互换交易指令 -EXECUTE 期权行权指令 -QUOTE 期权询价指令 -======================== ================================================================================= - -trade_type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -STOPLOSS 止损 -TAKEPROFIT 止盈 -======================== ================================================================================= - -price_type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -ANY 任意价 -LIMIT 限价 -BEST 最优价 -FIVELEVEL 五档价 -======================== ================================================================================= - -volume_condition -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -ANY 任何数量 -MIN 最小数量 -ALL 全部数量 -======================== ================================================================================= - -time_condition -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -IOC 立即完成,否则撤销 -GFS 本节有效 -GFD 当日有效 -GTD 指定日期前有效 -GTC 撤销前有效 -GFA 集合竞价有效 -======================== ================================================================================= - -force_close -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -======================== ================================================================================= -Name Value/Description -======================== ================================================================================= -NOT 非强平 -LACK_DEPOSIT 资金不足 -CLIENT_POSITION_LIMIT 客户超仓 -MEMBER_POSITION_LIMIT 会员超仓 -POSITION_MULTIPLE 持仓非整数倍 -VIOLATION 违规 -OTHER 其他 -PERSONAL_DELIV 自然人临近交割 -HEDGE_POSITION_LIMIT 客户套保超仓 -======================== ================================================================================= +======================== ======================== ================================================================================= +KEY VALUE Description +======================== ======================== ================================================================================= +ORDER_TYPE - TRADE 交易指令 + + - SWAP 互换交易指令 + + - EXECUTE 期权行权指令 + + - QUOTE 期权询价指令 +------------------------ ------------------------ --------------------------------------------------------------------------------- +TRADE_TYPE - STOPLOSS 止损 + + - TAKEPROFIT 止盈 +------------------------ ------------------------ --------------------------------------------------------------------------------- + +PRICE_TYPE - ANY 任意价 + + - LIMIT 限价 + + - BEST 最优价 + + - FIVELEVEL 五档价 +------------------------ ------------------------ --------------------------------------------------------------------------------- +VOLUME_CONDITION - ANY 任何数量 + + - MIN 最小数量 + + - ALL 全部数量 +------------------------ ------------------------ --------------------------------------------------------------------------------- +TIME_CONDITION - IOC 立即完成,否则撤销 + + - GFS 本节有效 + + - GFD 当日有效 + + - GTD 指定日期前有效 + + - GTC 撤销前有效 + + - GFA 集合竞价有效 +------------------------ ------------------------ --------------------------------------------------------------------------------- +FORCE_CLOSE - NOT 非强平 + + - LACK_DEPOSIT 资金不足 + + - CLIENT_POSITION_LIMIT 客户超仓 + + - MEMBER_POSITION_LIMIT 会员超仓 + + - POSITION_MULTIPLE 持仓非整数倍 + + - VIOLATION 违规 + + - OTHER 其他 + + - PERSONAL_DELIV 自然人临近交割 + + - HEDGE_POSITION_LIMIT 客户套保超仓 +======================== ======================== ================================================================================= 协议实现 diff --git a/general.rst b/general.rst index e8c0b42..d0ff4a2 100644 --- a/general.rst +++ b/general.rst @@ -1,115 +1,147 @@ Differential Information Flow for Finance (DIFF) 协议 ======================================================================= -目前市面上的接口通常以事件回调的方式进行信息交互,导致业务层对当前状况的全景缺乏了解,不便于编写复杂业务逻辑。而 DIFF 协议将异步的事件回调转为同步的数据访问,使得业务层能简单同步的访问业务数据,简化了编码复杂度。 +在任一时刻,一个用户的交易账户可以包含以下业务信息 -DIFF 协议分为两部分: **数据访问** 和 **数据传输** +* ``1~N 个资金账户(ACCOUNT)`` +* ``0~N 个持仓记录(POSITION)`` +* ``0~N 个委托单(ORDER)`` +* ``0~N 个成交单(TRADES)`` +* ``0~N 个行情订阅(QUOTES)`` +* ``其他账户状态信息`` -数据访问 ------------------------------------------------------- -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": "SHFE.cu1609,...." # 行情订阅的合约列表 - "quotes":{ # 所有订阅的实时行情 - "SHFE.cu1612": { - "instrument_id": "SHFE.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": "-", # 结算价 + "trade": { + "022000": { + "user_id": "022000", //登录用户名 + "accounts": { ... }, //资金账户 + "positions": { ... }, //持仓 + "orders": { ... }, //委托 + "trades": { ... }, //成交 + "banks": { ... }, //签约银行 + "transfers": { ... } //银期流水 }, - ... + "quotes": { ... }, //订阅行情信息 } } -对应的客户端也维护了一个该截面的镜像,因此业务层可以简单同步的访问到全部业务数据。 -* 业务截面的内容由各业务模块定义,例如 ref:`quote` ref:`trade` ref:`mdhis` -* 除非由业务模块的文档另行说明,否则业务截面中的数据应是自恰的。例如:任何时刻的业务截面都应包含 balance, static_balance 和 float_profit 字段,并且满足 balance = static_balance + float_profit * 业务截面可能会包含协议中未写出的额外字段,使用方应忽略这些字段的信息,因此在扩展或新增业务模块时可以保持向后兼容性 * 部分字段可能会有多种数据类型,例如上述例子中的收盘和结算价在收盘前是字符串,在收盘后会更新为对应的数值 -数据传输 +DIFF 协议约定了业务信息截面的 **数据格式**、 **数据传输** 和 **数据同步** 方式。 + +数据格式 ------------------------------------------------------ -DIFF 协议使用 json 编码通过 websocket 传输,因此可以使用 ssl 实现传输层安全加密,permessage-deflate 实现数据压缩。协议通讯为全双工模式,任何一方都可以随时向对方发送数据包,也应随时准备接收对方发来的数据包,发出的包之间无需等待对方回应。每个数据包中均有一个 aid 字段,此字段值即为数据包类型。 +DIFF 协议要求采用 Json 格式进行数据交换。数据包按照功能划分三种类型: **业务信息截面更新**、 **业务信息截面更新请求** 和 **交易包** ,由数据包中 ``aid`` 字段进行区分。 -数据包类型 +业务信息截面更新 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DIFF 协议要求服务端维护每个账户的业务信息截面,同时将业务信息的变化以 JSON Merge Patch 的格式推送给客户端,下面是业务信息截面更新包样例。 + +:: -* 业务信息截面更新: - DIFF 协议要求服务端将业务信息的变化以 JSON Merge Patch (https://tools.ietf.org/html/rfc7386) 的格式推送给客户端,例如: - - :: - + { + "aid": "rtn_data", # 业务信息截面更新 + "data": [ # 数据更新数组 { - "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 数据包以获得业务信息截面更新 - - :: - + "balance": 10237421.1, # 账户资金 + }, + { + "float_profit": 283114.780999997, # 浮动盈亏 + }, { - "aid": "peek_message" + "quotes":{ + "SHFE.cu1612": { + "datetime": "2016-12-30 14:31:02.000000", + "last_price": 36605.0, # 最新价 + "volume": 25431, # 成交量 + "pre_close": 36170.0, # 昨收 + } + } } - - - 服务端在收到 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 没有该合约则说明该合约不存在 + ] + } + +- 以上数据包中的 ``"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`` 数据包以获得业务信息截面更新。 +一个简单的客户端实现可以在连接成功后及每收到一个 rtn_data 数据包后发送一个 peek_message 数据包,这样当客户端带宽不足时会自动降低业务信息截面的更新频率以适应低带宽 + +:: + + { + "aid": "peek_message" + } + +指令包 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +当数据包中的 ``aid`` 字段不是 ``rtn_data`` 或 ``peek_message`` 时,则表示该包为一个指令包,具体指令由各业务模块定义,例如 subscribe_quote 表示订阅行情,insert_order 表示下单。 + +由于客户端和服务端存在网络通讯延迟,客户端的指令需要过一段时间才会影响到截面中的业务数据,为了使客户端能分辨出服务端是否处理了该指令,通常服务端会将 +客户端的请求以某种方式体现在截面中(具体方式由各业务模块定义)。例如 subscribe_quote 订阅行情时服务端会将业务截面中的 ins_list 字段更新为客户端订阅的合约列表, +这样当客户端检查业务截面时如果 ins_list 包含了客户端订阅的某个合约,但是 quotes 没有该合约则说明该合约不存在。 + +数据传输 +------------------------------------------------------ +DIFF 协议要求数据传输采用 websocket 协议,该协议的标准见`The WebSocket Protocol `_ 。 +websocket 协议通讯为全双工模式,任何一方都可以随时向对方发送数据包,也应随时准备接收对方发来的数据包,发出的包之间无需等待对方回应。 + +数据加密与压缩 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DIFF 协议数据传输过程中,数据的加密与压缩参考 websocket 实现,**推荐** ssl 实现传输层安全加密,`permessage-deflate `_ 实现数据压缩。 + +交互流程 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +目前市面上的接口通常以事件回调的方式进行信息交互,导致业务层对当前状况的全景缺乏了解,不便于编写复杂业务逻辑。而 DIFF 协议将异步的事件回调转为同步的数据访问,使得业务层能简单同步的访问业务数据,简化了编码复杂度。 +下图给出了 DIFF 协议下数据交互流程的样例。 + +:: + + client server + + + + | 1.insert_order | + +-------------------------------->+ + | | + | 2.peek_message | + +-------------------------------->+ + | | + | 3.rtn_data | + +<--------------------------------+ + | | + | 4.peek_message | + +-------------------------------->+ + | | + | 5.peek_message | + +-------------------------------->+ + | | + | 6.rtn_data | + +<--------------------------------+ + | | + + + + Diff Transmission Example + + +1. 客户端发起交易指令 +2. 客户端发起 peek_message 数据包 ,请求服务端推送账户最新业务截面 +3. 确定业务截面更新后,服务端将业务截面变化推送给客户端 + +其中,业务信息截面更新 ``rtn_data`` 的推送依赖于 业务信息截面更新请求包 peek_message。当服务端收到 peek_message 包之后,会检查截面是否发生 +变化,如果有则将更新推送给客户端,否则应等到有更新时再回应客户端;对于截面发生变化之前,客户端多次发起的 peek_message 请求包, 服务端会当作为一次请求处理, +如上图中, 对于客户端 4 和 5 peek_message包,服务端仅做一次推送。 + +数据同步 +------------------------------------------------------ +DIFF 协议要求客户端也维护了一个业务信息截面,该截面来源于服务端下发的 JSON Merge Patch 格式数据。客户端同步业务信息截面遵循 `JSON Merge Patch 标准 `_ \ No newline at end of file diff --git a/index.rst b/index.rst index f8156e0..74d7ed8 100644 --- a/index.rst +++ b/index.rst @@ -1,7 +1,7 @@ Differential Information Flow for Finance ===================================================================== -DIFF (Differential Information Flow for Finance) 是一个基于websocket和json的应用层协议,主要用于在金融IT系统的各组件间提供数据交互。该协议的主要特性包括: +DIFF (Differential Information Flow for Finance) 是一个基于 websocket 和 json 的应用层协议,主要用于在金融IT系统的各组件间提供数据交互。该协议的主要特性包括: * 使用统一易扩展的结构, 易于编程实现,`典型的客户端代码 `_ 仅需数十行即可处理行情/交易/历史数据等全部业务需求 * 对于各平台和编程语言均有良好支持