Futures Trading
Webull's Futures API enables developers to trade and query over HTTP. For more details, please refer to the Trading API Reference.
Before calling the Trading API, you need to have an App Key and secret. For more information, please refer to the Individual Application Process.
Base URLs
- Production Environment:
https://api.webull.com/ - Test Environment:
http://us-openapi-alb.uat.webullbroker.com/
Supported Futures Contracts
Webull provides futures contracts for indices, interest rates, currencies, agriculture, metals, energies, and cryptocurrencies.
Contract trading codes
Contract codes are often one to three letter codes identifying the asset that is attached to a specific contract. For example, E-mini S&P 500 futures' contract code is ES. Make note that these codes could vary across platforms. Following the contract code should be a letter and a number, for example, ESG8. The G represents a month (February) and the 8 represents a year (2018). Each month has its own letter. So, as in the example listed above, ESG8 would represent an E-mini S&P 500 futures contract expiring in February of 2018.
| Jan. | Feb. | Mar. | Apr. | May | Jun. | Jul. | Aug. | Sep. | Oct. | Nov. | Dec. |
|---|---|---|---|---|---|---|---|---|---|---|---|
| F | G | H | J | K | M | N | Q | U | V | X | Z |
To retrieve all available futures contracts, please use the API call below:
- Python
- Java
from webull.data.common.category import Category
from webull.data.common.contract_type import ContractType
from webull.data.common.timespan import Timespan
from webull.core.client import ApiClient
from webull.data.data_client import DataClient
optional_api_endpoint = "<api_endpoint>" # PRD env host: api.webull.com; Test env host: us-openapi-alb.uat.webullbroker.com
your_app_key = "<your_app_key>"
your_app_secret = "<your_app_secret>"
region_id = "us"
api_client = ApiClient(your_app_key, your_app_secret, region_id)
api_client.add_endpoint(region_id, optional_api_endpoint)
if __name__ == '__main__':
data_client = DataClient(api_client)
res = data_client.futures_market_data.get_futures_quotes("SILZ5", Category.US_FUTURES.name, depth=1)
if res.status_code == 200:
print('get_futures_quotes:', res.json())
res = data_client.futures_market_data.get_futures_history_bars('SILZ5,6BM6', Category.US_FUTURES.name, Timespan.M1.name)
if res.status_code == 200:
print('get_futures_history_bars:', res.json())
res = data_client.futures_market_data.get_futures_tick("SILZ5", Category.US_FUTURES.name, count=10)
if res.status_code == 200:
print('get_futures_tick:', res.json())
res = data_client.futures_market_data.get_futures_snapshot("SILZ5,6BM6", Category.US_FUTURES.name)
if res.status_code == 200:
print('get_futures_snapshot:', res.json())
res = data_client.instrument.get_futures_products(Category.US_FUTURES.name)
if res.status_code == 200:
print('get_futures_products:', res.json())
res = data_client.instrument.get_futures_instrument("ESZ5", Category.US_FUTURES.name)
if res.status_code == 200:
print('get_futures_instrument:', res.json())
res = data_client.instrument.get_futures_instrument_by_code("ES", Category.US_FUTURES.name, ContractType.MONTHLY.name)
if res.status_code == 200:
print('get_futures_instrument_by_code:', res.json())
import com.webull.openapi.core.common.dict.Category;
import com.webull.openapi.core.common.dict.ContractType;
import com.webull.openapi.core.common.dict.Timespan;
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.data.quotes.api.IDataClient;
import com.webull.openapi.data.quotes.domain.*;
import com.webull.openapi.samples.config.Env;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DataClient {
private static final Logger logger = LoggerFactory.getLogger(DataClient.class);
public static void main(String[] args) {
HttpApiConfig apiConfig = HttpApiConfig.builder()
.appKey(Env.APP_KEY)
.appSecret(Env.APP_SECRET)
.regionId(Env.REGION_ID)
// .endpoint("<api_endpoint>")
.build();
IDataClient dataClient = new com.webull.openapi.data.DataClient(apiConfig);
Quote futuresQuote = dataClient.getFuturesQuote("SILZ5", Category.US_FUTURES.name(), "1");
logger.info("Futures Quote: {}", futuresQuote);
Tick futureTicks = dataClient.getFuturesTicks("SILZ5", Category.US_FUTURES.name(), 100);
logger.info("Futures Ticks: {}", futureTicks);
Set<String> futuresSymbols = new HashSet<>();
futuresSymbols.add("ESZ5");
futuresSymbols.add("6BM6");
List<Snapshot> futureSnapshots = dataClient.getFuturesSnapshots(futuresSymbols, Category.US_FUTURES.name());
logger.info("Futures Snapshots: {}", futureSnapshots);
List<NBar> futureBars = dataClient.getFuturesBars(new ArrayList<>(futuresSymbols), Category.US_FUTURES.name(), Timespan.M1.name(), 10, false);
logger.info("Futures Bars: {}", futureBars);
List<FuturesProduct> futuresProducts = dataClient.getFuturesProducts(Category.US_FUTURES.name());
logger.info("Futures Products: {}", futuresProducts);
List<FuturesInstrument> futuresInstruments = dataClient.getFuturesInstruments(futuresSymbols, Category.US_FUTURES.name());
logger.info("Futures Instruments: {}", futuresInstruments);
List<FuturesInstrument> futuresInstrumentsByCode = dataClient.getFuturesInstrumentsByCode("ES", Category.US_FUTURES.name(), ContractType.MONTHLY.name());
logger.info("Futures Instruments By Code: {}", futuresInstrumentsByCode);
}
}
Futures Orders
Trading Hours
The market for futures is open virtually 24 hrs. a day and 6 days a week. However, each product has its own specific hours of trading.
Order Types
Futures trading supports only the following order types:
| Values |
|---|
| MARKET |
| LIMIT |
| STOP_LOSS |
| STOP_LOSS_LIMIT |
| TRAILING_STOP_LOSS |
Combo Orders are not supported for futures trading at this time.
TIF
Futures trading supports only the following time in force:
| Values |
|---|
| DAY |
| GTC |
Margin
- Initial margin is the cash you must deposit to open a position; it is usually three to twelve percent of the contract value.
- Intraday margin is available between 9:30 a.m. and 4:00 p.m. Eastern Time and is lower than overnight margin.
- Maintenance margin is the lowest equity you may keep while the position is open; it is lower than the initial margin. If your equity falls below maintenance margin you will receive a margin call; you will be asked to provide sufficient funds to bring the account back up to the initial margin requirement or your position will be liquidated.
Settlement & Expiry
- The majority of traders do not take physical delivery. They offset their positions before expiration by entering an opposite trade, canceling out their obligation. Physical delivery is not permitted at Webull.
- If a position is not closed by expiration, then the contract holder is required to either accept or provide the physical delivery of the underlying commodity. Most traders close their position before expiration to avoid taking physical delivery.
Code Example
- Python
- Java
import json
import uuid
from time import sleep
from webull.core.client import ApiClient
from webull.trade.trade_client import TradeClient
optional_api_endpoint = "<api_endpoint>" # PRD env host: api.webull.com; Test env host: us-openapi-alb.uat.webullbroker.com
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)
# normal futures order
normal_futures_client_order_id = uuid.uuid4().hex
print('futures client order id:', normal_futures_client_order_id)
new_normal_futures_orders = [
{
"combo_type": "NORMAL",
"client_order_id": normal_futures_client_order_id,
"symbol": "ESZ5",
"instrument_type": "FUTURES",
"market": "US",
"order_type": "LIMIT",
"limit_price": "4500",
"quantity": "1",
"side": "BUY",
"time_in_force": "DAY",
"entrust_type": "QTY"
}
]
res = trade_client.order_v3.place_order(account_id, new_normal_futures_orders)
if res.status_code == 200:
print('place normal futures order res:', res.json())
sleep(3)
# normal futures order replace
replace_normal_futures_orders = [
{
"client_order_id": normal_futures_client_order_id,
"quantity": "2",
"limit_price": "4550"
}
]
res = trade_client.order_v3.replace_order(account_id, replace_normal_futures_orders)
if res.status_code == 200:
print('replace normal futures order res:', res.json())
sleep(3)
# normal futures order cancel
res = trade_client.order_v3.cancel_order(account_id, normal_futures_client_order_id)
if res.status_code == 200:
print('cancel normal futures order res:', res.json())
# get futures order detail
res = trade_client.order_v3.get_order_detail(account_id, normal_futures_client_order_id)
if res.status_code == 200:
print('get futures order detail res:', res.json())
# get futures open orders
res = trade_client.order_v3.get_order_open(account_id, page_size=10)
if res.status_code == 200:
print("order_open_res=" + json.dumps(res.json(), indent=4))
# get futures order history
res = trade_client.order_v3.get_order_history(account_id, page_size=10)
if res.status_code == 200:
print('get order history res:', res.json())
import com.webull.openapi.core.common.dict.ComboType;
import com.webull.openapi.core.common.dict.EntrustType;
import com.webull.openapi.core.common.dict.InstrumentSuperType;
import com.webull.openapi.core.common.dict.Markets;
import com.webull.openapi.core.common.dict.OrderSide;
import com.webull.openapi.core.common.dict.OrderTIF;
import com.webull.openapi.core.common.dict.OrderType;
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.v3.TradeOrder;
import com.webull.openapi.trade.request.v3.TradeOrderItem;
import com.webull.openapi.trade.response.v3.OrderHistory;
import com.webull.openapi.trade.response.v3.PreviewOrderResponse;
import com.webull.openapi.trade.response.v3.TradeOrderResponse;
import java.util.ArrayList;
import java.util.List;
public class OrderTradeClient {
private static final Logger logger = LoggerFactory.getLogger(TradeClientV3.class);
public static void main(String[] args) throws InterruptedException {
OrderTradeClient orderTradeClient = new OrderTradeClient();
HttpApiConfig apiConfig = HttpApiConfig.builder()
.appKey(Env.APP_KEY) //<your_app_key>
.appSecret(Env.APP_SECRET) //<your_app_secret>
.regionId(Env.REGION_ID) //<your_region_id> @see com.webull.openapi.core.common.Region
// .endpoint("<api_endpoint>") // env api host endpoint
.build();
com.webull.openapi.trade.TradeClientV3 apiService =
new com.webull.openapi.trade.TradeClientV3(apiConfig);
// Use getAccountList interface to get account info
String accountId = "#{accountId}"; //<your_account_id> from by Account Api
String clientOrderId = GUID.get();
// build place order params
TradeOrder tradeOrder = orderTradeClient.buildPlaceFuturesParam(clientOrderId);
// preview order
PreviewOrderResponse previewOrderResponse = apiService.previewOrder(accountId, tradeOrder);
logger.info("preview order resp: {}", previewOrderResponse);
// place order
TradeOrderResponse placeOrderResp = apiService.placeOrder(accountId, tradeOrder);
logger.info("place order resp: {}", placeOrderResp);
Thread.sleep(2000);
// modify order
TradeOrder replaceOrder = orderTradeClient.buildReplaceFuturesParam(clientOrderId);
TradeOrderResponse replaceOrderResp = apiService.replaceOrder(accountId, replaceOrder);
logger.info("replace order resp: {}", replaceOrderResp);
Thread.sleep(2000);
// cancel order
TradeOrder cancelOrder = orderTradeClient.buildCancelFuturesParam(clientOrderId);
TradeOrderResponse cancelOrderResp = apiService.cancelOrder(accountId, cancelOrder);
logger.info("cancel order resp: {}", cancelOrderResp);
Thread.sleep(2000);
// list futures orders
List<OrderHistory> listOrdersResp = apiService.listOrders(accountId, 10, null, null, null);
logger.info("list orders resp: {}", listOrdersResp);
// list open futures orders
List<OrderHistory> openOrdersResp = apiService.openOrders(accountId, 10, null);
logger.info("open orders resp: {}", openOrdersResp);
// get futures order detail
OrderHistory orderDetailResp = apiService.getOrderDetails(accountId, clientOrderId);
logger.info("order detail resp: {}", orderDetailResp);
}
private TradeOrder buildPlaceFuturesParam(String clientOrderId) {
TradeOrder tradeOrder = new TradeOrder();
List<TradeOrderItem> orderItemList = new ArrayList<>();
TradeOrderItem orderItem = new TradeOrderItem();
orderItemList.add(orderItem);
orderItem.setClientOrderId(clientOrderId);
orderItem.setComboType(ComboType.NORMAL.name());
orderItem.setSymbol("ESZ5");
orderItem.setInstrumentType(InstrumentSuperType.FUTURES.name());
orderItem.setMarket(Markets.US.name());
orderItem.setOrderType(OrderType.LIMIT.name());
orderItem.setQuantity("1");
orderItem.setLimitPrice("46000");
orderItem.setSide(OrderSide.BUY.name());
orderItem.setTimeInForce(OrderTIF.DAY.name());
orderItem.setEntrustType(EntrustType.QTY.name());
tradeOrder.setNewOrders(orderItemList);
logger.info("tradeOrder: {}", tradeOrder);
return tradeOrder;
}
private TradeOrder buildReplaceFuturesParam(String clientOrderId) {
TradeOrder replaceOrder = new TradeOrder();
List<TradeOrderItem> replaceOrderItemList = new ArrayList<>();
TradeOrderItem replaceOrderItem = new TradeOrderItem();
replaceOrderItemList.add(replaceOrderItem);
replaceOrderItem.setClientOrderId(clientOrderId);
replaceOrderItem.setLimitPrice("46000");
replaceOrderItem.setQuantity("2");
replaceOrder.setModifyOrders(replaceOrderItemList);
logger.info("replaceOrder: {}", replaceOrder);
return replaceOrder;
}
private TradeOrder buildCancelFuturesParam(String clientOrderId) {
TradeOrder cancelOrder = new TradeOrder();
cancelOrder.setClientOrderId(clientOrderId);
logger.info("cancelOrder: {}", cancelOrder);
return cancelOrder;
}
}
Futures Market Data
Webull's Futures Market Data API offers futures market data access over HTTP. For more details, please refer to the Futures Market Data API Reference.
Currently, access to futures data via the OpenAPI requires a paid market-data subscription, which grants the necessary authorization. This subscription module is under active development and will be released soon—please stay tuned.
Futures Trading Fees
Please refer to the Webull's fee Schedule