Skip to main content

Historical Candles

Query historical futures candles by product symbol. Supports day / week / month K and minute K, regular and after-hours sessions; the contract month can be a specific month or a continuous contract 1! / 2! / 3!. A single query range must be shorter than 1 year; day / week / month K goes back to 2010, and minute K is available from 2017-06.

historical/candles/{product}
Version Note

Available since v2.4.0

Parameters​

NameTypeDescription
product*stringFutures product symbol, e.g. TXF, MXF, CDF (upper case). Specify the contract month with contractMonth; weekly contracts are also queried by product symbol (e.g. MXF with contractMonth=202609W2) — a weekly prefix such as MX4 is not a product symbol. A contract code (e.g. TXFI6) is not accepted as product and returns 404; see the conversion note below. Only futures are supported for now; options and spread contracts are not yet available
contractMonthstringContract month, defaults to 1! (front month) if omitted. Accepts a specific month yyyyMM (e.g. 202609), a weekly contract (e.g. 202609W2), or a continuous contract: 1! front month, 2! second month, 3! third month (4! and beyond return 400)
fromstringStart date (format: yyyy-MM-dd), defaults to 1 week ago if omitted
tostringEnd date (format: yyyy-MM-dd), defaults to today if omitted
timeframestringKLine timeframe, defaults to D (day) if omitted; offer 1 1m; 5 5m; 10 10m; 15 15m; 30 30m; 60 60m; D day; W week; M month
sessionstringTrading session, defaults to regular if omitted; offer afterhours for the after-hours session
fieldsstringField selection, defaults to open,high,low,close,volume if omitted (average, transaction and change are not included, specify them explicitly when needed); offer open, high, low, close, volume, average, transaction, change (see the Response table below for applicable timeframes). turnover is not provided and returns 400 if requested
sortstringSorting, defaults to desc (descending) if omitted; also offer asc (ascending)
info
  • Day / week / month K goes back to 2010.
  • Minute K (regular and after-hours sessions) is available from 2017-06 (the after-hours session starts at 15:00 and belongs to the next trading day).
  • Minute K is built from outright-order trades only; the two legs filled through spread (combination) orders and block trades are not counted, so a contract's daily K volume can exceed the sum of that day's minute K, and a day with daily K volume but no minute K is expected.
  • A single query range (from ~ to) must be shorter than 1 year. A range of exactly or more than 1 year returns 400 Bad Request (error message: Date range must be less than one year). from must not be later than to, otherwise 400 is returned as well.
  • from / to are trading days. Per TAIFEX rules an after-hours session belongs to the next trading day, e.g. session=afterhours&from=2026-09-10&to=2026-09-10 returns the after-hours session from 2026-09-09 15:00 to 2026-09-10 05:00.
  • Continuous contracts (1! / 2! / 3!) roll over on contract expiry: on the last trading day the alias still points to the expiring contract and switches to the next one from the following trading day; data.contractMonth on each candle shows the contract it actually belongs to. No roll adjustment is applied, so the candle on the rollover day may gap. Continuous contracts are ranked among monthly contracts only, excluding weekly contracts.
  • If no data exists in the query range, the API returns 404 Resource Not Found instead of an empty array (e.g. the range contains no trading days, falls entirely before the start date of that timeframe, the product symbol or contract month does not exist, or an options symbol is queried).
  • If you have an intraday contract code (e.g. TXFI6), split it into product symbol and contract month: the first 3 characters are the product symbol (TXF), the 4th is the month code A–L for January–December (I = September), and the 5th is the last digit of the year (6 = 2026), i.e. product=TXF with contractMonth=202609; the continuous alias TXF1! maps to product=TXF with contractMonth=1!.

Response​

