settfex
Your friendly Python library for fetching Thai stock market data 🇹🇭
settfex makes it super easy to get stock market data from the Stock Exchange of Thailand (SET) and Thailand Futures Exchange (TFEX). Whether you're building a trading bot, doing market analysis, or just curious about Thai stocks, we've got you covered!
Every service also exposes flat get_*() convenience functions that return typed Pydantic models — a clean fit for AI/LLM tool-calling and agent workflows.
⚡ Quick Install
pip install settfex
Optional: To run the Jupyter notebook examples, install with:
pip install settfex[examples]
This includes pandas, matplotlib, and jupyter notebook support.
Logging
settfex is silent by default — it installs no loguru handlers and emits no records until you ask. Importing settfex will never touch logging you have already configured.
import settfex # import FIRST — see the note below
from loguru import logger
logger.enable("settfex") # settfex's records flow through YOUR sinks, in YOUR format
⚠️
logger.enable("settfex")must come AFTERimport settfex. settfex disables itself at import, and in loguru the later call wins — so anenable()issued at application startup, with settfex imported lazily later, is silently undone by that import. Nothing raises; the logs simply never appear.
Or let settfex configure logging for you (useful in a script or notebook):
from settfex.utils.logging import setup_logger
setup_logger(level="INFO")
setup_logger(level="DEBUG", log_file="logs/settfex.log")
⚠️ setup_logger() installs unfiltered sinks: they receive every record in the process,
including your application's, rendered in settfex's format. If your app already configures loguru,
use logger.enable("settfex") instead.
Before 0.24.2, importing settfex deleted every loguru handler in the process — see #146. Errors have always been raised as exceptions; only whether settfex prints has changed.
Forwarding settfex's logs to stdlib logging
settfex uses loguru. If your application uses the standard library, bridge them with a sink — this stays in your code rather than in the library, so settfex imposes nothing:
import logging
from loguru import logger
class _ToStdlib(logging.Handler):
pass
def _sink(message):
record = message.record
logging.getLogger(record["name"]).log(
logging.getLevelName(record["level"].name), record["message"]
)
logger.remove() # only your own handlers; settfex adds none
logger.add(_sink)
logger.enable("settfex") # AFTER `import settfex` — see the note above
Known issues
- A flagged SEC company match can still be the wrong company.
is_primarymeans the SEC site recognised your query as its own identifier — SEC abbreviations and SET tickers are separate namespaces and they collide.PMCresolves, flagged, toPORT AND MARINE CORPORATIONrather than the SET-listedPMC LABEL MATERIAL. Verify by comparingcompany_nameagainst the issuer you intended; re-resolving and comparingunique_idproves only that resolution is deterministic. A caller-side check is proposed for 0.25.0. get_holidays()currently fails — the SET holiday endpoint answers HTTP 401 for the current year, which is the only year it serves. This is an upstream change, not a settfex defect, and upgrading does not fix it. Since 0.24.1 the call fails immediately rather than retrying for ~128 s. Tracking: #140.
Versioning policy
- 0.x: a minor bump may break. Pin
settfex>=0.23,<0.24until you have migrated. - 1.0 guarantees: every public entry point passes the fault-injection contract (no silent data loss), and breaking changes require a major bump.
- From 0.24 on: a
DeprecationWarningone minor release before any removal or behaviour change. - Stored results from before 0.24.0? Earlier versions could return incomplete data, or for a name lookup another company's data, without an error. Check them against the data completeness advisory.
📓 Interactive Examples
New to settfex? Start here! We have comprehensive Jupyter notebook examples that teach you everything step-by-step:
🎓 Learning Path
Beginners : Start with these three notebooks to get comfortable:
- Stock List - Discover all Thai stocks
- Highlight Data - Get key metrics like P/E, market cap, dividends
- Corporate Actions - Track dividends and shareholder meetings
Fundamental Analysis : Build a complete stock analysis workflow:
Professional Trading : Master all features for institutional use:
- All 20 SET notebooks + TFEX notebooks (see below)
📊 SET Examples (Stock Exchange of Thailand)
All examples include beginner explanations, professional trading use cases, and data export examples:
- Stock List - Fetch and filter all stocks, build portfolio universes
- Highlight Data - Value screeners, dividend portfolios, risk-adjusted returns
- Stock Profile - Listing details, IPO data, foreign ownership
- Company Profile - ESG ratings, governance scores, management
- Corporate Actions - Dividend calendars, shareholder meetings
- Shareholder Data - Ownership analysis, free float monitoring
- NVDR Holders - NVDR ownership tracking and analysis
- Board of Directors - Board composition and management structure
- Trading Statistics - Multi-period performance and volatility
- Price Performance - Sector comparison and alpha calculation
- Financial Statements - Balance sheet, income, cash flow analysis
- Earnings Call (Opportunity Day) - OPPDAY calendar, YouTube links, Thai transcripts for AI
- Chart Quotation & Latest Price - Intraday series and the latest traded price relative to now
- Latest Historical Trading - Latest trading-day summary: OHLCV, P/E, P/BV, market cap
- Market Index - Index directory, SET50/SETESG quotations, constituents, and index membership per stock
- SET News - Company news/disclosures for all stocks: symbol/date/keyword filters, Thai headlines
- Market Holidays - Official market-closure calendar: is the market open, next holiday, long weekends
- Asset Types & Depositary Receipts - Tell stocks/ETFs/DRs/DWs apart, DR profiles, and TradingView indicative prices
- Analyst Consensus (IAA) - Broker target prices, forecasts, research PDF links, and a whole-market buy/hold/sell screener
- Stock Info - Live quote block and trading signs (SP/NC/NP/CB/XD): is a symbol suspended, best bid/offer, every security type
📈 TFEX Examples (Thailand Futures Exchange)
Professional derivatives trading workflows with margin calculations and risk management:
- Series List - Discover futures/options, rollover monitoring, options chains
- Trading Statistics - Margin requirements, position sizing, P/L tracking
- Underlying Price - Underlying instrument prices for TFEX series (SET50 spot for index futures/options)
📂 View All Examples - Complete index with learning guides
📚 Full Documentation
Building an AI agent or LLM tool? Start with AGENTS.md — the service map,
the flat get_*() calling contract, and the failure modes that silently produce wrong answers.
Want to dig deeper? Check out our detailed guides:
SET Services
- Stock List Service - Get all stocks on SET/mai
- Highlight Data Service - Market metrics and valuations
- Stock Profile Service - Listing details and share structure
- Company Profile Service - Full company information
- Corporate Action Service - Dividends, meetings, and events
- Shareholder Service - Major shareholders and ownership data
- NVDR Holder Service - NVDR holder information and ownership
- Board of Director Service - Board of directors and management structure
- Trading Statistics Service - Historical trading performance and metrics
- Price Performance Service - Stock, sector, and market price performance comparison
- Financial Service - Balance sheet, income statement, and cash flow data
- Chart Quotation Service - Intraday/historical price chart series, plus the latest traded price relative to now (DRs answer with the indicative price — see below)
- Latest Historical Trading Service - Latest trading day summary with OHLCV and valuation metrics
- Earnings Call (Opportunity Day) Service - OPPDAY earnings-call calendar with YouTube links, as models or a DataFrame
- Market Index Service - Index directory (SET50/SET100/sSET/SETESG/...), quotations, constituents, and latest index value
- News Service - Company news and disclosures for all stocks, with symbol/date/keyword filters
- Market Holiday Service - Official SET market-closure calendar for the year, in English or Thai
- DR Profile Service - Depositary Receipt details: issuer, underlying, conversion ratio, and the TradingView "Indicative Price" link
- DR Indicative Price Service - A DR's fair value in THB (underlying × FX ÷ ratio) from TradingView
- Analyst Consensus Service - IAA broker target prices, earnings forecasts and research PDF links, plus a market-wide buy/hold/sell screener
- Stock Info Service - Live quote block and trading signs (
SP/NC/NP/CB/XD) for every security type, plus best bid/offer
TFEX Services
- TFEX Series List Service - Get all futures and options series on TFEX
- TFEX Trading Statistics Service - Trading statistics, settlement prices, and margin requirements
SEC Services (market.sec.or.th)
- SEC Document Service - List and download the raw disclosure documents filed with the Thai SEC (the original financial-statement Excel package, Form 56-1, Form 56-2, Key Financial Ratio, MD&A)
- SEC Form 59: Executive Trades - Directors' and executives' reported trades (แบบ 59), with the report behind each row: holdings before and after, unrounded price, channel and counterparty. Marks spouse duplicates and revoked filings instead of counting them
ThaiBMA Services (www.thaibma.or.th)
- Government Bond Yield Curve - The official Thai government bond yield curve for any date back to 1999, the bond quotes behind it, and daily history at one request per year (the whole 27-year record in 28 requests)
Utilities
- AsyncDataFetcher - Low-level async HTTP client
- Session Caching - How we make things 25x faster
🎯 What Can You Do?
SET (Stock Exchange of Thailand)
📋 Get Stock List
Want to see all stocks trading on SET? Easy!
from settfex.services.set import get_stock_list
stock_list = await get_stock_list()
print(f"Found {stock_list.count} stocks!")
# Filter by market
set_stocks = stock_list.filter_by_market("SET")
mai_stocks = stock_list.filter_by_market("mai")
# Index memberships are included by default
print(stock_list.get_symbol("CPALL").indices) # ['SET50', 'SET100', 'SETESG', ...]
set50_members = stock_list.filter_by_index("SET50")
# Filter by asset type (stock, ETF, DR, DW, warrant, ...)
drs = stock_list.filter_by_asset_type("dr") # GOOG80, MICRON01, ...
etfs = stock_list.filter_by_asset_type("etf")
👉 Learn more about Stock Lists
📈 Get Market Index Data
Track SET50, SET100, sSET, SETESG, and every other index — quotes and constituents!
from settfex import SetIndex
from settfex.services.set import get_index_list, get_index_info_list
# All indices with one call each
indices = await get_index_list() # directory: 55 indices
quotes = await get_index_info_list() # live quotes for the 11 headline indices
# Deep-dive one index
index = SetIndex("SET50")
info = await index.get_info() # last, change, OHLC, volume, value, status
print(f"SET50: {info.last} ({info.percent_change:+.2f}%) [{info.market_status}]")
constituents = await index.get_constituents() # 50 stocks with full quote rows
latest = await index.get_latest_price() # latest traded index value (intraday)
👉 Learn more about Market Indices
💰 Get Stock Highlight Data
Need market cap, P/E ratio, dividend yield? We got you!
from settfex.services.set import Stock
stock = Stock("CPALL")
data = await stock.get_highlight_data()
print(f"Market Cap: {data.market_cap:,.0f} THB")
print(f"P/E Ratio: {data.pe_ratio}")
print(f"Dividend Yield: {data.dividend_yield}%")
👉 Learn more about Highlight Data
📊 Get Stock Profile
Want to know when a company was listed? Its IPO price? Foreign ownership limits?
from settfex.services.set import get_profile
profile = await get_profile("PTT")
print(f"Listed: {profile.listed_date}")
print(f"IPO Price: {profile.ipo} {profile.currency}")
print(f"Foreign Limit: {profile.percent_foreign_limit}%")
👉 Learn more about Stock Profiles
🏢 Get Company Profile
Curious about company details, management, auditors, or ESG ratings?
from settfex.services.set import get_company_profile
company = await get_company_profile("CPN")
print(f"Company: {company.name}")
print(f"Website: {company.url}")
print(f"CG Score: {company.cg_score}/5")
print(f"ESG Rating: {company.setesg_rating}")
print(f"Executives: {len(company.managements)}")
👉 Learn more about Company Profiles
📅 Get Corporate Actions
Track dividends, shareholder meetings, and other corporate events:
from settfex.services.set import get_corporate_actions
actions = await get_corporate_actions("AOT")
for action in actions:
if action.ca_type == "XD":
print(f"Dividend: {action.dividend} {action.currency}")
print(f"XD Date: {action.x_date}")
print(f"Payment Date: {action.payment_date}")
elif action.ca_type == "XM":
print(f"Meeting: {action.meeting_type}")
print(f"Agenda: {action.agenda}")
👉 Learn more about Corporate Actions
👥 Get Shareholder Data
See who owns what! Get major shareholders, free float, and ownership distribution:
from settfex.services.set import get_shareholder_data
data = await get_shareholder_data("MINT")
print(f"Total Shareholders: {data.total_shareholder:,}")
print(f"Free Float: {data.free_float.percent_free_float:.2f}%")
for sh in data.major_shareholders[:5]:
print(f"{sh.sequence}. {sh.name}: {sh.percent_of_share:.2f}%")
👉 Learn more about Shareholder Data
📜 Get NVDR Holder Data
Track Non-Voting Depository Receipt (NVDR) holders and their ownership:
from settfex.services.set import get_nvdr_holder_data
data = await get_nvdr_holder_data("MINT")
print(f"Symbol: {data.symbol}") # MINT-R
print(f"Total NVDR Holders: {data.total_shareholder:,}")
for holder in data.major_shareholders[:5]:
print(f"{holder.sequence}. {holder.name}: {holder.percent_of_share:.2f}%")
👉 Learn more about NVDR Holder Data
👔 Get Board of Directors
See who's running the show! Get board of directors and management information:
from settfex.services.set import get_board_of_directors
directors = await get_board_of_directors("MINT")
for director in directors:
positions = ", ".join(director.positions)
print(f"{director.name}: {positions}")
# Find the Chairman
chairman = next((d for d in directors if "CHAIRMAN" in d.positions), None)
if chairman:
print(f"Chairman: {chairman.name}")
👉 Learn more about Board of Directors
📊 Get Trading Statistics
Track historical trading performance with comprehensive statistics across multiple time periods:
from settfex.services.set import get_trading_stats
stats = await get_trading_stats("MINT")
# YTD performance
ytd = next(s for s in stats if s.period == "YTD")
print(f"YTD Performance: {ytd.percent_change:.2f}%")
print(f"Current Price: {ytd.close:.2f} THB")
print(f"P/E Ratio: {ytd.pe}, Market Cap: {ytd.market_cap:,.0f} THB")
# Compare different periods
for stat in stats:
print(f"{stat.period}: {stat.close:.2f} THB ({stat.percent_change:+.2f}%)")
👉 Learn more about Trading Statistics
📈 Get Price Performance
Compare stock performance against sector and market with comprehensive price change data:
from settfex.services.set import get_price_performance
data = await get_price_performance("MINT")
# Stock performance
print(f"Stock: {data.stock.symbol}")
print(f" YTD: {data.stock.ytd_percent_change:+.2f}%")
print(f" P/E: {data.stock.pe_ratio}, P/B: {data.stock.pb_ratio}")
# Sector comparison
print(f"Sector ({data.sector.symbol}): {data.sector.ytd_percent_change:+.2f}%")
# Market comparison
print(f"Market ({data.market.symbol}): {data.market.ytd_percent_change:+.2f}%")
👉 Learn more about Price Performance
📉 Get Chart Quotation & Latest Traded Price
Fetch the intraday/historical price series for any stock, or jump straight to the latest traded price relative to now (null future/lunch/no-trade buckets are excluded automatically):
from settfex.services.set import get_chart_quotation, get_latest_price
# The latest TRADED quotation right now (or None if nothing has traded yet)
quote = await get_latest_price("CPALL")
if quote:
print(f"{quote.local_datetime}: {quote.price} (vol {quote.volume:,.0f})")
# Or work with the full series — intraday (1-minute) or historical (daily)
chart = await get_chart_quotation("CPALL", period="1D")
print(f"Prior close: {chart.prior}, data points: {len(chart.quotations)}")
print(f"Latest price (with prior fallback): {chart.get_latest_price()}")
chart_1y = await get_chart_quotation("CPALL", period="1Y")
print(f"1Y data points: {len(chart_1y.quotations)}")
Depositary Receipts differ:
Stock("GOOG80").get_latest_price()returns the TradingView indicative price instead of SET chart data (see the next section). The top-levelget_latest_price()function shown here is always SET chart data.
👉 Learn more about Chart Quotation & Latest Price
🌏 Get Asset Types & Depositary Receipt Prices
SET lists more than common stocks — ETFs, DRs, DWs, warrants, preferred shares and unit trusts all share the same APIs. Find out what a symbol actually is, and price DRs against their underlying:
from settfex.services.set import AssetType, Stock, get_dr_indicative_price
# What kind of instrument is this?
await Stock("CPALL").get_asset_type() # AssetType.STOCK ('stock')
await Stock("1DIV").get_asset_type() # AssetType.ETF ('etf')
await Stock("GOOG80").get_asset_type() # AssetType.DEPOSITARY_RECEIPT ('dr')
# DRs: issuer/underlying details + the "Indicative Price" TradingView chart
dr = Stock("GOOG80")
profile = await dr.get_dr_profile()
print(profile.underlying, profile.conversion_ratio) # GOOG "2,000 : 1"
print(await dr.get_tradingview_url()) # https://th.tradingview.com/chart/?symbol=...
# Fair value in THB = underlying price × FX ÷ conversion ratio (live from TradingView)
price = await get_dr_indicative_price("GOOG80")
print(f"{price.indicative_price:.2f} THB "
f"({price.underlying.close} {price.underlying.currency} × {price.fx.close} ÷ {price.ratio:.0f})")
# ...and get_latest_price() uses it automatically for DRs
quote = await dr.get_latest_price() # DrIndicativeQuotation
quote = await dr.get_latest_price(prefer_dr_indicative=False) # SET traded price instead
👉 Learn more about DR Profiles · DR Indicative Price
🚦 Check Trading Signs (is it suspended?)
The quote block is the only source of a symbol's trading sign — and the only one that has it
for warrants, DWs and DRs too. SET packs every active sign into one comma-separated string,
so never compare sign == "SP":
from settfex.services.set import Stock, get_stock_info
info = await get_stock_info("GRAND")
print(info.sign) # 'SP, CB, CS, CC' <- several codes in one string
print(info.signs) # ['SP', 'CB', 'CS', 'CC']
print(info.is_suspended) # True
print(info.has_sign("nc")) # False (case-insensitive)
# Live quote data comes with it
print(f"{info.name_en}: last={info.last} bid/ask={info.best_bid}/{info.best_offer}")
print(f"status={info.market_status} as of {info.market_date_time}")
# Or through the Stock facade
halted = await Stock("INGRS").is_suspended()
⚠️
market_statusreads'Closed'for every symbol outside trading hours — it is not a suspension flag. Onlyis_suspended(which reads the per-symbol sign) answers that. A halted symbol returns HTTP 200 withlast/OHLC/volumeNoneand an empty book; useprior.
🎯 Get Analyst Consensus
Broker target prices, earnings forecasts and links to each broker's research PDF — split into the two tables the Settrade page shows (aggregates, and one row per broker):
from settfex.services.set import get_analyst_consensus, get_consensus_overall
data = await get_analyst_consensus("GULF")
print(f"{data.count} brokers | target {data.low.target_price}–{data.high.target_price} "
f"(median {data.median.target_price})")
for broker, url in data.research_urls: # brokers that published a PDF
print(f"{broker:<10} {url}")
# Two DataFrames (needs the optional `dataframe` extra)
stats_df = data.stats_to_dataframe() # average / median / high / low
brokers_df = data.to_dataframe() # broker, analyst, recommend, target, PDF link
# Buy/hold/sell counts — omit the symbol for a whole-market screener
summary = await get_consensus_overall("GULF")
row = summary.get("GULF")
print(f"{row.buy} buy / {row.hold} hold / {row.sell} sell ({row.bullish}% bullish)")
market = await get_consensus_overall() # every covered SET stock, one request
👉 Learn more about Analyst Consensus
💰 Get Financial Statements
Fetch comprehensive financial data including balance sheet, income statement, and cash flow:
from settfex.services.set import (
get_balance_sheet,
get_income_statement,
get_cash_flow
)
# Balance sheet
balance_sheets = await get_balance_sheet("CPALL")
latest = balance_sheets[0]
print(f"Period: {latest.quarter} {latest.year}")
print(f"Total Assets: {latest.accounts[0].amount:,.0f}K")
# Income statement
income_statements = await get_income_statement("CPALL")
for stmt in income_statements[:3]:
print(f"{stmt.quarter} {stmt.year}: {stmt.status}")
# Cash flow
cash_flows = await get_cash_flow("CPALL")
👉 Learn more about Financial Service
📊 Get Latest Historical Trading
Get the latest trading day summary with OHLCV, P/E, P/BV, dividend yield, and market cap:
from settfex.services.set import get_latest_historical_trading
trading = await get_latest_historical_trading("CPALL")
print(f"Date: {trading.date}")
print(f"Close: {trading.close:.2f} THB ({trading.percent_change:+.2f}%)")
print(f"P/E: {trading.pe}, P/BV: {trading.pbv}")
print(f"Market Cap: {trading.market_cap:,.0f} THB")
👉 Learn more about Latest Historical Trading
🎤 Get Earnings Call (Opportunity Day) Calendar
Fetch the SET Opportunity Day earnings-call calendar — symbol, company, date, clip duration, and a ready-to-use YouTube link — as typed models or a pandas DataFrame:
from settfex.services.set import get_earnings_calls, get_earnings_calls_dataframe
# Typed models
response = await get_earnings_calls()
for item in response.items[:5]:
print(item.symbol, item.company_name_clean, item.youtube_url)
# …or straight to a DataFrame (requires: pip install "settfex[dataframe]")
df = await get_earnings_calls_dataframe()
# columns: stock_name, company_name, earnings_call_date, video_clip_time, youtube_url
# Search one company, or filter by quarter/type
hann = await get_earnings_calls(keyword="HANN")
# Grab the WHOLE archive fast — pages are fetched concurrently, with an optional progress bar
# (pip install "settfex[progress]"):
from settfex.services.set import get_all_earnings_calls
everything = await get_all_earnings_calls(progress=True) # ~9520 records in ~15s (~10x faster)
# Thai subtitles as raw text for AI/LLM use (pip install "settfex[transcript]"):
from settfex.services.set import fetch_transcripts, get_earnings_call_transcript
await fetch_transcripts(hann.items) # fills item.transcript (Thai) per video
text = await get_earnings_call_transcript(6319) # …or one presentation's transcript by id
⚠️ Transcripts & YouTube limits. YouTube rate-limits / IP-blocks aggressively (especially from cloud servers), so transcript fetching is built for a filtered set (a company / quarter) — not the full ~9,520-video archive — and defaults to low concurrency (3). A blocked/missing/disabled transcript simply comes back as
None; if your host IP is blocked, passproxies={"http": ..., "https": ...}. Results can vary by IP.
👉 Learn more about the Earnings Call Service
📰 Get SET News & Disclosures (All Stocks)
Company news for the whole market in one call — or filtered by symbol, date window, and keyword:
from settfex.services.set import get_news
# Latest trading day, ALL stocks (company disclosures, English)
news = await get_news()
print(f"{news.count} items")
# One stock, Thai headlines, a July window
# (dates are dd/MM/yyyy or datetime.date objects — ISO strings raise InvalidDateError)
cpall = await get_news(symbol="CPALL", lang="th", from_date="01/07/2026", to_date="17/07/2026")
# Helpers: today's items, financial statements only
today = news.filter_today()
fin = news.filter_by_tag("financial-statement")
👉 Learn more about the News Service
📅 Check Market Holidays
The official SET market-closure calendar for the year, in English or Thai:
from datetime import date
from settfex.services.set import get_holidays
# Defaults to the current year in Asia/Bangkok
calendar = await get_holidays()
print(f"{calendar.count} holidays in {calendar.year}")
for holiday in calendar.holidays[:3]:
print(f"{holiday.holiday_date:%Y-%m-%d %a} {holiday.description}")
# 2026-01-01 Thu New Year's Day
# 2026-01-02 Fri Additional special holiday
# 2026-03-03 Tue Makha Bucha Day
calendar.is_holiday(date(2026, 1, 1)) # True
calendar.next_holiday() # the next closure from today
calendar.filter_by_month(4) # Songkran cluster
# Thai names
thai = await get_holidays(lang="th")
⚠️ Two limits worth knowing: the API serves only the current year, and
is_holiday()means "on SET's published holiday list" — weekends are not in the payload, so combine it with a weekday check to answer "is the market open?".
👉 Learn more about the Market Holiday Service
TFEX (Thailand Futures Exchange)
📋 Get TFEX Series List
Want to see all futures and options trading on TFEX? Easy!
from settfex.services.tfex import get_series_list
series_list = await get_series_list()
print(f"Found {series_list.count} series!")
# Filter active futures only
active_futures = [s for s in series_list.get_futures() if s.active]
print(f"Active futures: {len(active_futures)}")
# Filter by underlying
set50_series = series_list.filter_by_underlying("SET50")
print(f"SET50 contracts: {len(set50_series)}")
👉 Learn more about TFEX Series List
📊 Get TFEX Trading Statistics
Get comprehensive trading statistics including settlement prices, margin requirements, and days to maturity!
from settfex.services.tfex import get_trading_statistics
stats = await get_trading_statistics("S50Z25")
print(f"Settlement Price: {stats.settlement_price:.5f}")
print(f"Days to Maturity: {stats.day_to_maturity}")
print(f"Initial Margin: {stats.im:,.2f} THB")
print(f"Maintenance Margin: {stats.mm:,.2f} THB")
# Calculate margin coverage
capital = 500000
max_contracts = int(capital / stats.im)
print(f"Can trade {max_contracts} contracts with {capital:,.0f} THB")
👉 Learn more about TFEX Trading Statistics
ThaiBMA (Thai Bond Market Association)
🏦 Get the Government Bond Yield Curve
The official Thai risk-free curve — for today, for any date back to 1999, or as daily history.
from settfex.services.thaibma import ThaiBMA, get_government_yield_curve
curve = await get_government_yield_curve() # the latest published curve
print(f"as of {curve.as_of}: 10Y = {curve.yield_at('10Y')}%")
print(f"2s10s slope: {curve.slope_bps('2Y', '10Y'):.1f} bp")
print(curve.interpolate(7.5)) # linear between grid points
# The endpoint NEVER 404s on a date — a weekend, a holiday, or a future date
# silently returns an earlier curve. settfex always tells you when that happened:
saturday = await ThaiBMA().get_yield_curve("2026-08-08")
print(saturday.is_rolled_back, saturday.as_of, saturday.rollback_days) # True 2026-08-07 1
History costs one request per year, not one per business day — the whole 27-year record is 28 requests instead of ~6,600:
from settfex.services.thaibma import get_yield_curve_history
history = await get_yield_curve_history("2020-01-01") # 7 requests
print(history.count, len(history.columns)) # 1608 business days x 54 tenors
df = history.to_dataframe() # requires: pip install "settfex[dataframe]"
df["10Y"].plot(title="Thai 10Y government yield")
👉 Learn more about the ThaiBMA Yield Curve
SEC (Thai Securities and Exchange Commission)
🧾 Directors' and executives' trades (Form 59)
Every reported insider trade, with the report behind it: holdings before and after, the exact average price, the trading channel, the counterparty.
from settfex.services.sec import get_executive_trades
trades = await get_executive_trades(symbol="SPALI", date_type="transaction",
start="2026-09-01", end="2026-09-30", with_details=True)
for t in trades.economic_trades(): # copies and revoked filings removed
print(t.transaction_date, t.reporter_name, "->", t.executor_name or "(self)",
t.side, f"{t.quantity:,}", t.detail.avg_price if t.detail else t.price)
print(trades.economic_trades().to_table()) # the Thai summary table
The named executive is the person who filed, who may not be the one who traded (a spouse, a
child, a family company). When both spouses are executives, one trade is listed twice. Count
trades on economic_trades(), never on the raw list.
👉 Learn more about SEC Form 59
🚀 Why settfex?
⚡ Blazing Fast
First request takes ~2 seconds (warming up). After that? 100ms! That's 25x faster thanks to smart session caching.
Dual-Site Support: Separate cached sessions for SET and TFEX - each optimized for its own API!
🇹🇭 Thai Language Support
Full UTF-8 support for Thai characters. Company names, sectors, everything just works!
🔒 Type Safe
Everything is type-hinted and validated with Pydantic. Your IDE will love it!
🪵 Smart Logging
Beautiful logs with loguru. Debug issues easily or turn them off in production.
💡 Quick Example
Here's everything in action:
import asyncio
from settfex.services.set import (
get_stock_list,
get_profile,
get_company_profile,
get_corporate_actions,
get_board_of_directors,
get_trading_stats,
Stock
)
async def analyze_stock(symbol: str):
# Get basic info from stock list
stock_list = await get_stock_list()
stock_info = stock_list.get_symbol(symbol)
if not stock_info:
print(f"Stock {symbol} not found!")
return
print(f"📊 {stock_info.name_en} ({symbol})")
print(f"Market: {stock_info.market}")
print(f"Sector: {stock_info.sector}")
# Get detailed metrics
stock = Stock(symbol)
highlight = await stock.get_highlight_data()
print(f"\n💰 Valuation:")
print(f"Market Cap: {highlight.market_cap:,.0f} THB")
print(f"P/E Ratio: {highlight.pe_ratio}")
print(f"Dividend Yield: {highlight.dividend_yield}%")
# Get listing details
profile = await get_profile(symbol)
print(f"\n📅 Listed: {profile.listed_date}")
print(f"IPO: {profile.ipo} {profile.currency}")
# Get company info
company = await get_company_profile(symbol)
print(f"\n🏢 {company.name}")
print(f"Website: {company.url}")
print(f"ESG Rating: {company.setesg_rating}")
# Get corporate actions
actions = await get_corporate_actions(symbol)
print(f"\n📅 Corporate Actions: {len(actions)}")
for action in actions[:3]: # Show first 3
if action.ca_type == "XD":
print(f" Dividend: {action.dividend} {action.currency}")
elif action.ca_type == "XM":
print(f" Meeting: {action.meeting_type}")
# Get board of directors
directors = await get_board_of_directors(symbol)
print(f"\n👔 Board of Directors: {len(directors)}")
chairman = next((d for d in directors if "CHAIRMAN" in d.positions), None)
if chairman:
print(f" Chairman: {chairman.name}")
# Get trading statistics
stats = await get_trading_stats(symbol)
ytd = next((s for s in stats if s.period == "YTD"), None)
if ytd:
print(f"\n📊 Trading Statistics (YTD):")
print(f" Performance: {ytd.percent_change:+.2f}%")
print(f" Volume: {ytd.total_volume:,.0f} shares")
print(f" Turnover: {ytd.turnover_ratio:.2f}%")
# Run it!
asyncio.run(analyze_stock("PTT"))
🛠️ Advanced Usage
Need more control? We've got you covered!
from settfex.utils.data_fetcher import AsyncDataFetcher, FetcherConfig
# Custom configuration
config = FetcherConfig(
timeout=60, # Longer timeout
max_retries=5, # More retries
browser_impersonate="safari17_0" # Different browser
)
# Use with any service
from settfex.services.set.stock import StockHighlightDataService
service = StockHighlightDataService(config=config)
data = await service.fetch_highlight_data("CPALL")
👉 Learn more about AsyncDataFetcher
🧪 Optional: Configure Logging
By default, settfex only shows ERROR-level logs to keep your terminal clean. Want to see more?
from settfex.utils.logging import setup_logger
# Turn on detailed logs for debugging
setup_logger(level="DEBUG", log_file="logs/settfex.log")
# Or just INFO level for general monitoring
setup_logger(level="INFO")
# Now run your code - you'll see what's happening!
stock_list = await get_stock_list()
Great for debugging or monitoring in production. Default is ERROR level for clean output.
🧯 Error Handling
settfex raises typed exceptions from settfex.exceptions (all re-exported from the top level), so you can catch exactly what you need. They stay backward-compatible — fetch errors subclass Exception, validation errors subclass ValueError:
from settfex import get_highlight_data
from settfex.exceptions import (
FetchError, # HTTP/transport failure — has .status_code and .symbol
SymbolNotFoundError, # HTTP 404 for a symbol — subclass of FetchError
InvalidSymbolError, # empty/invalid symbol — subclass of ValueError
InvalidLanguageError, # unrecognized language — subclass of ValueError
)
try:
data = await get_highlight_data("CPALLL") # typo
except SymbolNotFoundError as exc:
print(exc) # "... HTTP 404 — did you mean 'CPALL'?"
print(exc.suggestion) # "CPALL" — a network-free hint from a stock list you already fetched
except FetchError as exc:
print(f"Fetch failed (HTTP {exc.status_code}) for {exc.symbol}")
SymbolNotFoundError.suggestion is computed only from the stock list already fetched this session (via get_stock_list()); a 404 never triggers an extra request, and it's None when no list has been fetched yet.
📋 Changelog
See CHANGELOG.md for the full, versioned release history (this project follows Keep a Changelog and Semantic Versioning).
🤝 Contributing
We'd love your help making settfex better! Here's how:
- Fork the repo
- Create a feature branch:
git checkout -b feature/cool-new-thing - Install dependencies:
uv sync - Make your changes with proper type hints and tests
- Run tests:
uv run pytest - Run linting:
uv run ruff check . - Type-check:
uv run mypy . - Commit:
git commit -m 'Add cool new thing' - Push:
git push origin feature/cool-new-thing - Open a Pull Request
📜 License
MIT License - feel free to use this in your projects!
⚠️ Disclaimer
This library is not officially affiliated with the Stock Exchange of Thailand or Thailand Futures Exchange. Use at your own risk for educational and informational purposes.
🙋 Need Help?
- 📖 Check the detailed documentation
- 🐛 Found a bug? Open an issue
Metadata
Release files for settfex 0.26.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| settfex-0.26.0.tar.gz | 1.4 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| settfex-0.26.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / settfex-0.26.0.tar.gz
| Download URL | settfex-0.26.0.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8d85f61a29af0c97ae6711a8ef299037c3d58177fd3f007c8958cce0b47c0f09
|
|
BLAKE2b-256 checksum How to use checksums |
bb48eae40185afc70b63695103ee54f5b0d469991eb85f3c064d6130a17b50fc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / settfex-0.26.0-py3-none-any.whl
| Download URL | settfex-0.26.0-py3-none-any.whl |
|---|---|
| Size | 305.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bdb500ff9f4fccf715f096c6dadde10e3acbf5336990b4fdffe271aa085dbdcf
|
|
BLAKE2b-256 checksum How to use checksums |
b7c4ea1554b9e5565e7baede421e6a60405ec2a3990e84c0159daa9fcb6e65d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logSecurity for settfex 0.26.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for settfex-0.26.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.