跳转至

gRPC

:33333 提供一个通用 Request(proto_id, body) RPC + 一个流式 SubscribePush

服务定义

crates/futu-grpc/proto/futu.proto

service FutuOpenD {
  rpc Request(FutuRequest) returns (FutuResponse);
  rpc SubscribePush(SubscribePushRequest) returns (stream PushEvent);
}

message FutuRequest {
  uint32 proto_id = 1;
  bytes  body     = 2;   // protobuf 编码
}

message FutuResponse {
  int32  ret_type = 1;
  string ret_msg  = 2;
  uint32 proto_id = 3;
  bytes  body     = 4;
}

调用示例

# 不鉴权
grpcurl -plaintext -d '{"proto_id":1002}' \
  localhost:33333 futu.service.FutuOpenD/Request

# 鉴权
grpcurl -plaintext \
  -H 'authorization: Bearer fc_xxxx...' \
  -d '{"proto_id":1002}' \
  localhost:33333 futu.service.FutuOpenD/Request
import grpc
from futu_pb2 import FutuRequest
from futu_pb2_grpc import FutuOpenDStub

creds = grpc.metadata_call_credentials(
    lambda ctx, cb: cb((("authorization", "Bearer fc_xxxx..."),), None)
)
channel = grpc.secure_channel("localhost:33333", grpc.ssl_channel_credentials())
stub = FutuOpenDStub(channel)

# 打包 proto_id=3004 (QOT_GET_BASIC_QOT) body
body = build_get_basic_qot_request(code="00700")  # protobuf encode
resp = stub.Request(FutuRequest(proto_id=3004, body=body))
conn, _ := grpc.Dial("localhost:33333", grpc.WithInsecure())
client := futu.NewFutuOpenDClient(conn)

md := metadata.Pairs("authorization", "Bearer fc_xxxx...")
ctx := metadata.NewOutgoingContext(context.Background(), md)

resp, _ := client.Request(ctx, &futu.FutuRequest{
    ProtoId: 1002,
})

proto_id → scope 映射

范围 所需 scope
1xxx 系统(InitConnect / KeepAlive / ...)
3xxx 行情 qot:read
2005 UnlockTrade trade:real
2202 / 2205 / 2237 下单 / 改单 / 确认 trade:real
2001 / 2008 / 2101 / 2102 / ... 账户只读 acc:read
其他 fail-closed 拒(catch-all trade:real)

限额

  • auth 层:trade:real 请求过通用 rate + hours 闸门
  • handler 层(v1.2+):2202 PlaceOrder / 2205 ModifyOrder 解码 body 后做 market / symbol / value / side / daily 细粒度检查

详见 鉴权与限额

流式推送

grpcurl -plaintext \
  -H 'authorization: Bearer fc_xxxx...' \
  -d '{}' \
  localhost:33333 futu.service.FutuOpenD/SubscribePush

每条 PushEventevent_type (quote / notify / trade) 和 body (protobuf)。

v1.1+:服务端按 client key 的 scope 过滤推送 —— qot:read-only 的 key 不会收到 trade 类事件(交易回报)。过滤掉的事件计 futu_ws_filtered_pushes_total counter。

错误码映射

tonic Status 含义
Unauthenticated Bearer 缺失 / 无效 / 过期
PermissionDenied scope 不够
ResourceExhausted 限额触发(对应 REST 的 429)
InvalidArgument proto_id 不对 / body decode 失败
Unavailable 网关 disconnect