訂單
Webull 提供訂單 API,讓開發者能透過 HTTP 協議執行訂單與查詢。更多細節請參考 訂單介面。
在調用訂單 API 前,您需要擁有 App Key 與 App Secret。
注意
根據香港的安全與合規要求,除了需透過 App Key 及 App Secret 進行身份驗證外,OpenAPI 還要求使用 Token 進行身份驗證。 有關 Token 的產生與驗證,請參考:Token 產生與驗證
1. 支援市場
交易 API 支援下列市場:
| 市場 | 產品類型 |
|---|---|
| 美國 | 美國股票產品(股票、期權——不含指數期權) |
| 香港 | 香港股票產品(包括股票、ETF) |
| 中國大陸 | 滬深港通股票 |
2. 基礎 URL
- 正式環境:
https://api.webull.hk/ - 沙盒環境:
https://api.sandbox.webull.hk
3. 範例
3.1 股票訂單
- Python
- Java
import uuid
from webull.core.client import ApiClient
from webull.trade.trade_client import TradeClient
optional_api_endpoint = "<webull_api_host>" # PRD env host: api.webull.hk. Test env host: api.sanbox.webull.hk
your_app_key = "<your_app_key>"
your_app_secret = "<your_app_secret>"
region_id = "hk"
account_id = "<your_account_id>" # Use account_list interface to get account info
api_client = ApiClient(your_app_key, your_app_secret, region_id)
api_client.add_endpoint(region_id, optional_api_endpoint)
if __name__ == '__main__':
trade_client = TradeClient(api_client)
# simple order
client_order_id = uuid.uuid4().hex
print('client order id:', client_order_id)
new_simple_orders = [
{
"client_order_id": client_order_id,
"symbol": "BULL",
"instrument_type": "EQUITY",
"market": "US",
"order_type": "LIMIT",
"limit_price": "26",
"quantity": "1",
"support_trading_session": "CORE",
"side": "BUY",
"time_in_force": "DAY",
"entrust_type": "QTY"
}
]
# For hk stock place params
new_hk_stock_simple_orders = [
{
"client_order_id": client_order_id,
"symbol": "00700",
"instrument_type": "EQUITY",
"market": "HK",
"order_type": "ENHANCED_LIMIT",
"limit_price": "612",
"quantity": "100",
"side": "BUY",
"time_in_force": "DAY",
"entrust_type": "QTY"
}
]
res = trade_client.order_v2.preview_order(account_id, new_simple_orders)
if res.status_code == 200:
print('preview order res:', res.json())
res = trade_client.order_v2.place_order(account_id, new_simple_orders)
if res.status_code == 200:
print('place order res:', res.json())
modify_simple_orders = [
{
"client_order_id": client_order_id,
"quantity": "2",
"limit_price": "25"
}
]
res = trade_client.order_v2.replace_order(account_id, modify_simple_orders)
if res.status_code == 200:
print('replace order res:', res.json())
res = trade_client.order_v2.cancel_order(account_id, client_order_id)
if res.status_code == 200:
print('cancel order res:', res.json())
res = trade_client.order_v2.get_order_detail(account_id, client_order_id)
if res.status_code == 200:
print('order detail:', res.json())
import com.webull.openapi.core.common.Region;
import com.webull.openapi.core.common.dict.*;
import com.webull.openapi.core.http.HttpApiConfig;
import com.webull.openapi.core.logger.Logger;
import com.webull.openapi.core.logger.LoggerFactory;
import com.webull.openapi.core.utils.GUID;
import com.webull.openapi.samples.config.Env;
import com.webull.openapi.trade.request.v2.TradeOrder;
import com.webull.openapi.trade.request.v2.TradeOrderItem;
import com.webull.openapi.trade.response.v2.OrderHistory;
import com.webull.openapi.trade.response.v2.TradeOrderResponse;
import java.util.ArrayList;
import java.util.List;
public class OrderStockTradeClient {
private static final Logger logger = LoggerFactory.getLogger(OrderStockTradeClient.class);
public static void main(String[] args) {
OrderStockTradeClient orderStockTradeClient = new OrderStockTradeClient();
HttpApiConfig apiConfig = HttpApiConfig.builder()
.appKey("<your_app_key>") //<your_app_key>
.appSecret("<your_app_secret>") //<your_app_secret>
.regionId("hk") //<your_region_id> @see com.webull.openapi.core.common.Region
.endpoint("<webull_api_host>") //PRD env host: api.webull.hk. Test env host: api.sanbox.webull.hk
.build();
com.webull.openapi.trade.TradeClientV2 apiService = new com.webull.openapi.trade.TradeClientV2(apiConfig);
// Use getAccountList interface to get account info
String accountId = "#{accountId}"; //<your_account_id> from by Account Api
String clientOrderId = GUID.get();
com.webull.openapi.trade.TradeClientV2 apiService = new com.webull.openapi.trade.TradeClientV2(apiConfig);
// build place order params
TradeOrder tradeOrder = orderStockTradeClient.buildPlaceStockParams(clientOrderId);
// place order
TradeOrderResponse placeOrderResp = apiService.placeOrder(accountId,tradeOrder);
logger.info("Place order response: {}", placeOrderResp);
// get order detail
OrderHistory orderDetail = apiService.getOrderDetails(accountId,clientOrderId);
logger.info("Order details response: {}", orderDetail);
// replace order
TradeOrder modifyTradeOrder = orderStockTradeClient.buildReplaceOrderParams(clientOrderId);
TradeOrderResponse modifyOrderResponse = apiService.replaceOrder(accountId, modifyTradeOrder);
logger.info("Order modify response: {}", modifyOrderResponse);
// query order detail after replace order
OrderHistory orderDetail1 = apiService.getOrderDetails(accountId, clientOrderId);
logger.info("Order orderDetail response after replace order: {}", orderDetail1);
// cancel order
TradeOrder cancelOrder = new TradeOrder();
cancelOrder.setClientOrderId(clientOrderId);
TradeOrderResponse cancelOrderResponse = apiService.cancelOrder(accountId, cancelOrder);
logger.info("Order cancel order response: {}", cancelOrderResponse);
// query order detail after cancel order
OrderHistory orderDetail2 = apiService.getOrderDetails(accountId, clientOrderId);
logger.info("Order orderDetail response after cancel: {}", orderDetail2.getOrders().get(0).getStatus());
}
/**
* build hk stock place params
* Only for Webull HK's Client
* @param clientOrderId
* @return
*/
private TradeOrder buildPlaceHKStockParams(String clientOrderId) {
TradeOrder tradeOrder = new TradeOrder();
List<TradeOrderItem> newOrders = new ArrayList<>();
TradeOrderItem placeOne = new TradeOrderItem();
placeOne.setClientOrderId(clientOrderId);
// WebullUS need set combo_type, because WebullUS support combo order
placeOne.setComboType(ComboType.NORMAL.name());
newOrders.add(placeOne);
placeOne.setSymbol("00700");
placeOne.setInstrumentType(InstrumentSuperType.EQUITY.name());
placeOne.setMarket("HK");
placeOne.setOrderType(OrderType.ENHANCED_LIMIT.name());
placeOne.setQuantity("100");
placeOne.setLimitPrice("610");
placeOne.setSide(OrderSide.BUY.name());
placeOne.setTimeInForce(OrderTIF.DAY.name());
placeOne.setEntrustType(EntrustType.QTY.name());
tradeOrder.setNewOrders(newOrders);
return tradeOrder;
}
/**
* build your place order object
*
* @param clientOrderId
* @return
*/
private TradeOrder buildPlaceStockParams(String clientOrderId) {
TradeOrder tradeOrder = new TradeOrder();
List<TradeOrderItem> newOrders = new ArrayList<>();
TradeOrderItem placeOne = new TradeOrderItem();
placeOne.setClientOrderId(clientOrderId);
// WebullUS need set combo_type, because WebullUS support combo order
placeOne.setComboType(ComboType.NORMAL.name());
newOrders.add(placeOne);
placeOne.setSymbol("BULL");
placeOne.setInstrumentType(InstrumentSuperType.EQUITY.name());
placeOne.setMarket(Region.us.name().toUpperCase());
placeOne.setOrderType(OrderType.LIMIT.name());
placeOne.setQuantity("1");
placeOne.setLimitPrice("25");
placeOne.setSupportTradingSession("ALL");
placeOne.setSide(OrderSide.BUY.name());
placeOne.setTimeInForce(OrderTIF.DAY.name());
placeOne.setEntrustType(EntrustType.QTY.name());
tradeOrder.setNewOrders(newOrders);
return tradeOrder;
}
/**
* build your replace order params
* @param clientOrderId
* @return replace order object
*/
private TradeOrder buildReplaceOrderParams(String clientOrderId) {
TradeOrder replaceTradeOrder = new TradeOrder();
List<TradeOrderItem> modifyOrders = new ArrayList<>();
TradeOrderItem modifyOne = new TradeOrderItem();
modifyOne.setClientOrderId(clientOrderId);
modifyOne.setLimitPrice("25");
modifyOne.setQuantity("2");
modifyOrders.add(modifyOne);
replaceTradeOrder.setModifyOrders(modifyOrders);
return replaceTradeOrder;
}
}
3.2 期權訂單
- Python
- Java
import uuid
from webull.core.client import ApiClient
from webull.trade.trade_client import TradeClient
optional_api_endpoint = "<webull_api_host>" # PRD env host: api.webull.hk. Test env host: api.sanbox.webull.hk
your_app_key = "<your_app_key>"
your_app_secret = "<your_app_secret>"
region_id = "hk"
account_id = "<your_account_id>" # Use account_list interface to get account info
api_client = ApiClient(your_app_key, your_app_secret, region_id)
api_client.add_endpoint(region_id, optional_api_endpoint)
if __name__ == '__main__':
trade_client = TradeClient(api_client)
# Options
# For option order inquiries, please use the V2 query interface: api.order_v2.get_order_detail(account_id, client_order_id).
client_order_id = uuid.uuid4().hex
option_new_orders = [
{
"client_order_id": client_order_id,
"combo_type": "NORMAL",
"order_type": "LIMIT",
"quantity": "1",
"limit_price": "21.25",
"option_strategy": "SINGLE",
"side": "BUY",
"time_in_force": "GTC",
"entrust_type": "QTY",
"legs": [
{
"side": "BUY",
"quantity": "1",
"symbol": "TSLA",
"strike_price": "400",
"option_expire_date": "2025-11-26",
"instrument_type": "OPTION",
"option_type": "CALL",
"market": "US"
}
]
}
]
# preview
res = trade_client.order_v2.preview_option(account_id, option_new_orders)
if res.status_code == 200:
print("preview option res:", res.json())
# place
res = trade_client.order_v2.place_option(account_id, option_new_orders)
if res.status_code == 200:
print("place option res:" , res.json())
option_modify_orders = [
{
"client_order_id": client_order_id,
"quantity": "2",
"limit_price": "21.25"
}
]
res = trade_client.order_v2.replace_option(account_id, option_modify_orders)
if res.status_code == 200:
print("Replace option order res:" , res.json())
res = trade_client.order_v2.cancel_option(account_id, client_order_id)
if res.status_code == 200:
print("Replace option order res:" , res.json())
res = trade_client.order_v2.get_order_detail(account_id, client_order_id)
if res.status_code == 200:
print("Option order detail order res:" , res.json())
import com.webull.openapi.core.common.dict.*;
import com.webull.openapi.core.http.HttpApiConfig;
import com.webull.openapi.core.logger.Logger;
import com.webull.openapi.core.logger.LoggerFactory;
import com.webull.openapi.core.utils.GUID;
import com.webull.openapi.trade.request.v2.OptionOrder;
import com.webull.openapi.trade.request.v2.OptionOrderItem;
import com.webull.openapi.trade.request.v2.OptionOrderItemLeg;
import com.webull.openapi.trade.response.v2.OrderHistory;
import com.webull.openapi.trade.response.v2.TradeOrderResponse;
import java.util.ArrayList;
import java.util.List;
public class OrderOptionTradeClient {
private static final Logger logger = LoggerFactory.getLogger(OrderOptionTradeClient.class);
public static void main(String[] args) {
OrderOptionTradeClient orderOptionTradeClient = new OrderOptionTradeClient();
HttpApiConfig apiConfig = HttpApiConfig.builder()
.appKey("<your_app_key>") //<your_app_key>
.appSecret("<your_app_secret>") //<your_app_secret>
.regionId("hk") //<your_region_id> @see com.webull.openapi.core.common.Region
.endpoint("<webull_api_host>") //PRD env host: api.webull.hk. Test env host: api.sanbox.webull.hk
.build();
com.webull.openapi.trade.TradeClientV2 apiService = new com.webull.openapi.trade.TradeClientV2(apiConfig);
// Use getAccountList interface to get account info
String accountId = "#{accountId}"; //<your_account_id> from by Account Api
String clientOrderId = GUID.get();
// build place option order params
OptionOrder optionOrder = orderOptionTradeClient.buildOptionPlaceParams(clientOrderId);
TradeOrderResponse tradeOrderResponse = apiService.placeOption(accountId, optionOrder);
logger.info("Place option order response: {}", tradeOrderResponse);
// get option order detail
OrderHistory orderDetails = apiService.getOrderDetails(accountId, clientOrderId);
logger.info("Order details response: {}", orderDetails);
OptionOrder replaceOptionOrder = orderOptionTradeClient.buildReplaceOptionPlaceParams(clientOrderId);
TradeOrderResponse replaceResponse = apiService.replaceOption(accountId, replaceOptionOrder);
logger.info("Replace option order response: {}", replaceResponse);
// get option order detail
OrderHistory orderDetails2= apiService.getOrderDetails(accountId, clientOrderId);
logger.info("Order details response: {}", orderDetails2);
// cancel order
OptionOrder cancelOption = new OptionOrder();
cancelOption.setClientOr derId(clientOrderId);
TradeOrderResponse orderResponse = apiService.cancelOption(accountId, cancelOption);
logger.info("Option order cancel response: {}", orderResponse);
}
/**
* build your option stock place params
* @param clientOrderId
* @return option order place params
*/
private OptionOrder buildOptionPlaceParams(String clientOrderId) {
// Options
OptionOrderItemLeg optionOrderItemLeg = new OptionOrderItemLeg();
optionOrderItemLeg.setSide(OrderSide.BUY.name());
optionOrderItemLeg.setQuantity("1");
optionOrderItemLeg.setSymbol("AAPL");
optionOrderItemLeg.setStrikePrice("251");
optionOrderItemLeg.setOptionExpireDate("2025-11-19");
optionOrderItemLeg.setInstrumentType(InstrumentSuperType.OPTION.name());
optionOrderItemLeg.setOptionType(OptionType.CALL.name());
optionOrderItemLeg.setMarket(Markets.US.name());
List<OptionOrderItemLeg> optionOrderItemLegList = new ArrayList<>();
optionOrderItemLegList.add(optionOrderItemLeg);
OptionOrderItem optionOrderItem = new OptionOrderItem();
optionOrderItem.setClientOrderId(clientOrderId);
optionOrderItem.setComboType(ComboType.NORMAL.name());
optionOrderItem.setOptionStrategy(OptionStrategy.SINGLE.name());
optionOrderItem.setSide(OrderSide.BUY.name());
optionOrderItem.setOrderType(OrderType.LIMIT.name());
optionOrderItem.setTimeInForce(OrderTIF.GTC.name());
optionOrderItem.setLimitPrice("20.5");
optionOrderItem.setQuantity("1");
optionOrderItem.setEntrustType(EntrustType.QTY.name());
optionOrderItem.setLegs(optionOrderItemLegList);
List<OptionOrderItem> optionOrderItemList = new ArrayList<>();
optionOrderItemList.add(optionOrderItem);
OptionOrder optionOrder = new OptionOrder();
optionOrder.setNewOrders(optionOrderItemList);
return optionOrder;
}
/**
* build your option stock place params
* @param clientOrderId
* @return option order place params
*/
private OptionOrder buildReplaceOptionPlaceParams(String clientOrderId) {
OptionOrder replaceOptionOrder = new OptionOrder();
replaceOptionOrder.setClientOrderId(clientOrderId);
List<OptionOrderItem> modifyOrders = new ArrayList<>();
replaceOptionOrder.setModifyOrders(modifyOrders);
OptionOrderItem optionOrderItem = new OptionOrderItem();
modifyOrders.add(optionOrderItem);
optionOrderItem.setClientOrderId(clientOrderId);
optionOrderItem.setQuantity("2");
return replaceOptionOrder;
}
}