# ネイティブプロトコル概要
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 ハッシュ値 |
| arrReserved | 8バイト拡張予約 |
ご注意
- u8_t は8ビット符号なし整数、u32_t は32ビット符号なし整数を表します
- OpenD の内部処理は Protobuf を使用するため、Json 変換のオーバーヘッドを減らすために Protobuf 形式の使用を推奨します
- nProtoFmtType フィールドでボディのデータ型を指定すると、レスポンスは対応する型で返されます。プッシュプロトコルのデータ型は OpenD の設定ファイルで指定します
- arrBodySHA1 はリクエストデータのネットワーク転送前後の整合性検証に使用されます。正しく入力する必要があります
- プロトコルヘッダーのバイナリストリームはリトルエンディアンバイトオーダーを使用します。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 のインターフェースで直接 JSON に変換することを推奨します。
- 列挙値フィールドは符号付き整数で定義され、コメントで対応する列挙を示します。列挙は通常 Common.proto、Qot_Common.proto、Trd_Common.proto ファイルで定義されています。
- プロトコル内の価格・パーセンテージ等のデータは浮動小数点型で転送されるため、直接使用すると精度の問題が発生します。精度(プロトコルで未指定の場合はデフォルト小数点以下3桁)に基づいて四捨五入してから使用してください。
# ハートビート保持
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; //クライアントがパケット送信時のUTCタイムスタンプ(秒)
}
message S2C
{
required int64 time = 1; //サーバーがレスポンス送信時のUTCタイムスタンプ(秒)
}
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 と接続クライアントは同一の 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 インターフェース
# 送信データの暗号化
- RSA 暗号化ルール:鍵ビット数が key_size の場合、1回の暗号化文字列の最大長は (key_size)/8 - 11 です。現在 1024 ビットのため、1回の暗号化長は 100 に設定できます。
- 平文データを最大100バイトの小セグメントに分割して暗号化し、各セグメントの暗号化データを連結したものが最終的な Body 暗号化データになります。
# 受信データの復号
- RSA 復号も同様にセグメント分割ルールに従います。1024 ビット鍵の場合、各セグメントの復号データ長は 128 バイトです。
- 暗号文データを128バイトの小セグメントに分割して復号し、各セグメントの復号データを連結したものが最終的な Body 復号データになります。
# AES 暗号化・復号
- 暗号化 Key は InitConnect プロトコルから返される
- デフォルトでは AES の ECB 暗号化モードを使用
- Python API 参考実装: ConnMng クラスの encrypt_conn_data / decrypt_conn_data インターフェース
# 送信データの暗号化
- AES 暗号化はソースデータ長が16の倍数である必要があるため、'0'でパディングしてから暗号化し、mod_len をソースデータ長と16の剰余として記録します。
- 暗号化前にソースデータを変更する可能性があるため、暗号化データの末尾に16バイトのパディングブロックを追加します。最後の1バイトに mod_len を、残りのバイトに'0'を設定し、暗号化データとパディングブロックを連結して最終的な送信プロトコルの body データとします。
# 受信データの復号
- プロトコル body データの最後の1バイトを取り出して mod_len とし、末尾16バイトのパディングブロックを切り落としてから復号します(暗号化時のパディングロジックに対応)。
- mod_len が 0 の場合、復号後のデータがそのままプロトコルの body データです。0 以外の場合は末尾の (16 - mod_len) バイトのパディングデータを切り落とす必要があります。


