Skip to main content

settfex

Your friendly Python library for fetching Thai stock market data 🇹🇭

PyPI version Python 3.11+ License: MIT

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.

📓 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:

Fundamental Analysis : Build a complete stock analysis workflow:

Professional Trading : Master all features for institutional use:

  • All 15 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:

  1. Stock List - Fetch and filter all stocks, build portfolio universes
  2. Highlight Data - Value screeners, dividend portfolios, risk-adjusted returns
  3. Stock Profile - Listing details, IPO data, foreign ownership
  4. Company Profile - ESG ratings, governance scores, management
  5. Corporate Actions - Dividend calendars, shareholder meetings
  6. Shareholder Data - Ownership analysis, free float monitoring
  7. NVDR Holders - NVDR ownership tracking and analysis
  8. Board of Directors - Board composition and management structure
  9. Trading Statistics - Multi-period performance and volatility
  10. Price Performance - Sector comparison and alpha calculation
  11. Financial Statements - Balance sheet, income, cash flow analysis
  12. Earnings Call (Opportunity Day) - OPPDAY calendar, YouTube links, Thai transcripts for AI
  13. Chart Quotation & Latest Price - Intraday series and the latest traded price relative to now
  14. Latest Historical Trading - Latest trading-day summary: OHLCV, P/E, P/BV, market cap
  15. Market Index - Index directory, SET50/SETESG quotations, constituents, and index membership per stock

📈 TFEX Examples (Thailand Futures Exchange)

Professional derivatives trading workflows with margin calculations and risk management:

  1. Series List - Discover futures/options, rollover monitoring, options chains
  2. Trading Statistics - Margin requirements, position sizing, P/L tracking

📂 View All Examples - Complete index with learning guides

📚 Full Documentation

Want to dig deeper? Check out our detailed guides:

SET Services

TFEX Services

Utilities

🎯 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")

👉 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: {chart.quotations[-1].close:.2f} THB")

chart_1y = await get_chart_quotation("CPALL", period="1Y")
print(f"1Y data points: {len(chart_1y.quotations)}")

👉 Learn more about Chart Quotation & Latest Price


💰 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, pass proxies={"http": ..., "https": ...}. Results can vary by IP.

👉 Learn more about the Earnings Call 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


🚀 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!

👉 Learn about Session Caching

🇹🇭 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:

  1. Fork the repo
  2. Create a feature branch: git checkout -b feature/cool-new-thing
  3. Install dependencies: uv sync
  4. Make your changes with proper type hints and tests
  5. Run tests: uv run pytest
  6. Run linting: uv run ruff check .
  7. Type-check: uv run mypy .
  8. Commit: git commit -m 'Add cool new thing'
  9. Push: git push origin feature/cool-new-thing
  10. 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?


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

settfex-0.10.1.tar.gz (600.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

settfex-0.10.1-py3-none-any.whl (121.6 kB view details)

Uploaded Python 3

File details

Details for the file settfex-0.10.1.tar.gz.

File metadata

  • Download URL: settfex-0.10.1.tar.gz
  • Upload date:
  • Size: 600.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for settfex-0.10.1.tar.gz
Algorithm Hash digest
SHA256 3921845b38f7a9abf94401bb8d861ea1a09cb7c36a573f15ec2797d6548a884c
MD5 225c78a21f5c5f5aeb34de741e9ba4b8
BLAKE2b-256 6aa39b8d9ac5e15362a18436a1289538c6b362e1cc11d35233fc069bae1422f8

See more details on using hashes here.

Provenance

The following attestation bundles were made for settfex-0.10.1.tar.gz:

Publisher: release.yml on lumduan/settfex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file settfex-0.10.1-py3-none-any.whl.

File metadata

  • Download URL: settfex-0.10.1-py3-none-any.whl
  • Upload date:
  • Size: 121.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for settfex-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fe2fdcb66b1d1cab79914901130be6e11b09f2d4552c8ca2378c826967a23c2d
MD5 05e559e95962bad6d472fb757caff7ab
BLAKE2b-256 f2a458d9397db3cfc64b8f1152d8775ec6162bd773067db78fe40add9799df91

See more details on using hashes here.

Provenance

The following attestation bundles were made for settfex-0.10.1-py3-none-any.whl:

Publisher: release.yml on lumduan/settfex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.19.2

2 files

0.19.1

2 files

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.11.0

2 files

This release

0.10.1 This release

2 files

0.10.0

2 files

0.8.0

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page