Skip to main content

Quick Start

Key takeaways
  • Fubon Futures Market Data WebSocket API provides real-time futures subscriptions.
  • Supports Speed and Normal modes.
  • Login is required before subscribing to channels.
ItemDetails
InterfaceWebSocket API
MarketTaiwan futures / options
ModesSpeed / Normal
SDKPython / Node.js / C#
SubscriptionConnect then subscribe to channels

Fubon Futures Market Data WebSocket API provides real-time futures market data for Taiwan.

Using SDK​

Fubon Futures Market Data WebSocket API provides Python, Node.js, and C# SDKs. You can access the WebSocket API through the following methods:

And subscribe webSocket callback method to receive the callback messages below.

info

WebSocket API provides the low-latency Speed mode and the multi-information Normal mode.

FutOpt streaming version

The futures and options market data WebSocket offers two streaming versions, v1.0 and v1.1. When no version is specified, the connection resolves to v1.1. Trial-matching messages (isTrial) on the trades and books channels are pushed only over v1.1; pin the connection to v1.0 and you will not receive them. The aggregates channel pushes updates during the trial-matching session on both versions. Stock market data is served on v1.0 only. Available since v2.2.9.

from fubon_neo.sdk import FubonSDK, Mode

def handle_message(message):
print(message)

sdk = FubonSDK()
accounts = sdk.login("Your ID", "Your password", "Your cert path", "Your cert password")

sdk.init_realtime()
# Offer MarketData Mode , Default : Speed
# sdk.init_realtime(Mode.Speed) or sdk.init_realtime(Mode.Normal)
# Offer FutOpt streaming version , Default : v1.1 ; v1.0 does not deliver trial-matching messages on trades/books
# sdk.init_realtime(Mode.Speed, version={"futopt": "v1.0"})
# After connecting, sdk.marketdata.websocket_client.futopt.url reports the version actually resolved

futopt = sdk.marketdata.websocket_client.futopt
futopt.on('message', handle_message)
futopt.connect()

Authenticated​

When the verification is successful, you will receive the following message:

{
"event": "authenticated",
"data": {
"message": "Authenticated successfully"
}
}

When the verification is failed, you will receive the following message:

{
"event": "error",
"data": {
"message": "Invalid authentication credentials"
}
}

Heartbeat​

Every 30 seconds, the WebSocket server will send out a heartbeat message:

{
"event": "heartbeat",
"data": {
"time": "<Timestamp>"
}
}

Ping/Pong​

Every 30 seconds, the SDK will send the following JSON-formatted message to the WebSocket Server, or follow the below example (where state is optional):

futopt.ping('<ANY>')

The WebSocket Server will respond with the following message (if ping was sent, the state field will not be present):

{
"event": "pong",
"data": {
"time": "<TIMESTAMP>",
"state": "<ANY>"
}
}

Channels​

The Fubon Futures Market Data WebSocket API currently provides the following subscribable channels:

Futures/Options Market Data at a Glance​

The following market data features are available since v2.2.9:

  • Continuous-month quotes: Subscribe with a {ROOT}1!/2!/3! nearby-month alias; see Continuous Month Aliases below.
  • Spread-contract quotes: First retrieve spread contract symbols with isSpread=true from Intraday Tickers, then use the returned symbol in the WebSocket subscription; see Subscribing to Spread Contracts below.
  • Derived best-level quotes: derivedBid/derivedAsk in the books channel provide a derived best quote level, primarily for spread contracts.
  • Trial-matching information: isTrial in books and lastTrial in aggregates disclose information for the trial-matching phase.

Subscribe Channel​

Subscribe to a channel, send following example to the WebSocket Server:

futopt.subscribe({
"channel" : "<CHANNEL_NAME>",
"symbol" : "<SYMBOL_ID>"
#"afterHours" : True Subscribe after-hours
})

After a successful subscription, you will receive the following event response:

{
"event": "subscribed",
"data": {
"id": "<CHANNEL_ID>",
"channel": "<CHANNEL_NAME>",
"symbol": "<SYMBOL_ID>"
}
}

Supports the subscription of multiple stocks within the same channel:

futopt.subscribe({
"channel" : "<CHANNEL_NAME>",
"symbols" : ["<SYMBOL_ID>","<SYMBOL_ID>"]
#"afterHours" : True Subscribe after-hours
})

After a successful subscription, you will receive the following event response:

{
"event": "subscribed",
"data": [
{
"id": "<CHANNEL_ID>",
"channel": "<CHANNEL_NAME>",
"symbol": "<SYMBOL_ID_1>"
},
{
"id": "<CHANNEL_ID>",
"channel": "<CHANNEL_NAME>",
"symbol": "<SYMBOL_ID_2>"
}
]
}

Continuous Month Aliases​

In addition to a concrete contract symbol (e.g. TXFG6), the symbol used when subscribing can also be a continuous month alias: {ROOT}1!/2!/3!, which subscribes to the 1st/2nd/3rd nearby month contract (e.g. TXF1! for the nearest-month Taiwan stock index future). The alias is resolved to the matching concrete contract once, when the subscription is sent; the symbol in data events is the resolved concrete contract symbol. The subscription stays bound to that contract afterwards and does not roll over after settlement — on a long-lived connection, resubscribe after the roll (unsubscribe then subscribe) or subscribe with concrete contract symbols instead. The Web API is unaffected: every request resolves the alias again. See Web API — Contract Symbols and Continuous-Month Aliases.

