Skip to content

Upgrading to 1.7.0

1.7.0 rewrites how contracts work, bringing a new api.contracts query API and keeping contracts up to date automatically. Your existing code runs essentially without changes, and you can move to the new API at your own pace.

Highlights

1.7.0's changes center on contracts, in three main directions:

  • Contracts stay up to date automatically: no more managing update timing yourself, and login is faster — login no longer downloads the full contract set; each type is loaded on first query instead.
  • New api.contracts query: a single entry point to query every product, with the ability to look up derivatives by underlying and filter by criteria; contract details are also richer.
  • Stronger index support: indices now use their exchange codes (e.g. the TAIEX is IX0001), and you can subscribe to real-time index quotes.

Most of these changes don't affect your existing code; the few that need adjusting are collected in How to migrate to 1.7? below.

What's new?

1. New api.contracts query

In 1.5, we used api.Contracts (capital C) to query products — you had to know which security type a product belonged to, then access it level by level through the matching category, e.g. api.Contracts.Stocks["2330"] for stocks or api.Contracts.Futures.TXF for futures.

In 1.7.0, we added the api.contracts (lowercase) query entry point: a single get() method looks up any product without distinguishing categories, plus functions the old style didn't have — futures_by_underlying() to look up derivatives by underlying, options() to filter by criteria, and more.

Capability api.Contracts (old) api.contracts (new)
Unified lookup (no categories) Access by category, e.g. api.Contracts.Stocks["2330"] api.contracts.get("2330") for any product
Look up derivatives by underlying Not supported api.contracts.futures_by_underlying(c) finds an underlying's futures
Filter by criteria Not supported api.contracts.options(root, ...) filters by strike, expiry
List all of a security type list(api.Contracts.Stocks) api.contracts.list(sj.SecurityType.Stock)

Consider moving to api.contracts

The old api.Contracts (capital C) style still works and both can coexist; however, it will no longer be maintained, so we recommend gradually moving to the new api.contracts (lowercase).

Contract and contract info objects

api.contracts splits the query result into two kinds of objects:

  • Contract object (Contract): contains only the fields needed to identify a product (security_type, region, exchange, code, target_code) — lightweight. Obtained via api.contracts.get(code). Placing orders and subscribing to quotes only need this object.
  • Contract info object (StockInfo, etc.): contains the full fields such as name, limit-up/down, and margin/short-selling balances. Obtained via api.contracts.info(contract). Use it only when you need the product's full details.
api.Contracts (old) api.contracts (new)
Get the contract object (for identity) — (returns the full object at once) api.contracts.get("2330")
Get the contract info object (full fields) api.Contracts.Stocks["2330"] api.contracts.info(contract)

Bandwidth savings

The old api.Contracts downloads the full data as one bundle; whereas api.contracts.get() only downloads the contract object's data, without fetching the full fields — those are fetched only when you call info(). If you only need to place orders or subscribe to quotes, get() alone is enough, with no need to carry the full data's bandwidth.

2. Real-time index quotes

1.7.0 lets you subscribe to real-time quotes for indices (e.g. the TAIEX IX0001). See Index Streaming.

How to migrate to 1.7?

The changes from 1.5.x to 1.7.0 that need your attention are listed below. Your existing usage — placing orders, subscribing to quotes, passing in contracts — is unchanged; the "Must change?" column marks only the items that actually require an edit. Click a change to jump to its explanation below.

Change 1.5.x 1.7.0 Must change?
Login arguments login()'s fetch_contractcontracts_timeoutcontracts_cb The three arguments removed Yes, if used
Contract download management api.fetch_contracts(), api.Contracts.status Removed; loaded automatically on query Yes, if you poll status
Contract objects api.Contracts.Stocks["2330"] returns StockFutureOptionIndex The same call now returns StockInfo, etc., with renamed/removed/retyped fields Yes, if you read changed fields
Index codes api.Contracts.Indexs.TSE["001"] api.Contracts.Indexs.TSE["IX0001"] Yes, if you use indices
SecurityType sj.SecurityType.Future (singular) sj.SecurityType.Futures (plural) Yes, if used
Listing contracts over HTTP POST + JSON body GET + query params Yes, HTTP only

1. Login arguments removed

login() no longer downloads contracts at login, so the fetch_contract, contracts_timeout, and contracts_cb arguments have been removed. Remove them from your login call.

api.login(
    api_key="YOUR_API_KEY",
    secret_key="YOUR_SECRET_KEY",
    fetch_contract=True,
    contracts_timeout=10000,
    contracts_cb=lambda st: print(f"{st} fetch done."),
)
api.login(
    api_key="YOUR_API_KEY",
    secret_key="YOUR_SECRET_KEY",
)

2. Contract download management removed

