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.contractsquery: 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 viaapi.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 viaapi.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_contract/contracts_timeout/contracts_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 Stock/Future/Option/Index |
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:
Stock → StockInfo, 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),
)
Future → FuturesInfo, 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),
)
Option → OptionInfo, 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),
)
Index → IndexInfo, 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.