futopt.subscribe({
"channel": "trades",
"symbol": "TXF1!" # Continuous month alias, subscribes to the nearest-month Taiwan stock index future
#"afterHours" : True Subscribe after-hours
})

Subscribing to Spread Contracts​

Calendar spread contracts (e.g. TXFJ6/K6) are subscribed with the same symbol field; pass the symbol as-is and the connection will start receiving that spread contract's data once subscribed. See Spread Contracts for the symbol format.

futopt.subscribe({
"channel": "books",
"symbol": "TXFJ6/K6"
})

Unsubscribe​

Unsubscribe from a subscribed channel, please send the following example to WebSocket Server:

futopt.unsubscribe({
'id':'<CHANNEL_ID>'
})

After a successful unsubscription, you will receive the following event response:

{
"event": "unsubscribed",
"data": {
"id": "<CHANNEL_ID>",
"channel" : "<CHANNEL_NAME>",
"symbol" : "<SYMBOL_ID>"
}
}

Supports the unsubscription from multiple channels:

futopt.unsubscribe({
'ids':['<CHANNEL_ID>','<CHANNEL_ID>']
})

After a successful unsubscription, you will receive the following event response:

{
"event": "unsubscribed",
"data": [
{
"id": "<CHANNEL_ID_1>",
"channel" : "<CHANNEL_NAME>",
"symbol" : "<SYMBOL_ID>"
},
{
"id": "<CHANNEL_ID_2>",
"channel" : "<CHANNEL_NAME>",
"symbol" : "<SYMBOL_ID>"
}
]
}

Subscriptions​

To list the channels currently subscribed on this connection, use the following example:

futopt.subscriptions()

You will receive the following event response:

{
"event": "subscriptions",
"data": [
{
"id": "<CHANNEL_ID>",
"channel": "<CHANNEL_NAME>",
"symbol": "<SYMBOL_ID>"
}
]
}

Error Handle​

When there is an anomaly in the WebSocket callback you subscribed to or processed, you can handle error messages as follows.

The disconnect event fires in two cases: the server closes the connection, or the connection health check times out — when no message of any kind has arrived from the server for roughly 60 seconds, the SDK closes the connection itself and emits this event. How a timeout is reported in each language is noted under its example.

def handle_connect():
print('market data connected')


def handle_disconnect(code, message, *reason): # a health-check-timeout disconnect passes one extra argument
print(f'market data disconnect: {code}, {message}')


def handle_error(error):
print(f'market data error: {error}')

futopt.on("connect", handle_connect)
futopt.on("disconnect", handle_disconnect)
futopt.on("error", handle_error)

On a health-check timeout, disconnect passes one extra argument {'reason': 'health-check-timeout'}; *reason in the handler catches it.

Reconnection​

After a disconnection the SDK neither reconnects on its own nor keeps your subscriptions; your program has to reconnect when it receives the disconnect event and send every subscription again. The following example continues from the login and connection code above: on a disconnect event it reconnects and resubscribes in the background, with a growing delay between attempts (2, 4, 8, 16, 30 seconds) and a limit of 5 attempts, so that a network that has not recovered yet is not flooded with reconnects. Keep one entry per channel in the subscription list and subscribe to several symbols at once with symbols, so the number of subscribe calls after a reconnect equals the number of channels rather than the number of symbols.

# continue after futopt = sdk.marketdata.websocket_client.futopt; replaces the original futopt.connect()
import threading
import time

SUBSCRIPTIONS = [ # one list shared by the first subscription and every resubscription; one entry per channel, several symbols via symbols
{'channel': 'trades', 'symbols': ['TXF1!', 'MXF1!'], 'afterHours': True},
]
reconnect_needed = threading.Event()

def handle_disconnect(code, message, *reason): # a health-check-timeout disconnect passes one extra argument; *reason catches it
print(f'market data disconnect: {code}, {message}')
reconnect_needed.set() # notify only; do not reconnect here

def subscribe_all():
for params in SUBSCRIPTIONS:
futopt.subscribe(params)

def connect_with_timeout(seconds=10):
# connect() never returns while the network is down; run it in a thread with a timeout
result = {}
def run():
try:
futopt.connect()
except Exception as error:
result['error'] = error
worker = threading.Thread(target=run, daemon=True)
worker.start()
worker.join(seconds)
if worker.is_alive():
raise TimeoutError('connect timeout')
if 'error' in result:
raise result['error']

def reconnect_loop():
retries = 0
while reconnect_needed.wait():
reconnect_needed.clear()
retries += 1
if retries > 5:
print('Reconnect failed 5 times, please check the network')
return
time.sleep(min(2 ** retries, 30)) # wait 2, 4, 8, 16, 30 seconds before retrying
try:
futopt.disconnect() # reset the connection state first, otherwise connect() will not reconnect
connect_with_timeout()
subscribe_all()
retries = 0
print('Reconnected and resubscribed')
except Exception as error:
print(f'Reconnect failed: {error}')
reconnect_needed.set()

futopt.on('disconnect', handle_disconnect)
threading.Thread(target=reconnect_loop, daemon=True).start()

futopt.connect()
subscribe_all()