NameTypeDescriptionApplicable Timeframe
product*stringProduct symbol—
contractMonth*stringThe contract month passed in the query (1! / 2! / 3! for continuous contracts)—
exchange*stringExchange—
session*stringSession, REGULAR or AFTERHOURS—
timeframe*stringKLine timeframe—
sort*stringSorting direction (desc or asc)—
data*object[]KLine data—
data.date*stringTrading day for day K, Monday of the week for week K, first day of the month for month K (yyyy-MM-dd); start time of the candle for minute K, ISO 8601 with timezone (e.g. 2026-09-10T08:45:00.000+08:00)All
data.contractMonth*stringThe contract month this candle actually belongs to. Use it to see where the rollover happens in a continuous-contract query; a week / month K spanning a rollover is labelled with the contract it closed onAll
data.opennumberOpening PriceAll
data.highnumberHighest PriceAll
data.lownumberLowest PriceAll
data.closenumberClose PriceAll
data.volumenumberVolume (contracts)All
data.averagenumberVolume-weighted average price (VWAP) of the candleMinute K only
data.transactionnumberNumber of transactions in the candleMinute K only
data.changenumberPrice ChangeDay / Week / Month K only
info

How change is calculated:

  • Day K: the difference from the previous trading day's close of the same contract. In a continuous-contract query, change on the rollover day is calculated against the new contract's own previous close, not against the previous contract.
  • Week / Month K: the sum of the daily changes within the period.

Examples​

Day K Example (Continuous Contract)​

Query day K with the front-month continuous contract 1! and a specified date range. The range spans the expiry of the 2026 August contract (8/19), and contractMonth shows the rollover on 8/20:

from fubon_neo.sdk import FubonSDK, Order

sdk = FubonSDK()

accounts = sdk.login("Your ID", "Your password", "Your cert path", "Your cert password") # Login first before connecting market-data

sdk.init_realtime() # Establish market-data

restfutopt = sdk.marketdata.rest_client.futopt

## After version 2.2.4 (use Exception for exception handling)
from fubon_neo.fugle_marketdata.rest.base_rest import FugleAPIError

try:
restfutopt.historical.candles(**{"product": "TXF", "contractMonth": "1!", "from": "2026-08-17", "to": "2026-08-21", "fields": "open,high,low,close,volume,change"})
except FugleAPIError as e:
print(f"Error: {e}")
print("------------")
print(f"Status Code: {e.status_code}") # Ex: 429
print(f"Response Text: {e.response_text}") # Ex: {"statusCode":429,"message":"Rate limit exceeded"}

Response Body:

{
"product": "TXF",
"contractMonth": "1!",
"exchange": "TAIFEX",
"session": "REGULAR",
"timeframe": "D",
"sort": "desc",
"data": [
{
"date": "2026-08-21",
"contractMonth": "202609",
"open": 44887,
"high": 45294,
"low": 44566,
"close": 45148,
"volume": 51446,
"change": 245
},
{
"date": "2026-08-20",
"contractMonth": "202609",
"open": 44950,
"high": 45122,
"low": 44448,
"close": 44868,
"volume": 53693,
"change": 368
},
{
"date": "2026-08-19",
"contractMonth": "202608",
"open": 44411,
"high": 44869,
"low": 44208,
"close": 44612,
"volume": 27795,
"change": -476
},
{
"date": "2026-08-18",
"contractMonth": "202608",
"open": 45967,
"high": 45977,
"low": 45070,
"close": 45085,
"volume": 83392,
"change": -782
},
{
"date": "2026-08-17",
"contractMonth": "202608",
"open": 45850,
"high": 46140,
"low": 45738,
"close": 45888,
"volume": 79450,
"change": 76
}
]
}

Minute K Example​

Query 60-minute K with a specific contract month and a specified date range (including average price average and number of transactions transaction):

from fubon_neo.sdk import FubonSDK, Order

sdk = FubonSDK()

accounts = sdk.login("Your ID", "Your password", "Your cert path", "Your cert password") # Login first before connecting market-data

sdk.init_realtime() # Establish market-data

restfutopt = sdk.marketdata.rest_client.futopt

## After version 2.2.4 (use Exception for exception handling)
from fubon_neo.fugle_marketdata.rest.base_rest import FugleAPIError

