Skip to content

nutdnuy/sec-opendata-th

Repository files navigation

sec-opendata-th

A Python client and Codex/Claude plugin for pulling Thai capital-market data from the SEC OpenAPI (api.sec.or.th) — every SEC Open Data portal category, including digital assets, funds, Licence Check, One Report, provident funds, debt, equity, ESG, capital-market operators/professionals, investors, and future /v1/... or /v2/... endpoints.

The SEC exposes both legacy products such as FundFactsheet and FundDailyInfo, and current portal paths such as /v1/one-report/fs/{report_year}/financial_statement/{unique_id} and /v2/fund/factsheet/performance. The generic request command reaches any endpoint once you have the matching subscription key.

Install

git clone https://github.com/nutdnuy/sec-opendata-th
cd sec-opendata-th
pip install -e .            # installs the `secopendata` package + CLI
# (or: pip install -r requirements.txt and run via `python -m secopendata`)

Subscription keys

Get keys from the SEC API Developer Portal. The portal is migrating: secopendata.sec.or.th/sec-open-apis is the new home; the old api-portal.sec.or.th is discontinued on 30 June 2026. Subscription keys carry over. Keys are resolved in this order:

  1. value passed in code
  2. SEC_<PRODUCT_OR_CATEGORY>_KEY env var, e.g. SEC_FUND_KEY, SEC_BOND_KEY, SEC_PVD_KEY, SEC_ONE_REPORT_KEY, SEC_DIGITAL_ASSET_KEY
  3. SEC_API_KEY (shared fallback)
  4. ~/.config/secopendata/keys.toml
export SEC_FUNDFACTSHEET_KEY="your-factsheet-key"
export SEC_FUNDDAILYINFO_KEY="your-nav-key"
export SEC_FUND_KEY="your-v2-fund-key"

Or ~/.config/secopendata/keys.toml:

[keys]
FundFactsheet = "your-factsheet-key"
FundDailyInfo = "your-nav-key"

Keys are never committed (keys.toml and .env are git-ignored).

CLI

secopendata products                                   # registered products
secopendata categories                                 # current portal key scopes
secopendata catalog-info                               # bundled snapshot metadata
secopendata amc-search "กสิกร"                         # offline AMC lookup; no key
secopendata fund-search "K-CHANGE" --status Registered # offline fund lookup; no key
secopendata amcs                                       # all AMCs (id + name)
secopendata funds --amc KASIKORN                       # funds of an AMC
secopendata nav --proj-id <id> --date 2026-06-16       # daily NAV
secopendata nav --amc KASIKORN --abbr <ABBR> --date 2026-06-16
secopendata fund-info --proj-id <id> --top5 <period>   # policy + classes + top5
secopendata get --product <Name> --path <path> --param key=value [--paginate]  # legacy
secopendata request --method GET --path /v1/one-report/fs/2021/financial_statement/C0000000013
secopendata request --method GET --path /v2/fund/factsheet/performance --cursor-paginate
secopendata request --method GET --path /v2/fund/factsheet/benchmarks --cursor-paginate
secopendata request --method POST --path /v1/digital-asset/profile/intermediary --json '{"IntermediaryName":""}'

All subcommands print JSON. Exit codes: 3 missing key, 4 not subscribed, 5 other API error.

amc-search and fund-search use the bundled compact SEC profile snapshot and do not make a network request. Run catalog-info to check its as-of date before relying on current registration status.

Library

from secopendata import SECClient, FundFactsheet, FundDailyInfo

client = SECClient()
ff = FundFactsheet(client)
amc_id = ff.resolve_amc_id("KASIKORN")
funds = ff.funds_by_amc(amc_id)

nav = FundDailyInfo(client).daily_nav(funds[0]["proj_id"], "2026-06-16")

