Ruby对接富途牛牛证券自定义TCP协议、Websocket协议的思路

富途牛牛是唯数不多的互联网券商,有详细的API文档(同样有开放API的雪盈、老虎)但技术上都没有富途那么专业和强大。

最近刚好要写一个拉取富途行情数据的接口,用来做小工具—价格向右回撤提醒工具,一旦价格符合设定的点立即电话通知我出货。

这些年对过的API接口也不少,一开始我以为能很快用Ruby对接好,但实际上我错了,整整花费了大半个月。借此我也更深入的了解http与tcp之间的关系。自定义的tcp协议要如何对接等。

其实市场上是有其他开源的php/go/java对接好的源代码,但我不信,我就是想要用Ruby来对接…..

文档地址:https://openapi.futunn.com/futu-api-doc/

文档使用了自定义tcp头 + google Protobuf 协议介绍

这就导致了只有http api对接经验的我,一下子要对接tcp协议一下懵逼了,也没有人能请教,只有自己死磕。

这里讲一讲自己的理解:

http是应用层协议基于tcp传输层,所以tcp更底层一点,tcp有协议头和协议体。其中协议体可以用富途提供的Google protobuf来解析出具体数据,这里重点是协议头,在ruby中要如何解析,解析出来之后,每次通信就能算得我们想要的数据了

这里直接上Ruby代码解析协议头的代码:

  1. 首先安装2个相关的gem:

    1
    2
    gem 'google-protobuf'
    gem 'bindata'
  2. 解析和序列Tcp头部信息相关的代码:

    这里按websocket协议写的协议头信息,可能写官方API文档不太一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    请求Request的相关代码
    class ReqHead < BinData::Record
    endian :big
    string :sign, read_length: 8
    uint32 :cmd
    uint64 :section
    end
    响应Response的相关代码
    class ResHead < BinData::Record
    endian :big
    string :sign, read_length: 8
    uint32 :cmd
    uint64 :section
    uint32 :error
    string :errmsg, read_length: 20
    end

    每次请求,Request的头部信息这样序列:

    1
    2
    3
    4
    5
    6
    def header body_binary, id
    res = Futu::ReqHead.new sign: "ft-v1.0\x00",
    cmd: id,
    section: serial_id
    res.to_binary_s
    end

    每次接收到请求时这样解析:

    1
    2
    3
    def parse_header binary
    Futu::ResHead.read binary
    end
解决完协议头部信息后,再来解决协议体的解析

协议体是从44位开始的,所以也挺简单,假设我们接收到的response赋值给raw_data,那么解析方法如下:

1
TrdGetAccList::Response.decode raw_data.byteslice(44..-1)

具体这个TrdGetAccList::Response文件要怎么生成和引入,请参考:https://developers.google.com/protocol-buffers/docs/reference/ruby-generated

说说Websocket连接时和普通tcp连接的区别

  1. 不再需要间隔一段时间发送keep alive保活
  2. 协议头有所变化,见我上方代码中的协议头