訂閱交易事件
接口說明
交易事件檢閱是基於grpc實現的Server Streaming長連接,適用於通過OpenApi開發平台對接Webull客戶。交易事件檢閱完全遵循grpc開源協議,您在使用時可以參考Python grpc開源庫。
目前接口支持訂單狀態變化消息推送,支持的場景類型如下:
| scene_type | Description |
|---|---|
| FILLED | 部分成交 |
| FINAL_FILLED | 全部成交 |
| PLACE_FAILED | 下單失敗 |
| MODIFY_SUCCESS | 改單成功 |
| MODIFY_FAILED | 改單失敗 |
| CANCEL_SUCCESS | 撤單成功 |
| CANCEL_FAILED | 撤單失敗 |
交易事件檢閱Proto協議定義
請求Proto
message SubscribeRequest {
uint32 subscribeType = 1; // Subscription type
int64 timestamp = 2; // Timestamp
string contentType = 3; // Content type
string payload = 4; // Content
repeated string accounts = 5; // Account ID
}
響應Proto
message SubscribeResponse {
EventType eventType = 1; // Event type
uint32 subscribeType = 2; // Subscription type
string contentType = 3; // Subscription type
string payload = 4; // Content
string requestId = 5; // Request id
int64 timestamp = 6; // Timestamp
}
EventType 枚舉
enum EventType {
SubscribeSuccess = 0; // Subscription succeeded
Ping = 1; // Heartbeat information
AuthError = 2; // Authentication error
NumOfConnExceed = 3; // Connection limit exceeded
SubscribeExpired = 4; // Subscription expired
}
Request Example
- Python
- Java
使用sdk請求時,可以忽略subscribeType、timestamp、contentType和payload。 只需提交帳目即可。 subscribeType現時僅支持=1。 在以下情况下,_on_log方法用於輸出日誌。 my_on_events_message方法用於接收訂單狀態更改消息。
import logging
from webull.trade.events.types import ORDER_STATUS_CHANGED, EVENT_TYPE_ORDER
from webull.trade.trade_events_client import TradeEventsClient
your_app_key = "<your_app_key>"
your_app_secret = "<your_app_secret>"
account_id = "<your_account_id>"
region_id = "<region_id>"
optional_api_endpoint = "<event_api_endpoint>"
def _on_log(level, log_content):
print(logging.getLevelName(level), log_content)
def my_on_events_message(event_type, subscribe_type, payload, raw_message):
if EVENT_TYPE_ORDER == event_type and ORDER_STATUS_CHANGED == subscribe_type:
print('%s' % payload)
if __name__ == '__main__':
# Create EventsClient instance
trade_events_client = TradeEventsClient(your_app_key, your_app_secret, region_id)
# For non production environment, you need to set the domain name of the subscription service through eventsclient. For example, the domain name of the UAT environment is set here
# trade_events_client = TradeEventsClient(your_app_key, your_app_secret, region_id, host=optional_api_endpoint)
trade_events_client.on_log = _on_log
# Set the callback function when the event data is received.
# The data of order status change is printed here
trade_events_client.on_events_message = my_on_events_message
# Set the account ID to be subscribed and initiate the subscription. This method is synchronous
trade_events_client.do_subscribe([account_id])
handleEventMessage方法用於接收訂單狀態更改消息。
import com.google.gson.reflect.TypeToken;
import com.webull.openapi.core.execption.ClientException;
import com.webull.openapi.core.execption.ServerException;
import com.webull.openapi.core.logger.Logger;
import com.webull.openapi.core.logger.LoggerFactory;
import com.webull.openapi.core.serialize.JsonSerializer;
import com.webull.openapi.samples.config.Env;
import com.webull.openapi.trade.events.subscribe.ISubscription;
import com.webull.openapi.trade.events.subscribe.ITradeEventClient;
import com.webull.openapi.trade.events.subscribe.message.EventType;
import com.webull.openapi.trade.events.subscribe.message.SubscribeRequest;
import com.webull.openapi.trade.events.subscribe.message.SubscribeResponse;
import java.util.Map;
public class TradeEventsClient {
private static final Logger logger = LoggerFactory.getLogger(TradeEventsClient.class);
public static void main(String[] args) {
try (ITradeEventClient client = ITradeEventClient.builder()
.appKey(Env.APP_KEY)
.appSecret(Env.APP_SECRET)
.regionId(Env.REGION_ID)
// .host("<event_api_endpoint>")
.onMessage(TradeEventsClient::handleEventMessage)
.build()) {
SubscribeRequest request = new SubscribeRequest("<your_account_id>");
ISubscription subscription = client.subscribe(request);
subscription.blockingAwait();
} catch (ClientException ex) {
logger.error("Client error", ex);
} catch (ServerException ex) {
logger.error("Sever error", ex);
} catch (Exception ex) {
logger.error("Unknown error", ex);
}
}
private static void handleEventMessage(SubscribeResponse response) {
if (SubscribeResponse.CONTENT_TYPE_JSON.equals(response.getContentType())) {
Map<String, String> payload = JsonSerializer.fromJson(response.getPayload(),
new TypeToken<Map<String, String>>(){}.getType());
if (EventType.Order.getCode() == response.getEventType() || EventType.Position.getCode() == response.getEventType()) {
logger.info("{}", payload);
}
}
}
}
響應示例
交易事件場景類型
- FILLED
- FINAL_FILLED
- PLACE_FAILED
- MODIFY_SUCCESS
- CANCEL_SUCCESS
{
"account_id": "PHIUK08VAKH7EOVG85ULCAG3JB",
"request_id": "036LVUOVRA8BV0KHKN60000000",
"order_id": "036LVUOVRA8BV0KHKN60000000",
"client_order_id": "6adeba36fc174acd92538e990c06274e",
"instrument_id": "913256135",
"order_status": "PARTIAL_FILLED",
"symbol": "AAPL",
"short_name": "Apple Inc",
"qty": "3.0000000000",
"filled_price": "10.0",
"filled_qty": "1.0",
"filled_time": "2025-11-26T11:40:35.524+0000",
"side": "BUY",
"scene_type": "FILLED",
"category": "US_STOCK",
"order_type": "LIMIT"
}
DEBUG response:eventType: Ping
subscribeType: 1
contentType: "text/plain"
requestId: "5ca8b9d7-6f57-4218-9fb3-d3951c2fa399"
timestamp: 1764157239726
{
"account_id": "PHIUK08VAKH7EOVG85ULCAG3JB",
"request_id": "036LVUAB7C8BV0KHKN60000000",
"order_id": "036LVUAB7C8BV0KHKN60000000",
"client_order_id": "9b0440781feb4c0cb8523224ce0926ac",
"instrument_id": "913256135",
"order_status": "FILLED",
"symbol": "AAPL",
"short_name": "Apple Inc",
"qty": "2.0000000000",
"filled_price": "277.98",
"filled_qty": "2.0",
"filled_time": "2025-11-26T11:35:38.513+0000",
"side": "BUY",
"scene_type": "FINAL_FILLED",
"category": "US_STOCK",
"order_type": "MARKET"
}
DEBUG response:eventType: Ping
subscribeType: 1
contentType: "text/plain"
requestId: "5ca8b9d7-6f57-4218-9fb3-d3951c2fa399"
timestamp: 1764156939725
{
"account_id": "PHIUK08VAKH7EOVG85ULCAG3JB",
"request_id": "036LVV3TME8BV0KHKN60000000",
"order_id": "036LVV3TME8BV0KHKN60000000",
"client_order_id": "794cabc6783640d7869a37f966ae4819",
"instrument_id": "913256135",
"order_status": "FAILED",
"symbol": "AAPL",
"short_name": "Apple Inc",
"qty": "92.0000000000",
"filled_price": "0E-10",
"filled_qty": "0E-10",
"side": "BUY",
"scene_type": "PLACE_FAILED",
"category": "US_STOCK",
"order_type": "LIMIT"
}
DEBUG response:eventType: Ping
subscribeType: 1
contentType: "text/plain"
requestId: "5ca8b9d7-6f57-4218-9fb3-d3951c2fa399"
timestamp: 1764157359725
{
"account_id": "PHIUK08VAKH7EOVG85ULCAG3JB",
"request_id": "036LVV5P4I8BV0KHKN60000000",
"order_id": "036LVV5P4I8BV0KHKN60000000",
"client_order_id": "04cda8db7ed940f6afeb26be6201ee53",
"instrument_id": "913256135",
"order_status": "WORKING",
"symbol": "AAPL",
"short_name": "Apple Inc",
"qty": "4.0000000000",
"filled_price": "0E-10",
"filled_qty": "0E-10",
"side": "BUY",
"scene_type": "MODIFY_SUCCESS",
"category": "US_STOCK",
"order_type": "LIMIT"
}
DEBUG response:eventType: Ping
subscribeType: 1
contentType: "text/plain"
requestId: "5ca8b9d7-6f57-4218-9fb3-d3951c2fa399"
timestamp: 1764157419725
{
"account_id": "PHIUK08VAKH7EOVG85ULCAG3JB",
"request_id": "036LVV5P4I8BV0KHKN60000000",
"order_id": "036LVV5P4I8BV0KHKN60000000",
"client_order_id": "04cda8db7ed940f6afeb26be6201ee53",
"instrument_id": "913256135",
"order_status": "CANCELLED",
"symbol": "AAPL",
"short_name": "Apple Inc",
"qty": "4.0000000000",
"filled_price": "0E-10",
"filled_qty": "0E-10",
"side": "BUY",
"scene_type": "CANCEL_SUCCESS",
"category": "US_STOCK",
"order_type": "LIMIT"
}
DEBUG response:eventType: Ping
subscribeType: 1
contentType: "text/plain"
requestId: "5ca8b9d7-6f57-4218-9fb3-d3951c2fa399"
timestamp: 1764157479725