# Any other product:
data = client.get("SomeOtherProduct", "some/path", params={"q": "x"})
one_report = client.request(
    "GET",
    "/v1/one-report/fs/2021/financial_statement/C0000000013",
)
digital_asset = client.request(
    "POST",
    "/v1/digital-asset/profile/intermediary",
    json_body={"IntermediaryName": ""},
)

for row in client.request_cursor_paginated("/v2/fund/factsheet/performance"):
    ...

The client handles the subscription-key header, a sliding-window rate limiter (default 5000 calls / 300 s with >=16 ms between requests, matching the current SEC portal), retries throttle/5xx responses with Retry-After (the SEC gateway throttles with HTTP 421), and treats 204 / empty 200 as "no data".

Equity active/passive study

The scripts/ folder includes resumable research utilities for SEC Fund v2 data. They read SEC_FUND_KEY from the environment and write local artifacts under outputs/, which is intentionally git-ignored.

export SEC_FUND_KEY="your-v2-fund-key"

# 1) Export the full fund profile universe once.
python scripts/sec_fund_research_export.py profiles \
  --out outputs/sec-active-passive/raw

# 2a) Build the latest factsheet active-vs-passive study for domestic Thai equity funds.
python scripts/sec_active_passive_study.py all \
  --raw-dir outputs/sec-active-passive/raw \
  --out outputs/sec-active-passive/study

# 2b) Or include every equity fund, including foreign/feeder/global equity funds.
python scripts/sec_active_passive_study.py all \
  --raw-dir outputs/sec-active-passive/raw \
  --out outputs/sec-active-passive/study-all-equity \
  --include-foreign-equity

By default the study filters policy_desc = "ตราสารทุน" and invest_country_flag = 3. Add --include-foreign-equity to include every fund where policy_desc = "ตราสารทุน" across all invest_country_flag values. It classifies active/passive from management_style, and compares SEC factsheet ผลตอบแทนกองทุนรวม against ผลตอบแทนตัวชี้วัด for 1Y/3Y/5Y/10Y. Outputs include latest_return_pairs.csv, summary_by_horizon_style.csv, and a QuantSeras-style HTML report under the selected --out directory.

Gateway host

Defaults to https://api.sec.or.th. If the SEC moves the gateway during the 2026 portal migration, override it without touching code:

export SEC_API_BASE_URL="https://new-host.sec.or.th"

Use as a Codex plugin

This repo is also a Codex plugin (.codex-plugin/plugin.json) with a sec-open-api skill. The skill includes endpoint references for bond, fund, PVD, digital asset, license, and One Report APIs.

codex plugin marketplace add nutdnuy/sec-opendata-th

Then enable/install sec-opendata from the Codex plugin UI. The repo includes .agents/plugins/marketplace.json so Codex can read it as a marketplace, and .codex-plugin/plugin.json so the plugin root is valid.

Use as a Claude Code plugin

The repo is a Claude marketplace and plugin. Add this GitHub repository as a marketplace in Claude Code:

/plugin marketplace add https://github.com/nutdnuy/sec-opendata-th
/plugin install sec-opendata

The Claude UI "Add marketplace" field should use the same GitHub URL: https://github.com/nutdnuy/sec-opendata-th. The marketplace manifest lives at .claude-plugin/marketplace.json, and the plugin manifest lives at .claude-plugin/plugin.json.

Then in Claude Code:

  • /sec-categories
  • /sec-fund-search K-CHANGE
  • /sec-request GET /v1/one-report/fs/2021/financial_statement/C0000000013
  • /sec-funds KASIKORN
  • /sec-nav <proj_id> 2026-06-16
  • /sec-fund-info <proj_id> <top5_period>

Or just ask in natural language — the sec-open-api skill triggers on Thai SEC Open Data requests and runs the CLI for you.

Develop

pip install -e ".[dev]"
pytest          # fully mocked — no live subscription key needed

License

MIT

About

Python client + Claude Code plugin for the Thai SEC OpenAPI (api.sec.or.th): fund factsheets, daily NAV, and any SEC API product.

Resources

License

Stars

8 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages