# 底層協議介紹
moomoo API 是 moomoo 為主流的程式語言(Python、Java、C#、C++、JavaScript)封裝的 API SDK,以方便您調用,降低策略開發難度。
這部分主要介紹策略腳本與 OpenD 服務之間通訊的底層協議,適用於非上述 5 種程式語言用戶,自行對接實現底層裸協議。
提示
- 如果您使用的程式語言在上述的 5 種主流程式語言之內,可以直接跳過這部分內容。
# 協議請求流程
- 建立連接
- 初始化連接
- 請求數據或接收推送數據
- 定時發送 KeepAlive 保持連接

# 協議設計
協議數據包括協議頭以及協議體,協議頭固定欄位,協議體根據具體協議決定。
# 協議頭
struct APIProtoHeader
{
u8_t szHeaderFlag[2];
u32_t nProtoID;
u8_t nProtoFmtType;
u8_t nProtoVer;
u32_t nSerialNo;
u32_t nBodyLen;
u8_t arrBodySHA1[20];
u8_t arrReserved[8];
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
| 欄位 | 説明 |
|---|---|
| szHeaderFlag | 包頭起始標誌,固定為“FT” |
| nProtoID | 協議 ID |
| nProtoFmtType | 協議格式類型,0 為 Protobuf 格式,1 為 Json 格式 |
| nProtoVer | 協議版本,用於迭代兼容,目前填 0 |
| nSerialNo | 包序列號,用於對應請求包和回傳封包 / 回傳資料,要求遞增 |
| nBodyLen | 包體長度 |
| arrBodySHA1 | 包體原始數據(解密後)的 SHA1 雜湊值 (Hash) |
| arrReserved | 保留 8 位元組 (Byte)擴展 |
提示
- u8_t 表示 8 位無符號整數,u32_t 表示 32 位無符號整數
- OpenD 內部處理使用 Protobuf,因此協議格式建議使用 Protobuf,減少 Json 轉換開銷
- nProtoFmtType 欄位指定了包體的數據類型,回傳封包 / 回傳資料會回對應類型的數據;推送協議數據類型由 OpenD 配置檔案指定
- arrBodySHA1 用於驗證請求數據在網絡傳輸前後的一致性,必須正確填入
- 協議頭的二進制流使用的是小端位元組 (Byte)序,即一般不需要使用 ntohl 等相關函數轉換數據
# 協議體
# Protobuf 協議請求包體結構
message C2S
{
required int64 req = 1;
}
message Request
{
required C2S c2s = 1;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Protobuf 協議回應包體結構
message S2C
{
required int64 data = 1;
}
message Response
{
required int32 retType = 1 [default = -400]; //RetType,返回結果
optional string retMsg = 2;
optional int32 errCode = 3;
optional S2C s2c = 4;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
| 欄位 | 説明 |
|---|---|
| c2s | 請求參數結構 |
| req | 請求參數,實際根據協議定義 |
| retType | 請求結果 |
| retMsg | 若請求失敗,説明失敗原因 |
| errCode | 若請求失敗對應錯誤碼 |
| s2c | 回應數據結構,部分協議不返回數據則無該欄位 |
| data | 回應數據,實際根據協議定義 |
提示
- 包體格式類型請求包由協議頭 nProtoFmtType 指定,OpenD 主動推送格式在 InitConnect 設置。
- 原始協議檔案格式是以 Protobuf 格式定義,若需要 json 格式傳輸,建議使用 protobuf3 的介面 / API直接轉換成 json。
- 枚舉值欄位定義使用有符號整形,註解指明對應枚舉,枚舉一般定義於 Common.proto,Qot_Common.proto,Trd_Common.proto 檔案中。
- 協議中價格、百分比等數據用浮點類型來傳輸,直接使用會有精度問題,需要根據精度(如協議中未指明,預設小數點後三位)做四捨五入之後再使用。
# 心跳保活
syntax = "proto2";
package KeepAlive;
option java_package = "com.moomoo.openapi.pb";
option go_package = "github.com/moomooopen/mmapi4go/pb/keepalive";
import "Common.proto";
message C2S
{
required int64 time = 1; //客户端發包時的格林威治時間戳,單位秒
}
message S2C
{
required int64 time = 1; //伺服器回傳封包 / 回傳資料時的格林威治時間戳,單位秒
}
message Request
{
required C2S c2s = 1;
}
message Response
{
required int32 retType = 1 [default = -400]; //RetType,返回結果
optional string retMsg = 2;
optional int32 errCode = 3;
optional S2C s2c = 4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
介紹
心跳保活
協議 ID
1004
使用
根據初始化連結返回的心跳保活間隔時間,向 OpenD 發送保活協議
# 加密通訊流程
- 若 OpenD 配置了加密,InitConnect 初始化連接協議必須使用 RSA 公鑰加密,後續其他協議使用 InitConnect 返回的隨機密鑰進行 AES 加密通訊。
- OpenD 的加密流程借鑑了 SSL 協議,但考慮到一般是本地部署服務和應用,簡化了相關流程,OpenD 與接入 Client 共用了同一個 RSA 私鑰檔案,請妥善保存和分發私鑰檔案。
- 可到這個 網址 在線生成隨機 RSA 密鑰對,密鑰格式必須為 PCKS#1,密鑰長度 512,1024 都可以,不要設置密碼,將生成的私鑰複製保存到檔案中,然後將私鑰檔案路徑配置到 OpenD 配置 約定的 rsa_private_key 配置項中。
- 建議有實盤交易的用戶配置加密,避免賬户和交易資訊泄露。

# RSA 加解密
- OpenD 配置 約定 rsa_private_key 為私鑰檔案路徑
- OpenD 與接入客户端共用相同的私鑰檔案
- RSA 加解密僅用於 InitConnect 請求,用於安全獲取其它請求協議的對稱加密 Key
- OpenD 的 RSA 密鑰為 1024 位,填充方式 PKCS1,公鑰加密,私鑰解密,公鑰可通過私鑰生成
- Python API 參考實現:RsaCrypt 類的 encrypt / decrypt 介面 / API
# 發送數據加密
- RSA 加密規則:若密鑰位數是 key_size,單次加密串的最大長度為 (key_size)/8 - 11,目前位數 1024,一次加密長度可定為 100。
- 將明文數據分成一個或數個最長 100 位元組 (Byte)的小段進行加密,拼接分段加密數據即為最終的 Body 加密數據。
# 接收數據解密
- RSA 解密同樣遵循分段規則,對於 1024 位密鑰,每小段待解密數據長度為 128 位元組 (Byte)。
- 將密文數據分成一個或數個 128 位元組 (Byte)長的小段進行解密,拼接分段解密數據即為最終的 Body 解密數據。
# AES 加解密
- 加密 key 由 InitConnect 協議返回
- 預設使用的是 AES 的 ecb 加密模式。
- Python API 參考實現: ConnMng 類的 encrypt_conn_data / decrypt_conn_data 介面 / API
# 發送數據加密
- AES 加密要求源數據長度必須是 16 的整數倍,故需補‘0’對齊後再加密,記錄 mod_len 為源數據長度與 16 取模值。
- 因加密前有可能對源數據作修改,故需在加密後的數據尾再增加一個 16 位元組 (Byte)的填充數據塊,其最後一個位元組 (Byte)賦值 mod_len,其餘位元組 (Byte)賦值‘0’,將加密數據和額外的填充數據塊拼接作為最終要發送協議的 body 數據。
# 接收數據解密
- 協議 body 數據,先將最後一個位元組 (Byte)取出,記為 mod_len,然後將 body 截掉尾部 16 位元組 (Byte)填充數據塊後再解密(與加密填充額外數據塊邏輯對應)。
- mod_len 為 0 時,上述解密後的數據即為協議返回的 body 數據,否則需截掉尾部(16 - mod_len)長度的用於填充對齊的數據。