From 1.7.0, contracts are loaded automatically on query and kept up to date, so there's no need to download them at login. The 1.5 api.fetch_contracts() for manual download and api.Contracts.status for checking download status are no longer needed; if your code calls these or polls download status, remove them.

api.login(
    api_key="YOUR_API_KEY",
    secret_key="YOUR_SECRET_KEY",
    fetch_contract=False,
)
api.fetch_contracts(contract_download=True)   # manual download
api.login(
    api_key="YOUR_API_KEY",
    secret_key="YOUR_SECRET_KEY",
)
# No manual download; loaded automatically on query

3. Contract objects

Your old access such as api.Contracts.Stocks["2330"] and api.Contracts.Futures.TXF.TXFR1 still works, but the object returned has changed from the previous Stock, Future, Option, Index to StockInfo, FuturesInfo, OptionInfo, IndexInfo (with a new WarrantInfo). Each type's field changes and old-vs-new object comparison are below:

StockStockInfo, field changes:

1.5 1.7.0 How to adjust
symbol Removed Use code
update_date str '2026/07/17'datetime.date If you did string handling (e.g. .split("/")), use the date directly
Stock(
    exchange=<Exchange.TSE: 'TSE'>,
    code='2330',
    symbol='TSE2330',
    name='台積電',
    category='24',
    unit=1000.0,
    limit_up=2715.0,
    limit_down=2225.0,
    reference=2470.0,
    update_date='2026/07/17',
    margin_trading_balance=15,
    short_selling_balance=98,
    day_trade=<DayTrade.Yes: 'Yes'>,
)
StockInfo(
    Contract(security_type='STK', region='TW', exchange='TSE', code='2330'),
    code='2330',
    name='台積電',
    category='24',
    currency=<Currency.TWD: 'TWD'>,
    unit=1000.0,
    day_trade=<DayTrade.Yes: 'Yes'>,
    reference=2470.0,
    limit_up=2715.0,
    limit_down=2225.0,
    margin_trading_balance=15,
    short_selling_balance=98,
    trading_suspended=False,
    margin_loan_ratio=0.6,
    margin_quota_lots=15,
    short_margin_ratio=0.9,
    short_quota_lots=98,
    short_selling_suspended=False,
    disposition_level=0,
    attention_flag=False,
    short_below_par_eligible=True,
    slb_below_par_eligible=True,
    etf_constituent=True,
    settlement_type='0',
    update_date=datetime.date(2026, 7, 17),
)

FutureFuturesInfo, field changes:

1.5 1.7.0 How to adjust
symbol Removed Use code
category Renamed to root Use root
unit Removed
delivery_date, update_date str → datetime.date If you did string handling, use the date directly
Future(
    code='TXFR1',
    symbol='TXFR1',
    name='臺股期貨近月',
    category='TXF',
    delivery_month='202608',
    delivery_date='2026/08/19',
    underlying_kind='I',
    unit=1.0,
    limit_up=46864.0,
    limit_down=38344.0,
    reference=42604.0,
    update_date='2026/07/17',
    target_code='TXFH6',
)
FuturesInfo(
    Contract(security_type='FUT', region='TW', exchange='TAIFEX', code='TXFR1', target_code='TXFH6'),
    code='TXFR1',
    name='臺股期貨 近月',
    root='TXF',
    delivery_month='202608',
    delivery_date=datetime.date(2026, 8, 19),
    last_trading_date=datetime.date(2026, 8, 19),
    begin_date=datetime.date(2026, 5, 21),
    underlying_kind='I',
    underlying_code='IX0001',
    multiplier=200.0,
    contract_size=200.0,
    size_unit='pt',
    quote_ccy='TWD',
    tick_basis='fixed',
    tick=1.0,
    tick_value=200.0,
    spec_kind='index_fut',
    decimal_locator=2,
    dynamic_banding=True,
    flow_group='10',
    reference=42604.0,
    limit_up=46864.0,
    limit_down=38344.0,
    update_date=datetime.date(2026, 7, 17),
)

OptionOptionInfo, field changes:

