futu-java-samples is a Java port of the futu-python-samples project, demonstrating the Futu OpenAPI Java SDK (com.futunn.openapi:futu-api:10.7.6708). Each example is a standalone Java file runnable via Maven.
The SDK uses a callback-driven async model — all API calls return immediately, and responses arrive via SPI (Service Provider Interface) callbacks. This differs significantly from the synchronous Python SDK and requires careful handling of the async flow.
The SDK uses retCode differently across sync vs async contexts:
| Context | retCode=0 |
retCode=2 |
Action |
|---|---|---|---|
Sync return (qot.getGlobalState(req)) |
Success | Error | Check ret value |
Async callback (onReply_GetGlobalState) |
Not used for success | Server returned data in s2C | Check rsp.getS2C().getQotLogined() |
In async callbacks, retCode=2 with qotLogined=true trdLogined=true is the
expected success path for getGlobalState. Never treat retCode=2 as an
error without also checking hasS2C() and the login flags.
GetGlobalState.Request.getDefaultInstance() creates an uninitialized message
that throws UninitializedMessageException at serialization time. Always build
the C2S struct explicitly:
// Wrong:
qot.getGlobalState(GetGlobalState.Request.getDefaultInstance())
// → UninitializedMessageException: Message missing required fields: c2s
// Correct:
GetGlobalState.C2S c2s = GetGlobalState.C2S.newBuilder().setUserID(0).build();
GetGlobalState.Request req = GetGlobalState.Request.newBuilder().setC2S(c2s).build();
qot.getGlobalState(req);getBasicQot returns retCode=3 with message "Before calling the Get Real-time
Quotes interface, please subscribe to Basic data first." Use getSecuritySnapshot
instead for one-shot quote retrieval without any prior subscription.
The Java SDK requires PKCS#1 format (-----BEGIN RSA PRIVATE KEY-----).
Config.java auto-converts PKCS#8 (-----BEGIN PRIVATE KEY-----) to PKCS#1
via openssl rsa -traditional at startup. The converted key content is stored
as Config.RSA_KEY_CONTENT.
Loads environment variables from .env using dotenv-java. Supports:
- Single host mode:
FUTU_OPEND_HOST/FUTU_OPEND_PORT - HA mode:
FUTU_OPEND_HOSTS— comma-separatedhost:port:isRSAtuples - RSA key loading: Auto-converts PKCS#8 → PKCS#1 via OpenSSL at startup
The foundation for all examples. Two modes:
- Single connect: calls
initConnect()directly - HA mode: parallel TCP probe across all hosts → pick fastest → connect
Flow:
tcpProbe()→HostEntry.parse()→initConnect()→onInitConnectcallback
All quote-related examples use FTAPI_Conn_Qot. Two API flavors:
- Request/Reply:
getSecuritySnapshot,getKL,getOrderBook,getTicker,getRT,stockFilter,getOptionChain - Subscribe/Push:
sub()to register, then push callbacks fire continuously
Note: getBasicQot requires prior subscription; use getSecuritySnapshot instead for no-subscription one-shot quotes.
Separate connection for trading operations. Requires:
- Trading context unlock (
unlockTradewith MD5(password)) TrdHeaderbuilt from accId + trdEnv + trdMarket on every request
- Stock Filter (
Example03_StockFilter):stockFilter()withBaseFilter+FinancialFilter - Option Chain (
Example25_OptionChain):getOptionExpirationDate()+getOptionChain() - K-Line (
Example07_Kline):getKL()(current bars) +requestHistoryKL()(historical, with pagination vianextReqKey)
main() → FTAPI.init()
→ run(): getHosts() [parses hosts from Config]
→ run(): tcpProbe() [parallel TCP connect, timeout 3s]
→ tcpConnect() × N hosts in parallel
→ run(): sort by latency → pick fastest
→ tryConnect(fastest)
→ qot.setClientInfo()
→ qot.setConnSpi(this)
→ qot.setRSAPrivateKey() [PKCS#1 key from Config]
→ qot.initConnect(host, port, isRSA)
→ poll connected flag [wait up to 8s]
→ onInitConnect callback fires (errCode=0 → connected=true)
→ qot.getGlobalState(GetGlobalState.C2S.setUserID(0))
→ onReply_GetGlobalState callback
[retCode=2 + qotLogined=true + trdLogined=true = SUCCESS]
main() → start()
→ qot.initConnect() → onInitConnect
→ qot.sub(QotSub) [isSubOrUnSub=true, isRegPush=true, isFirstPush=true]
→ onReply_Sub callback [confirms subscription]
→ push callbacks fire: onPush_UpdateBasicQuote, onPush_UpdateOrderBook,
onPush_UpdateTicker, onPush_UpdateBroker
→ qot.sub(QotSub) [isSubOrUnSub=false] to unsubscribe
→ qot.close()
main() → start()
→ qot.initConnect() + trd.initConnect() [dual connections]
→ trd.getAccList()
→ onReply_GetAccList: accList populated, first acc selected
→ build TrdHeader (accId + trdEnv=SIM + trdMarket)
→ qot.getSecuritySnapshot() [get current price + lot_size]
→ onReply_GetSecuritySnapshot: lastSnapshotPrice, lotSize saved
→ trd.getFunds()
→ onReply_GetFunds: lastFundsPower saved
→ trd.getPositionList()
→ onReply_GetPositionList
→ qty = floor(lastFundsPower / lastSnapshotPrice / lotSize) * lotSize
→ trd.placeOrder(TrdSide=BUY, qty, price)
→ onReply_PlaceOrder: orderID returned
→ qot.close() + trd.close()
main() → start()
→ qot.initConnect() → onInitConnect
→ qot.getKL() [for DAY, 60M, 30M, 5M periods]
→ onReply_GetKL callback
→ qot.requestHistoryKL(beginTime, endTime, maxAckNum=100)
→ onReply_RequestHistoryKL
[may have nextReqKey if more data]
→ if nextReqKey present: repeat with nextReqKey
main() → start()
→ qot.initConnect()
→ qot.sub(QotSub ORDER_BOOK) [subscribe first]
→ onReply_Sub
→ qot.getOrderBook(num=10) [fetch 10-level depth]
→ onReply_GetOrderBook: logs bid/ask levels, spread, volume ratio
→ qot.getOrderBook(num=50) [fetch 50-level for total volume]
| Market | ID | Notes |
|---|---|---|
| HK Securities | 1 | 00700, HSI (index — not a stock, use futures合约) |
| US Securities | 11 | AAPL, NDX (not market ID 2) |
| SH | 4 | |
| SZ | 5 | |
| HK Future | 7 | HSImain |
| US Future | 23 | |
| SG Future | 13 | |
| JP Future | 25 |
Note: US securities use market ID
11inQotCommon.QotMarket, not2.
| Env | ID | Unlock required |
|---|---|---|
| SIMULATE | 1 | No |
| REAL | 2 | Yes — unlockTrade(MD5(password)) |
flowchart TB
subgraph "External Services"
OpenD["FutuOpenD Gateway<br/>:11111"]
end
subgraph "Configuration"
Config["Config.java<br/>.env loader<br/>RSA PKCS#1 key<br/>Host resolution"]
end
subgraph "Connection Layer"
FTAPI["FTAPI.init()"]
FTAPI_Conn_Qot["FTAPI_Conn_Qot<br/>Quote connection"]
FTAPI_Conn_Trd["FTAPI_Conn_Trd<br/>Trading connection"]
end
subgraph "SPI Callbacks"
FTSPI_Conn["FTSPI_Conn<br/>onInitConnect<br/>onDisconnect"]
FTSPI_Qot["FTSPI_Qot<br/>onReply_*<br/>onPush_*"]
FTSPI_Trd["FTSPI_Trd<br/>onReply_*<br/>onPush_*"]
end
subgraph "Quote Examples"
Ex00["Example00_ConnectHA<br/>HA + getGlobalState"]
Ex01["Example01_MarketSnapshot<br/>getSecuritySnapshot"]
Ex02["Example02_QuotePush<br/>sub + push"]
Ex03["Example03_StockFilter<br/>stockFilter screener"]
Ex07["Example07_Kline<br/>getKL + historyKL"]
Ex08["Example08_RtTicker<br/>getTicker + getRT"]
Ex10["Example10_OrderBook<br/>getOrderBook depth"]
Ex25["Example25_OptionChain<br/>getOptionChain"]
end
subgraph "Trading Examples"
Ex05["Example05_QuoteTrade<br/>qot + trd dual"]
Ex30["Example30_UserInfo<br/>getAccList"]
Ex32["Example32_OrderQuery<br/>unlock + query"]
Ex33["Example33_TradingInfo<br/>getMaxTrdQtys"]
end
Config -->|RSA key, host config| FTAPI
FTAPI --> FTAPI_Conn_Qot
FTAPI --> FTAPI_Conn_Trd
FTAPI_Conn_Qot -->|setConnSpi| FTSPI_Conn
FTAPI_Conn_Qot -->|setQotSpi| FTSPI_Qot
FTAPI_Conn_Trd -->|setConnSpi| FTSPI_Conn
FTAPI_Conn_Trd -->|setTrdSpi| FTSPI_Trd
Ex00 -->|TCP probe + connect| OpenD
Ex01 -->|getSecuritySnapshot| OpenD
Ex02 -->|sub + push| OpenD
Ex03 -->|stockFilter| OpenD
Ex07 -->|getKL + historyKL| OpenD
Ex08 -->|getTicker + getRT| OpenD
Ex10 -->|sub + getOrderBook| OpenD
Ex25 -->|getOptionChain| OpenD
Ex05 -->|qot + trd| OpenD
Ex30 -->|trd getAccList| OpenD
Ex32 -->|trd unlock + query| OpenD
Ex33 -->|trd getMaxTrdQtys| OpenD
style Config fill:#e1f5fe
style FTAPI fill:#fff3e0
style OpenD fill:#f3e5f5
All SDK calls are fire-and-forget with a matching callback:
qot.getSecuritySnapshot(req) → onReply_GetSecuritySnapshot(client, retCode, rsp)
qot.getKL(req) → onReply_GetKL(client, retCode, rsp)
qot.sub(req) → onReply_Sub(client, retCode, rsp)
qot.getOrderBook(req) → onReply_GetOrderBook(client, retCode, rsp)
qot.stockFilter(req) → onReply_StockFilter(client, retCode, rsp)
trd.unlockTrade(req) → onReply_UnlockTrade(client, retCode, rsp)
trd.placeOrder(req) → onReply_PlaceOrder(client, retCode, rsp)
trd.getAccList(req) → onReply_GetAccList(client, retCode, rsp)
trd.getFunds(req) → onReply_GetFunds(client, retCode, rsp)
initConnect() → [connecting] → onInitConnect(errCode=0) → [connected]
→ onDisconnect() → [disconnected]
src/main/java/com/futu/sdk/examples/
├── Config.java # .env loading, RSA key PKCS#8→PKCS#1 conversion
├── Example00_ConnectHA.java # HA TCP probe + getGlobalState (RSA auth)
├── Example01_MarketSnapshot.java # getSecuritySnapshot (no subscription needed)
├── Example02_QuotePush.java # sub() + onPush_UpdateBasicQuote, etc.
├── Example03_StockFilter.java # stockFilter screener
├── Example04_MacdStrategy.java # MACD trend strategy + getKL + onPush_UpdateKL
├── Example05_QuoteTrade.java # Dual qot+trd connections, SIMULATE order
├── Example06_StockSell.java # Sell order — position query + sell in SIMULATE
├── Example07_Kline.java # getKL + requestHistoryKL with pagination
├── Example08_RtTicker.java # getTicker + getRT
├── Example09_BrokerQueue.java # getBroker — broker bid/ask wall
├── Example10_OrderBook.java # getOrderBook depth (10-level + 50-level)
├── Example11_AccInfo.java # getAccList + getFunds + getPositionList
├── Example12_TradingDays.java # requestTradeDate
├── Example13_Plate.java # getPlateSet / getPlateSecurity
├── Example14_CurKline.java # getKL + onPush_UpdateKL live push
├── Example15_SubList.java # getSubInfo, sub/unsub
├── Example16_StockQuote.java # getBasicQot (requires subscription)
├── Example17_OwnerPlate.java # getOwnerPlate / getReference
├── Example18_ReferenceStock.java # getReference
├── Example19_CapitalFlow.java # getCapitalFlow / getCapitalDistribution
├── Example20_IpoList.java # getIpoList
├── Example21_FutureInfo.java # getFutureInfo
├── Example22_MarketState.java # getMarketState
├── Example23_PriceReminder.java # setPriceReminder / getPriceReminder
├── Example24_UserSecurity.java # getUserSecurity / modifyUserSecurity
├── Example25_OptionChain.java # getOptionExpirationDate + getOptionChain
├── Example26_HistoryKLQuota.java # requestHistoryKL quota tracking
├── Example27_CodeChange.java # getCodeChange — code/name changes
├── Example28_Warrant.java # getWarrant
├── Example30_UserInfo.java # getAccList + subAccPush
├── Example31_Misc.java # getHoldingChangeList, requestRehab, user security group
├── Example32_OrderQuery.java # unlockTrade + getOrderList + getOrderFillList
├── Example33_TradingInfo.java # getMaxTrdQtys, margin requirements
├── Example34_CancelAll.java # getOrderList + modifyOrder batch cancel
├── Example35_CashFlow.java # getCashFlow
├── Example36_StockBasicInfo.java # getSecurityStaticInfo
├── Example37_MarginRatio.java # getMarginRatio
├── Example38_OrderFee.java # getOrderFee
├── Example39_SysNotify.java # subSysNotify push
├── Example40_TradePush.java # onPush_UpdateOrder / onPush_UpdateFill
├── Example41_Rehab.java # requestRehab
├── Example42_CapitalDistribution.java # getCapitalDistribution
├── Example43_SubscribeLifecycle.java # subscription lifecycle demo
├── Example44_MultiMarketSnapshot.java # getSecuritySnapshot multi-market
├── Example45_StockFilter.java # stockFilter extended criteria
├── Example46_PlateStockFilter.java # getPlateSecurity + stockFilter
├── Example47_WarrantFilter.java # getWarrant + filter screener
├── Example48_OptionsStrategy.java # Options combo strategies
├── Example49_AccCashFlow.java # getCashFlow per-account
├── Example50_HistoryOrderDeal.java # getOrderList / getOrderFillList historical
├── Example51_AccList.java # getAccList with securities firm info
├── Example52_OptionChainFilter.java # getOptionChain + stockFilter
├── Example53_MarketHeat.java # getSecuritySnapshot + getCapitalFlow
├── Example54_OptionAnalytics.java # getOptionVolatility, getOptionExerciseProbability
├── Example55_EMA.java # EMA crossover detection
├── Example56_ETFComposition.java # getOwnerPlate + getPlateSecurity
├── Example57_VWAPBenchmark.java # VWAP benchmark from K-line
├── Example58_FinancialStatements.java # 4 financial statement APIs
├── Example59_ResearchRatings.java # getResearchAnalystConsensus, getResearchRatingSummary
├── Example60_CompanyFundamentals.java # getCompanyProfile, getCompanyExecutives, getCompanyOperationalEfficiency
├── Example61_ShareholdersInsiders.java # 5 shareholder/insider APIs
├── Example62_CorporateActions.java # dividends, buybacks, stock splits
├── Example63_ShortVolumeInterest.java # getDailyShortVolume, getShortInterest
├── Example64_ValuationScreener.java # getValuationDetail, getValuationPlateStockList
├── Example65_TopTenBrokers.java # getTopTenBuySellBrokers
├── Example66_StockScreen.java # getStockScreen — multi-criteria screener (10.7+)
├── Example67_OptionScreen.java # getOptionScreen — option screener (10.7+)
├── Example68_WarrantScreen.java # getWarrantScreen — warrant screener (10.7+)
├── Example69_UnusualActivity.java # getTechnical/Financial/Derivative Unusual (10.7+)
└── Helpers.java # Shared utilities (sleep, md5, formatting, labels)
| Project | Language | Description |
|---|---|---|
| futu-python-samples | Python | Original reference implementation |
| futuapi4go | Go | Go port of the same SDK, reference for RSA implementation |