Futu Securities
Trades Hong Kong, US and China A-share (Stock Connect) securities and futures through Futu's FutuOpenD gateway, live or paper. A FutuOpenD reachable from the docker must be running (the server address in the exchange configuration, 127.0.0.1:11111 on the local machine by default). The Futu ID logs in inside FutuOpenD; the exchange configuration only holds the FutuOpenD address and the Futu trade password. RSA encryption must be turned off in FutuOpenD (the docker talks to it over the plain-text protocol). For configuring the exchange object and running FutuOpenD, see the Futu Securities configuration guide.
Names and Configuration
exchange.GetName(): Futures_Futu (Securities and futures); aliases: Futu.
| Field | Required | Default | Description |
|---|---|---|---|
| Server Address | Yes | 127.0.0.1:11111 | IP and port FutuOpenD listens on, for example 127.0.0.1:11111. |
| Trade Password | Yes | — | Futu trade password, used to unlock trading and read account information. |
| Simulate | No | Unchecked | — |
Trading Pairs and Contract Codes
Pair examples: 00700.HK, AAPL.US, 600519.SH
Instrument codes are written code.market and set with exchange.SetContractType() (or exchange.SetCurrency()); the whole code becomes the current instrument. Market suffixes: HK Hong Kong stocks, US US stocks, SH Shanghai, SZ Shenzhen, SG Singapore futures, JP Japan futures. Queries, cancels and amendments also accept the legacy form HK.00700. exchange.GetMarkets() lists only the instruments this strategy has already set.
Exchange-specific IO Commands
| Command | Arguments | Description | Returns |
|---|---|---|---|
refresh | bool | Whether trade queries (GetOrder, GetOrders, GetPositions, GetAccount, GetAssets) refresh the OpenD cache; refreshes by default, pass false to use the cache, and nothing changes when no argument is given. (more below the table) | — |
accounts | — | Get the full list of trading accounts returned by Futu (raw data, including accounts that do not match the live/paper setting and are not used). | — |
lock | — | Lock trading. | Null on success. |
unlock | — | Unlock trading with the configured trade password. Trade requests also unlock once automatically and retry when Futu asks for unlocking, so a manual call is usually unnecessary. | Null on success. |
instrument | symbol | Get the static information of an instrument, for example exchange.IO("instrument", "00700.HK"); instruments queried this way appear in exchange.GetMarkets(). | {instrument, InstrumentID, InstrumentName, VolumeMultiple, IsTrading, SecType, info, ...}; VolumeMultiple is the lot size (the contract multiplier for options and futures) and info is Futu's raw static information (a JSON string). |
timeout | number | Set the timeout for communication with OpenD (milliseconds); 20000 by default, clamped to 100–600000. | {"ok": true, "timeout": effective milliseconds} |
statusDetail | — | Get connection status details. The connection to FutuOpenD is made on the first request, so connected is false before it. | {"connected":..., "server":..., "simulate":...} |
historyKlines | object | Get historical K-lines over a time range (Futu's historical K-line interface, which has a quota); parameter {symbol, interval, begin, end, limit}: symbol is required (the current instrument is not filled in); interval is in seconds and must be one of the native periods of exchange.GetRecords(); begin and end are dates in YYYY-MM-DD form, defaulting to the last 7 days plus interval × limit up to tomorrow; limit is the page size (at most 1000, 1000 by default), and pages are fetched until the whole range is covered. (more below the table) | An array of K-lines, each {time, open, high, low, close, volume} (lower-case fields, time in milliseconds). |
status | — | Always returns true (legacy compatibility); use statusDetail for the connection status. | — |
wait | [number] | Legacy compatibility probe: the Futu connector has no market data or order push, so it does not wait and always returns true. | — |
The rest of the longer descriptions in the table:
refresh: Queries that refresh the cache are rate-limited by Futu (at most 10 per 30 seconds).historyKlines: K-lines are forward-adjusted.
For the common commands base and currency see exchange.IO.
The common command api is not supported (the call fails with an error and returns null).
Common Usage
List all trading accounts returned by Futu and unlock or lock trading by hand
javascript
function main() {
// Raw account rows: trdEnv 0 paper, 1 live; accStatus 0 active
exchange.IO("accounts").forEach(function (a) {
Log("Account:", a.accID, "trdEnv:", a.trdEnv, "accType:", a.accType,
"accStatus:", a.accStatus, "Markets:", a.trdMarketAuthList)
})
// Trade requests unlock once automatically when Futu asks for it, so this is
// rarely needed; null is returned on success and on failure alike
exchange.IO("unlock")
var err = GetLastError()
Log(err ? "Unlock failed: " + err : "Unlocked")
// ... trading ...
exchange.IO("lock")
}
Get historical K-lines over a date range
javascript
function main() {
// symbol is required; interval is in seconds and must be a native period;
// ranges longer than limit bars are fetched page by page
var bars = exchange.IO("historyKlines", {
symbol: "00700.HK", interval: 86400, begin: "2026-01-01", end: "2026-06-30"
})
if (!bars) {
Log("Failed:", GetLastError())
return
}
Log(bars.length, "bars")
bars.slice(-3).forEach(function (b) {
Log(_D(b.time), "O:", b.open, "H:", b.high, "L:", b.low, "C:", b.close, "V:", b.volume)
})
}
Query an instrument's static information (lot size, security type)
javascript
function main() {
var r = exchange.IO("instrument", "00700.HK")
Log(r.InstrumentName, "Lot size:", r.VolumeMultiple, "SecType:", r.SecType,
"Not delisted:", r.IsTrading)
var basic = JSON.parse(r.info) // Futu's raw static information
Log("Listed on:", basic.listTime)
// Instruments queried this way appear in GetMarkets
Log(Object.keys(exchange.GetMarkets()))
}
Check the FutuOpenD connection, adjust the timeout and read the cache when polling often
javascript
function main() {
// status and wait are legacy probes that always return true; they say nothing
// about the connection
Log(exchange.IO("status"), exchange.IO("wait"))
exchange.SetContractType("00700.HK") // queries FutuOpenD, which connects
var d = exchange.IO("statusDetail")
Log("Connected:", d.connected, "FutuOpenD:", d.server, "Paper:", d.simulate)
Log("Timeout (ms):", exchange.IO("timeout", 60000).timeout)
// Trade queries read FutuOpenD's cache, free of the 10-per-30-seconds refresh limit
exchange.IO("refresh", false)
while (true) {
var pos = exchange.GetPositions()
Log("Positions:", pos ? pos.length : GetLastError())
Sleep(1000)
}
}
Function Support
Not supported (the call fails with an error and returns null): exchange.GetFundings, exchange.SetMarginLevel, exchange.CreateConditionOrder, exchange.ModifyConditionOrder, exchange.CancelConditionOrder.
Served through a fallback:
exchange.GetRecords: Native periods are 1m, 3m, 5m, 15m, 30m, 1h, 1d, 1w, 1M; other periods are synthesized by the docker.exchange.GetConditionOrder: There is no single condition-order query, so the order is looked up among the instrument's open condition orders and its condition orders of the last 90 days (at most 100). Condition orders are native Futu orders placed through the order functions withorderType(10 to 15) in the options object; see the notes.
Margin, Account and Simulation
Account: Futu uses TrdMarket to tell markets apart (1 Hong Kong, 2 US, 3 mainland, 4 Hong Kong Stock Connect, 5 futures). Balance, FrozenBalance, Equity and UPnL of exchange.GetAccount() are in the currency of the current instrument's market (HKD for 00700.HK; the Hong Kong market when no instrument with a market suffix is set), and Stocks and FrozenStocks are 0; Info contains accounts (the trading accounts) and funds (each market's trdMarket, currency and fund details). exchange.GetAssets() sums the funds of all authorized markets per currency. Live trading uses the margin account and paper trading the cash account, and only active accounts with market permissions are used; when no account matches, an error listing the available accounts is reported.
Simulation: The Simulate box connects to Futu paper trading. When FutuOpenD is used for paper trading, some stock codes are not supported and cannot be traded (paper trading works in the Futu NiuNiu mobile app).
Notes
exchange.IO("api", ...)is not supported.- Trading functions such as
exchange.SetDirection(),exchange.Buy()/exchange.Sell(),exchange.CancelOrder(),exchange.ModifyOrder()andexchange.GetOrder()are used the same way as in futures markets. - Futu orders have only two sides, buy and sell, with no open/close flag:
closebuy(close long) is sent as a sell andclosesell(close short) as a buy, for stocks and futures alike. So that a closing order never opens a position in the opposite direction, the docker queries the instrument's positions before submitting it: if there is no position in the matching direction, the closable quantity (held minus frozen) is less than the order quantity, or the position query fails, no order is placed, an error is reported and null is returned. Futures positions are recorded per specific contract month, so closing through a continuous (main) contract code is rejected because no position is found. Long and short positions cannot be held in the same instrument at once. Queried orders do not distinguish opening from closing, andOffsetis reported as open (except orders Futu reports as "buy back"). - Trade queries refresh from the Futu server on every call by default and are subject to Futu's rate limit (at most 10 per 30 seconds), returning an error beyond it; for frequent polling use
exchange.IO("refresh", false)to readFutuOpenD's cache, whichFutuOpenDupdates automatically when new data arrives. FutuOpenDdecides the region by the IP address it logs in from; accounts logged in from outside mainland China have some market data restrictions. See Futu's official documentation.- When the trading pair configured on the exchange object is not in
code.marketform (for example00700),exchange.GetCurrency()returns the configured value andexchange.GetContractType()returns an empty string, meaning no instrument is set yet. Callexchange.SetContractType("00700.HK")first; otherwise market data and trading functions called without an instrument report an error and return null. exchange.GetDepth()subscribes to Futu's order book and returns up to 10 levels; the subscription counts against Futu's subscription quota. Without order book permission, with the quota used up, or when the book has no levels (market closed, trading halted), it falls back to the best bid and ask from the snapshot;Infothen hassourceset tosnapshotandorderBookErrorgiving the reason.- Order quantities are numbers of shares (contracts for futures and options), not amounts of money, market buy orders included; a price of
-1or0submits a Futu market order. Limit orders default toGTC(good till canceled) in live trading and to day orders in paper trading, and market orders are day orders;ioc,fokandpostOnlyare not supported. Orders may fill in all trading sessions (pre-market and after-hours included) by default. - Futu-specific order parameters are passed in the options object at the end of the order functions and handed to Futu as is:
orderType(aTrd_Common.OrderTypevalue: 10 stop market, 11 stop limit, 12 market if touched, 13 limit if touched, 14 trailing stop market, 15 trailing stop limit),auxPrice(trigger price),trailType,trailValue,trailSpread,timeInForce(0 day, 1 good till canceled),session,fillOutsideRTH,adjustPrice,adjustSideAndLimit,remarkandtrdSide(a raw side value; when given, the position check before closing is skipped). Condition orders placed withorderTypeshare one order list with ordinary orders: bothexchange.GetOrders()andexchange.GetConditionOrders()list them, andexchange.CancelOrder()cancels them. exchange.ModifyOrder()changes the price and quantity of a limit order in place and keeps the order ID; when only one of them is changed, the other is taken from the original order; market orders cannot be modified.- Without an instrument,
exchange.GetOrders()andexchange.GetPositions()return all open orders and positions of every authorized market.exchange.GetHistoryOrders()returns only finished orders; without an instrument it covers all instruments, without a start time it covers the last 90 days, results are in ascending time order, and a given count keeps the most recent ones. - Without arguments,
exchange.GetTickers()returns the tickers of the instruments this strategy has set or queried.exchange.GetRecords()returns at most 1000 bars, forward-adjusted;exchange.GetTrades()returns 100 trades by default and at most 1000. - In
exchange.GetMarkets(),CtValis 1 for stocks (the number of shares per lot islotSizeinInfo), and for options and futuresCtValis the contract multiplier.TickSizeis the minimum price change at the current price level (Hong Kong stocks have price-tiered tick sizes that change with the price).