1.5 1.7.0 How to adjust
symbol Removed Use code
category Renamed to root Use root
unit Removed
delivery_date, update_date str → datetime.date If you did string handling, use the date directly
Option(
    code='TXO34000H6',
    symbol='TXO20260834000C',
    name='臺指選擇權F508月 34000C',
    category='TXO',
    delivery_month='202608',
    delivery_date='2026/08/19',
    strike_price=34000.0,
    option_right=<OptionRight.Call: 'C'>,
    underlying_kind='I',
    unit=1.0,
    limit_up=16270.0,
    limit_down=7150.0,
    reference=11710.0,
    update_date='2026/07/17',
)
OptionInfo(
    Contract(security_type='OPT', region='TW', exchange='TAIFEX', code='TXO34000H6'),
    code='TXO34000H6',
    name='臺指選擇權 202608 C 34000',
    root='TXO',
    delivery_month='202608',
    delivery_date=datetime.date(2026, 8, 19),
    last_trading_date=datetime.date(2026, 8, 19),
    begin_date=datetime.date(2026, 5, 21),
    strike_price=34000.0,
    option_right=<OptionRight.Call: 'C'>,
    expiry_weekday='Wed',
    week_of_month=3,
    underlying_kind='I',
    underlying_code='IX0001',
    multiplier=50.0,
    contract_size=50.0,
    size_unit='pt',
    quote_ccy='TWD',
    tick_basis='premium',
    tick_rule='tw_txo_premium_band',
    tick=10.0,
    tick_value=500.0,
    spec_kind='index_opt',
    decimal_locator=3,
    strike_decimal_locator=0,
    dynamic_banding=True,
    flow_group='1',
    reference=11710.0,
    limit_up=16270.0,
    limit_down=7150.0,
    update_date=datetime.date(2026, 7, 17),
)

IndexIndexInfo, field changes:

1.5 1.7.0 How to adjust
symbol Removed Use code
code '001''IX0001' Indices use exchange codes; see Index codes
Index(
    exchange=<Exchange.TSE: 'TSE'>,
    code='001',
    symbol='TSE001',
    name='加權指數',
)
IndexInfo(
    Contract(security_type='IND', region='TW', exchange='TSE', code='IX0001'),
    code='IX0001',
    name='發行量加權股價指數',
    reference=45624.98,
    open_time='09:00',
    close_time='13:30',
    update_date=datetime.date(2026, 7, 17),
)

In 1.5, warrants were classified as stocks, queried via api.Contracts.Stocks[...] and returning a Stock (with only the common stock fields). In 1.7.0, warrants become a dedicated WarrantInfo, queried by underlying via api.contracts.warrants(underlying), and provide warrant-specific fields such as strike price, call/put, and exercise ratio.

1.5 1.7.0 How to adjust
api.Contracts.Stocks["03011T"] api.contracts.warrants(underlying) Look up warrants by underlying
Stock object WarrantInfo object Fields differ substantially; see the comparison below
Stock(
    exchange=<Exchange.TSE: 'TSE'>,
    code='03011T',
    symbol='TSE03011T',
    name='台積電凱基67售14',
    category='00',
    unit=1000.0,
    limit_up=3.7,
    limit_down=0.01,
    reference=1.25,
    update_date='2026/07/17',
)
WarrantInfo(
    Contract(security_type='WRT', region='TW', exchange='TSE', code='03011T'),
    code='03011T',
    underlying_code='2330',
    underlying_type='1',
    call_put='P',
    financial='W4',
    strike_price=1745.34,
    expiry_date=datetime.date(2027, 7, 19),
    last_trading_date=datetime.date(2027, 7, 15),
    exercise_ratio=0.01,
    exercise_style='European',
    listing_date=datetime.date(2026, 4, 20),
    exercise_start_date=datetime.date(2027, 7, 19),
    exercise_end_date=datetime.date(2027, 7, 19),
    barrier_upper=0.0,
    barrier_lower=0.0,
    residual_value=0.0,
    settlement_method='1',
    investor_restriction=' ',
    issue_size=1200,
    name='台積電凱基67售14',
    reference=1.25,
    limit_up=3.7,
    limit_down=0.01,
    update_date=datetime.date(2026, 7, 17),
    exchange='TSE',
    security_type='WRT',
    region='TW',
)

4. Indices now use exchange codes

Indices now use their exchange codes (e.g. the TAIEX is IX0001); the pre-1.7.0 index codes (such as 001) no longer apply. If your code uses the old index codes, switch to the exchange codes.

api.Contracts.Indexs.TSE["001"]
api.Contracts.Indexs.TSE["IX0001"]

5. SecurityType.Future renamed to Futures

SecurityType.Future (singular) has been renamed to SecurityType.Futures (plural). Use the new name.

sj.SecurityType.Future
sj.SecurityType.Futures

6. Listing contracts over HTTP is now a GET

This change only affects users who query contracts over HTTP (Python users are unaffected). In 1.5, listing contracts of a security type used POST /api/v1/data/contracts with a JSON body; 1.7.0 changes it to GET + query parameters:

curl -X POST http://localhost:8080/api/v1/data/contracts \
  -H "Content-Type: application/json" \
  -d '{"security_type":"STK","page":1,"page_size":3}'
curl "http://localhost:8080/api/v1/data/contracts?security_type=STK&page=1&page_size=3"

Looking up a single contract with GET /api/v1/data/contracts/{code} is unchanged. In addition, 1.7.0 adds several new query endpoints for futures, options, warrants, and more — see Contract for their usage.

Ready to go?

The legacy access still works, so you can move to api.contracts at your own pace. Start with Contract to learn the new query API.