gRPC¶
:33333 exposes a generic Request(proto_id, body) RPC plus a streaming
SubscribePush.
Service definition¶
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-encoded
}
message FutuResponse {
int32 ret_type = 1;
string ret_msg = 2;
uint32 proto_id = 3;
bytes body = 4;
}
Call examples¶
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)
# encode 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))
proto_id → scope mapping¶
| Range | Required scope |
|---|---|
1xxx system (InitConnect / KeepAlive / ...) |
none |
3xxx quote |
qot:read |
2005 UnlockTrade |
trade:real |
2202 / 2205 / 2237 place / modify / confirm order |
trade:real |
2001 / 2008 / 2101 / 2102 / ... account read |
acc:read |
| Others | fail-closed reject (catch-all trade:real) |
Limits¶
- Auth layer:
trade:realrequests pass through the generic rate + hours gate - Handler layer (v1.2+):
2202 PlaceOrder/2205 ModifyOrderdecode the body and run fine-grained market / symbol / value / side / daily checks
See Auth & limits for details.
Streaming pushes¶
grpcurl -plaintext \
-H 'authorization: Bearer fc_xxxx...' \
-d '{}' \
localhost:33333 futu.service.FutuOpenD/SubscribePush
Each PushEvent has an event_type (quote / notify / trade) and a
body (protobuf).
v1.1+: the server filters pushes by the client key's scope — a
qot:read-only key does not receive trade events (order/fill callbacks).
Filtered events increment the futu_ws_filtered_pushes_total counter.
Error-code mapping¶
| tonic Status | Meaning |
|---|---|
Unauthenticated |
Bearer missing / invalid / expired |
PermissionDenied |
Scope insufficient |
ResourceExhausted |
Limit tripped (REST equivalent: 429) |
InvalidArgument |
Wrong proto_id or body decode failed |
Unavailable |
Gateway disconnected |