try:
restfutopt.historical.candles(**{"product": "TXF", "contractMonth": "202609", "timeframe": "60", "from": "2026-09-10", "to": "2026-09-10", "fields": "open,high,low,close,volume,average,transaction"})
except FugleAPIError as e:
print(f"Error: {e}")
print("------------")
print(f"Status Code: {e.status_code}") # Ex: 429
print(f"Response Text: {e.response_text}") # Ex: {"statusCode":429,"message":"Rate limit exceeded"}

Response Body:

{
"product": "TXF",
"contractMonth": "202609",
"exchange": "TAIFEX",
"session": "REGULAR",
"timeframe": "60",
"sort": "desc",
"data": [
{
"date": "2026-09-10T12:45:00.000+08:00",
"contractMonth": "202609",
"open": 46733,
"high": 46949,
"low": 46733,
"close": 46870,
"volume": 10066,
"average": 46868.04,
"transaction": 7729
},
{
"date": "2026-09-10T11:45:00.000+08:00",
"contractMonth": "202609",
"open": 46673,
"high": 46820,
"low": 46666,
"close": 46730,
"volume": 4133,
"average": 46745.72,
"transaction": 3212
},
{
"date": "2026-09-10T10:45:00.000+08:00",
"contractMonth": "202609",
"open": 46748,
"high": 46803,
"low": 46652,
"close": 46677,
"volume": 3896,
"average": 46719.47,
"transaction": 3192
},
{
"date": "2026-09-10T09:45:00.000+08:00",
"contractMonth": "202609",
"open": 46661,
"high": 46800,
"low": 46589,
"close": 46747,
"volume": 7357,
"average": 46688.19,
"transaction": 5398
},
{
"date": "2026-09-10T08:45:00.000+08:00",
"contractMonth": "202609",
"open": 46940,
"high": 46950,
"low": 46506,
"close": 46663,
"volume": 20094,
"average": 46683.01,
"transaction": 16071
}
]
}

After-hours Example​

Query 60-minute K of the after-hours session with session=afterhours. from / to take the trading day the session belongs to (2026-09-10); the response covers the after-hours session from 2026-09-09 15:00 to 2026-09-10 05:00:

from fubon_neo.sdk import FubonSDK, Order

sdk = FubonSDK()

accounts = sdk.login("Your ID", "Your password", "Your cert path", "Your cert password") # Login first before connecting market-data

sdk.init_realtime() # Establish market-data

restfutopt = sdk.marketdata.rest_client.futopt

## After version 2.2.4 (use Exception for exception handling)
from fubon_neo.fugle_marketdata.rest.base_rest import FugleAPIError

try:
restfutopt.historical.candles(**{"product": "TXF", "contractMonth": "202609", "timeframe": "60", "session": "afterhours", "from": "2026-09-10", "to": "2026-09-10"})
except FugleAPIError as e:
print(f"Error: {e}")
print("------------")
print(f"Status Code: {e.status_code}") # Ex: 429
print(f"Response Text: {e.response_text}") # Ex: {"statusCode":429,"message":"Rate limit exceeded"}

Response Body (excerpt):

{
"product": "TXF",
"contractMonth": "202609",
"exchange": "TAIFEX",
"session": "AFTERHOURS",
"timeframe": "60",
"sort": "desc",
"data": [
{
"date": "2026-09-10T04:00:00.000+08:00",
"contractMonth": "202609",
"open": 46987,
"high": 47019,
"low": 46971,
"close": 46984,
"volume": 359
},
{
"date": "2026-09-10T03:00:00.000+08:00",
"contractMonth": "202609",
"open": 47024,
"high": 47086,
"low": 46965,
"close": 46992,
"volume": 671
},
...
{
"date": "2026-09-09T16:00:00.000+08:00",
"contractMonth": "202609",
"open": 47234,
"high": 47295,
"low": 47182,
"close": 47233,
"volume": 1608
},
{
"date": "2026-09-09T15:00:00.000+08:00",
"contractMonth": "202609",
"open": 47214,
"high": 47267,
"low": 47136,
"close": 47232,
"volume": 2098
}
]
}
info

'*' Indicates mandatory disclosure fields.