settfex 0.5.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {settfex-0.5.0 → settfex-0.7.0}/.gitignore +1 -1
- {settfex-0.5.0 → settfex-0.7.0}/CHANGELOG.md +35 -0
- settfex-0.7.0/CLAUDE.md +234 -0
- {settfex-0.5.0 → settfex-0.7.0}/PKG-INFO +39 -2
- {settfex-0.5.0 → settfex-0.7.0}/README.md +35 -1
- settfex-0.7.0/docs/settfex/services/set/chart_quotation.md +191 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/earnings_call.md +38 -4
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/12_earnings_call.ipynb +33 -0
- settfex-0.7.0/examples/set/13_chart_quotation.ipynb +221 -0
- {settfex-0.5.0 → settfex-0.7.0}/pyproject.toml +18 -4
- {settfex-0.5.0 → settfex-0.7.0}/settfex/__init__.py +1 -1
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/__init__.py +19 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/earnings_call.py +102 -1
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/__init__.py +2 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/chart_quotation.py +108 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/stock.py +34 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/__init__.py +2 -0
- settfex-0.7.0/settfex/utils/youtube_transcript.py +83 -0
- settfex-0.7.0/tests/services/set/stock/__init__.py +0 -0
- settfex-0.7.0/tests/services/set/stock/test_chart_quotation.py +405 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_earnings_call.py +70 -0
- settfex-0.7.0/tests/utils/test_youtube_transcript.py +66 -0
- {settfex-0.5.0 → settfex-0.7.0}/uv.lock +23 -2
- {settfex-0.5.0 → settfex-0.7.0}/.editorconfig +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/copilot-instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/dependabot.yml +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/instructions/core-architectrual-principles.instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/instructions/documentation-standards.instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/instructions/file-organization.instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/instructions/git-commit.instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/instructions/python-dependency-management.instructions.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/prompts/Coding.prompt.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/prompts/Git-Commit-Reviewer.prompt.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/prompts/Prompt-Engineer.prompt.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/prompts/Python-Architect.prompt.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/workflows/ci.yml +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/workflows/release.yml +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.github/workflows/security.yml +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.pre-commit-config.yaml +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/.python-version +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/COMPREHENSIVE_AUDIT.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/CONTRIBUTING.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/LICENSE +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/MANIFEST.in +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/RELEASING.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/SECURITY.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/guide/PYTHON_LIBRARY_BEST_PRACTICES.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/index.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/API_PROTECTION_NOTE.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/board_of_director.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/corporate_action.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/financial.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/highlight_data.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/list.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/nvdr_holder.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/price_performance.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/profile_company.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/profile_stock.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/shareholder.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/set/trading_stat.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/tfex/list.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/services/tfex/trading_statistics.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/utils/data_fetcher.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/settfex/utils/session_caching.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/solution/FINDINGS.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/solution/Performance Boost Session Caching Implementation.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/solution/SESSION_CACHE_SUMMARY.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/docs/solution/SOLUTION_100_PERCENT.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/README.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/01_stock_list.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/02_highlight_data.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/03_stock_profile.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/04_company_profile.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/05_corporate_action.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/06_shareholder.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/07_nvdr_holder.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/08_board_of_director.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/09_trading_statistics.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/10_price_performance.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/11_financial.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/README.md +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/cpall_balance_sheet.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/market_dashboard_20251005_173221.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/market_dashboard_20251005_210737.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/market_dashboard_20251006_091440.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/thailand_banking_stocks.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/thailand_set_main_board.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/set/thailand_stock_universe.csv +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/tfex/01_series_list.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/tfex/02_trading_statistics.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/examples/tfex/03_underlying_price.ipynb +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/py.typed +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/constants.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/list.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/board_of_director.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/corporate_action.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/financial/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/financial/financial.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/highlight_data.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/latest_historical_trading.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/nvdr_holder.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/price_performance.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/profile_company.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/profile_stock.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/shareholder.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/trading_stat.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/set/stock/utils.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/tfex/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/tfex/constants.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/tfex/list.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/tfex/trading_statistics.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/services/tfex/underlying_price.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/data_fetcher.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/http.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/logging.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/parsing.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/session_cache.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/settfex/utils/session_manager.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/conftest.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/stock/financial/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/stock/financial/test_financial.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_board_of_director.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_client.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_corporate_action.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_historical.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_realtime.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/set/test_shareholder.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_client.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_historical.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_list.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_realtime.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_trading_statistics.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/services/tfex/test_underlying_price.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/__init__.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_data_fetcher.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_formatting.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_http.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_logging.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_parsing.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_session_manager.py +0 -0
- {settfex-0.5.0 → settfex-0.7.0}/tests/utils/test_validation.py +0 -0
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-06-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Latest traded price for SET stocks** — on top of the existing chart-quotation service, a
|
|
15
|
+
first-class way to get the most recent *traded* price relative to now. The SET intraday feed
|
|
16
|
+
pre-populates the rest of the session with null/no-trade buckets; these are excluded
|
|
17
|
+
automatically.
|
|
18
|
+
- `get_latest_price(symbol, period="1D", accumulated=False, as_of=None) -> Quotation | None` —
|
|
19
|
+
top-level convenience returning the latest traded quotation (time, price, volume, change), or
|
|
20
|
+
`None` if nothing has traded yet.
|
|
21
|
+
- `ChartQuotation.get_latest_quotation(as_of=None) -> Quotation | None` and
|
|
22
|
+
`ChartQuotation.get_latest_price(as_of=None) -> float | None` (scalar, falls back to `prior`) —
|
|
23
|
+
pure, timezone-safe selection in Asia/Bangkok; `as_of` defaults to now (naive values are
|
|
24
|
+
treated as Bangkok local time).
|
|
25
|
+
- `Stock.get_latest_price(period="1D", accumulated=False, as_of=None)` on the unified Stock class.
|
|
26
|
+
- Hyphenated warrant symbols (e.g. `JAS-W4`) are preserved.
|
|
27
|
+
- The chart-quotation models, service, and `get_chart_quotation` are now also exported from
|
|
28
|
+
`settfex.services.set` (previously only from `settfex.services.set.stock`).
|
|
29
|
+
|
|
30
|
+
## [0.6.0] - 2026-06-19
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **Thai YouTube transcripts for earnings calls** (raw text for AI/LLM use), behind a new optional
|
|
35
|
+
`transcript` extra (`pip install "settfex[transcript]"`, backed by `youtube-transcript-api`):
|
|
36
|
+
- `fetch_youtube_transcript(video_id, *, languages=("th",), proxies=None) -> str | None` — a
|
|
37
|
+
generic async wrapper that returns the caption text as one string, or `None` when the video has
|
|
38
|
+
no matching captions / they're disabled / the request is blocked (never raises for those).
|
|
39
|
+
- `fetch_transcripts(items, ...) -> list[EarningsCallItem]` — fills `EarningsCallItem.transcript`
|
|
40
|
+
for every item that has a YouTube video (bounded concurrency, default 3; optional progress bar;
|
|
41
|
+
per-item tolerant; items without a video are skipped).
|
|
42
|
+
- `get_earnings_call_transcript(id, ...) -> str | None` — one presentation's transcript by id.
|
|
43
|
+
- New `EarningsCallItem.transcript: str | None` field (populated only by the above).
|
|
44
|
+
|
|
10
45
|
## [0.5.0] - 2026-06-19
|
|
11
46
|
|
|
12
47
|
### Added
|
settfex-0.7.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# CLAUDE.md - AI Assistant Context
|
|
2
|
+
|
|
3
|
+
Essential context and guidelines for AI assistants working on the settfex project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**settfex** is a Python library that fetches real-time and historical data from:
|
|
8
|
+
- **SET** (Stock Exchange of Thailand)
|
|
9
|
+
- **TFEX** (Thailand Futures Exchange)
|
|
10
|
+
|
|
11
|
+
Published on PyPI, targeting Python 3.11+ with modern async patterns.
|
|
12
|
+
|
|
13
|
+
## Project Structure
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
settfex/
|
|
17
|
+
├── settfex/ # Main package
|
|
18
|
+
│ ├── services/ # Business logic and API integrations
|
|
19
|
+
│ │ ├── set/ # SET-specific services (11 services)
|
|
20
|
+
│ │ │ ├── constants.py, list.py
|
|
21
|
+
│ │ │ └── stock/ # Stock services: highlight_data, profile_stock,
|
|
22
|
+
│ │ │ # profile_company, corporate_action, shareholder,
|
|
23
|
+
│ │ │ # nvdr_holder, board_of_director, trading_stat,
|
|
24
|
+
│ │ │ # price_performance, financial/, stock.py, utils.py
|
|
25
|
+
│ │ └── tfex/ # TFEX services: list.py, trading_statistics.py
|
|
26
|
+
│ └── utils/ # http.py, data_fetcher.py, session_manager.py,
|
|
27
|
+
│ # session_cache.py, logging.py
|
|
28
|
+
├── tests/ # Mirror of settfex/ with test_ prefix
|
|
29
|
+
├── docs/ # Service docs, guides, solutions
|
|
30
|
+
├── examples/ # 13 Jupyter notebooks (11 SET + 2 TFEX)
|
|
31
|
+
├── scripts/ # Verification scripts per service
|
|
32
|
+
├── .github/ # CI and agent instructions
|
|
33
|
+
├── pyproject.toml # uv-based config
|
|
34
|
+
└── README.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Architecture Principles
|
|
38
|
+
|
|
39
|
+
1. **Modular Design**: Clear separation between SET and TFEX services
|
|
40
|
+
2. **Service Layer**: All external API interactions encapsulated in `services/`
|
|
41
|
+
3. **Utilities**: Reusable helpers in `utils/` for cross-cutting concerns
|
|
42
|
+
4. **Type Safety**: Full type hints and Pydantic validation throughout
|
|
43
|
+
5. **Modern Python**: Python 3.11+ with async/await patterns
|
|
44
|
+
6. **Testing**: Comprehensive pytest coverage (>80% target)
|
|
45
|
+
7. **Documentation**: Maintained docs for all public APIs
|
|
46
|
+
|
|
47
|
+
## Development Guidelines
|
|
48
|
+
|
|
49
|
+
### Code Style
|
|
50
|
+
- PEP 8 with 100-char line length; Ruff linting; mypy strict mode
|
|
51
|
+
- All functions must have type hints
|
|
52
|
+
|
|
53
|
+
### Dependencies
|
|
54
|
+
- **curl_cffi**: Async HTTP with browser impersonation (replaced httpx 2025-10-01)
|
|
55
|
+
- **loguru**: Structured logging with colored output, rotation, compression (replaced stdlib logging 2025-10-01)
|
|
56
|
+
- **pydantic**: Runtime validation and settings management
|
|
57
|
+
- Minimize external dependencies
|
|
58
|
+
|
|
59
|
+
### Testing
|
|
60
|
+
- Write tests for all new features; mock external API calls
|
|
61
|
+
- Use pytest fixtures in `conftest.py` for shared setup
|
|
62
|
+
- Maintain >80% coverage
|
|
63
|
+
|
|
64
|
+
### Documentation
|
|
65
|
+
- Update docs when adding features; include docstrings for all public APIs
|
|
66
|
+
- Keep Jupyter notebook examples up-to-date
|
|
67
|
+
|
|
68
|
+
## Common Tasks
|
|
69
|
+
|
|
70
|
+
### Adding a New Service (SET or TFEX)
|
|
71
|
+
1. Create module in `settfex/services/{set,tfex}/`
|
|
72
|
+
2. Add tests in `tests/services/{set,tfex}/`
|
|
73
|
+
3. Update the appropriate `__init__.py` to export the service
|
|
74
|
+
4. Document with docstrings + create verification script in `scripts/settfex/services/`
|
|
75
|
+
5. Add Jupyter notebook example in `examples/`
|
|
76
|
+
|
|
77
|
+
### Adding Utility Functions
|
|
78
|
+
1. Add to appropriate module in `settfex/utils/` or create new one
|
|
79
|
+
2. Add tests in `tests/utils/`; ensure utilities are generic and reusable
|
|
80
|
+
|
|
81
|
+
## Service Design Patterns (Must Follow)
|
|
82
|
+
|
|
83
|
+
Every service follows this consistent pattern:
|
|
84
|
+
- **Pydantic models** for all data with full type annotations
|
|
85
|
+
- **Two fetch methods**: `fetch_*()` returns Pydantic models; `fetch_*_raw()` returns raw dicts
|
|
86
|
+
- **Convenience function**: `get_*()` top-level function for one-line access
|
|
87
|
+
- **Dual language**: `en`/`th` support via `normalize_language()` (accepts: en/eng/english, th/tha/thai)
|
|
88
|
+
- **Symbol normalization**: Auto-uppercase via `normalize_symbol()`
|
|
89
|
+
- **SessionManager**: All cookie/bot-detection handled automatically (no manual cookie params)
|
|
90
|
+
- **Async-first**: All I/O uses async/await via `AsyncDataFetcher`
|
|
91
|
+
- **Bot bypass**: Symbol-specific referer header + SessionManager cookies (Incapsula bypass)
|
|
92
|
+
|
|
93
|
+
Typical usage:
|
|
94
|
+
```python
|
|
95
|
+
from settfex.services.set import Stock, get_highlight_data, get_stock_list
|
|
96
|
+
|
|
97
|
+
stock = Stock("CPALL")
|
|
98
|
+
data = await stock.get_highlight_data() # via unified Stock class
|
|
99
|
+
data = await get_highlight_data("CPALL") # or convenience function
|
|
100
|
+
all_stocks = await get_stock_list() # no cookie params needed
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Services Inventory (16 total)
|
|
104
|
+
|
|
105
|
+
### SET Services (14)
|
|
106
|
+
|
|
107
|
+
| # | Service | Module | Endpoint Pattern | Key Data |
|
|
108
|
+
|---|---|---|---|---|
|
|
109
|
+
| 1 | Stock List | `list.py` | `/api/set/stock/list` | All SET/MAI stocks, filter by market/industry/symbol |
|
|
110
|
+
| 2 | Highlight Data | `stock/highlight_data.py` | `/api/set/stock/{sym}/highlight-data` | P/E, P/B, market cap, beta, dividends, 52-wk range, NVDR |
|
|
111
|
+
| 3 | Stock Profile | `stock/profile_stock.py` | `/api/set/stock/{sym}/profile` | Listing details, IPO, sector, foreign limits, ISIN, warrants |
|
|
112
|
+
| 4 | Company Profile | `stock/profile_company.py` | `/api/set/company/{sym}/profile` | ESG rating, CG score, auditors, management, capital structure |
|
|
113
|
+
| 5 | Corporate Actions | `stock/corporate_action.py` | `/api/set/stock/{sym}/corporate-action` | Dividends (XD), meetings (XM/AGM/EGM), payment dates |
|
|
114
|
+
| 6 | Shareholders | `stock/shareholder.py` | `/api/set/stock/{sym}/shareholder` | Major holders, free float %, ownership distribution |
|
|
115
|
+
| 7 | NVDR Holders | `stock/nvdr_holder.py` | `/api/set/stock/{sym}/nvdr-holder` | NVDR ownership, Thai vs foreign holders |
|
|
116
|
+
| 8 | Board of Directors | `stock/board_of_director.py` | `/api/set/company/{sym}/board-of-director` | Directors, positions (Chairman, CEO, Independent) |
|
|
117
|
+
| 9 | Trading Statistics | `stock/trading_stat.py` | `/api/set/factsheet/{sym}/trading-stat` | 30+ fields: price/volume/valuation/beta, 5 periods (YTD-1Y) |
|
|
118
|
+
| 10 | Price Performance | `stock/price_performance.py` | `/api/set/factsheet/{sym}/price-performance` | Stock vs sector vs market (5D/1M/3M/6M/YTD), P/E, P/B |
|
|
119
|
+
| 11 | Financial Statements | `stock/financial/financial.py` | `/api/set/factsheet/{sym}/financialstatement` | Balance sheet, income, cash flow (multi-period, en/th) |
|
|
120
|
+
| 12 | Earnings Call (Opportunity Day) | `earnings_call.py` | `POST api.lcp.setgroup.or.th/.../investor/search/archive` (+ `GET /investor/vdo/{id}`, `/investor/filter/*`) | OPPDAY calendar (symbol, company, date, clip duration, YouTube URL); concurrent `fetch_all`/`get_all_earnings_calls` (+ optional `tqdm` progress); detail-by-id (`get_earnings_call_detail`); 7 filter helpers; pandas `to_dataframe()`; **Thai YouTube transcripts** for AI (`fetch_transcripts` / `get_earnings_call_transcript` / `fetch_youtube_transcript`, `EarningsCallItem.transcript`); stateless host (no SessionManager); optional extras: `dataframe` (pandas) / `progress` (tqdm) / `transcript` (youtube-transcript-api) |
|
|
121
|
+
| 13 | Chart Quotation / Latest Price | `stock/chart_quotation.py` | `/api/set/stock/{sym}/chart-quotation` | Intraday/historical per-minute series (price/volume/value/%chg, intermissions, prior close); **latest *traded* price relative to now** — `get_latest_price()` (→ `Quotation`), model `get_latest_quotation()`/`get_latest_price()` (→ float, `prior` fallback); skips null future/lunch/no-trade buckets; Asia/Bangkok tz-safe `as_of`; hyphen-safe symbols (`JAS-W4`) |
|
|
122
|
+
| 14 | Latest Historical Trading | `stock/latest_historical_trading.py` | `/api/set/stock/{sym}/latest-historical-trading` | Latest trading-day summary: OHLCV, change/%change, and valuation metrics |
|
|
123
|
+
|
|
124
|
+
### TFEX Services (2)
|
|
125
|
+
|
|
126
|
+
| # | Service | Module | Endpoint Pattern | Key Data |
|
|
127
|
+
|---|---|---|---|---|
|
|
128
|
+
| 1 | Series List | `list.py` | `/api/set/tfex/series/list` | Futures/options, 8 filter methods, contract details |
|
|
129
|
+
| 2 | Trading Statistics | `trading_statistics.py` | `/api/set/tfex/series/{sym}/trading-statistics` | Settlement, margin (IM/MM), theoretical price, days to maturity |
|
|
130
|
+
|
|
131
|
+
### Unified Stock Class (`stock/stock.py`)
|
|
132
|
+
Single entry point for SET stock data — initialize with symbol, access all services via lazy-init properties:
|
|
133
|
+
```python
|
|
134
|
+
stock = Stock("CPALL")
|
|
135
|
+
highlight = await stock.get_highlight_data()
|
|
136
|
+
profile = await stock.get_profile()
|
|
137
|
+
financials = await stock.get_balance_sheet()
|
|
138
|
+
latest = await stock.get_latest_price() # latest traded price vs now
|
|
139
|
+
# ... all stock services accessible
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API Design Principles
|
|
143
|
+
|
|
144
|
+
1. **Consistency**: SET and TFEX services follow identical patterns
|
|
145
|
+
2. **Simplicity**: Simple, intuitive APIs; one-line convenience functions
|
|
146
|
+
3. **Async-first**: async/await for all I/O operations
|
|
147
|
+
4. **Error Handling**: Clear, informative error messages
|
|
148
|
+
5. **Validation**: Pydantic models for all inputs and outputs
|
|
149
|
+
6. **Documentation**: All public APIs well-documented
|
|
150
|
+
|
|
151
|
+
## Key Technical Decisions
|
|
152
|
+
|
|
153
|
+
| Area | Choice | Reason |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| HTTP client | `curl_cffi` | Browser impersonation for bot detection bypass |
|
|
156
|
+
| Logging | `loguru` | Colored output, auto-rotation, better exception traces |
|
|
157
|
+
| Validation | Pydantic | Full type safety with runtime validation |
|
|
158
|
+
| Async | `asyncio.to_thread` | Wraps sync curl_cffi for async compatibility |
|
|
159
|
+
| Session mgmt | `SessionManager` | 25x speedup via cookie warming + caching |
|
|
160
|
+
| Build | `uv` (pyproject.toml) | Fast dependency resolution |
|
|
161
|
+
| Lint | Ruff + mypy strict | Modern, fast tooling |
|
|
162
|
+
|
|
163
|
+
## Target Users
|
|
164
|
+
|
|
165
|
+
- Python developers building trading applications
|
|
166
|
+
- Financial analysts needing Thailand market data
|
|
167
|
+
- Quantitative researchers and data scientists
|
|
168
|
+
- Automated trading system developers
|
|
169
|
+
|
|
170
|
+
## Important Notes
|
|
171
|
+
|
|
172
|
+
- This library is **not officially affiliated** with SET or TFEX
|
|
173
|
+
- Always respect API rate limits and terms of service
|
|
174
|
+
- Handle sensitive data (API keys, credentials) securely
|
|
175
|
+
- Never commit credentials or API keys to version control
|
|
176
|
+
|
|
177
|
+
## Recent Change History (Condensed)
|
|
178
|
+
|
|
179
|
+
### 2025-10-05
|
|
180
|
+
- **Jupyter Examples**: 13 notebooks covering all services with beginner guides, professional use cases, and CSV/pandas export
|
|
181
|
+
- **TFEX Trading Statistics**: Settlement prices, margin (IM/MM), days to maturity, theoretical pricing
|
|
182
|
+
- **TFEX Series List**: Complete futures/options discovery with 8 filter methods
|
|
183
|
+
- **Financial Service**: Balance sheet, income statement, cash flow — multi-period with en/th support, 98% test coverage
|
|
184
|
+
|
|
185
|
+
### 2025-10-04
|
|
186
|
+
- **Price Performance**: Stock vs sector vs market across 5 periods (5D–YTD), P/E, P/B, turnover
|
|
187
|
+
- **Trading Statistics (SET)**: 30+ fields across YTD/1M/3M/6M/1Y periods — price, volume, valuation, beta
|
|
188
|
+
|
|
189
|
+
### 2025-10-03
|
|
190
|
+
- **Board of Directors**: Director names, positions, en/th support
|
|
191
|
+
- **NVDR Holders**: NVDR ownership data with Thai/foreign holder identification
|
|
192
|
+
- **Corporate Actions**: Dividends (XD) and meetings (XM) with full detail
|
|
193
|
+
- **Company Profile**: ESG ratings, CG scores, management, auditors, capital structure
|
|
194
|
+
- **Shareholders**: Major holders, free float, ownership distribution
|
|
195
|
+
- **Refactoring**: Removed legacy cookie generation (~200 lines); SessionManager now handles all bot detection
|
|
196
|
+
|
|
197
|
+
### 2025-10-02
|
|
198
|
+
- **Stock Profile**: 30+ fields — listing, IPO, sector, foreign limits, ISIN, warrants
|
|
199
|
+
- **Highlight Data**: P/E, P/B, market cap, dividends, NVDR, 52-week range
|
|
200
|
+
- **Unified Stock class**: Single entry point for all stock services with lazy init
|
|
201
|
+
- **Stock utilities**: `normalize_symbol()`, `normalize_language()` shared across all services
|
|
202
|
+
|
|
203
|
+
### 2025-10-01
|
|
204
|
+
- **Stock List Service**: Full SET/MAI stock list with market/industry/symbol filtering
|
|
205
|
+
- **AsyncDataFetcher**: Core HTTP client with Unicode/Thai support, retry logic, bot impersonation
|
|
206
|
+
- **Migration**: httpx → curl_cffi (bot bypass), stdlib logging → loguru
|
|
207
|
+
- Established the service architecture pattern used by all subsequent services
|
|
208
|
+
|
|
209
|
+
## Future Enhancements (Ideas)
|
|
210
|
+
|
|
211
|
+
- WebSocket support for real-time streaming
|
|
212
|
+
- Data caching mechanisms, rate limiting
|
|
213
|
+
- CLI tool for quick data queries
|
|
214
|
+
- pandas/polars integration
|
|
215
|
+
- Historical data export (CSV, Parquet)
|
|
216
|
+
|
|
217
|
+
## When Working on This Project
|
|
218
|
+
|
|
219
|
+
1. **Read First**: Check existing code patterns before implementing
|
|
220
|
+
2. **Test**: Write tests before or alongside code
|
|
221
|
+
3. **Document**: Update docs when adding features
|
|
222
|
+
4. **Consistency**: Follow existing patterns and naming conventions
|
|
223
|
+
5. **Type Safety**: Always use type hints
|
|
224
|
+
6. **Ask Questions**: If unclear about architecture, ask for clarification
|
|
225
|
+
|
|
226
|
+
## Contact & Resources
|
|
227
|
+
|
|
228
|
+
- Documentation: `docs/` directory
|
|
229
|
+
- Issues: GitHub Issues
|
|
230
|
+
- License: MIT
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
*This file should be kept up-to-date as the project evolves.*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: settfex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Async Python library for fetching real-time and historical data from the Stock Exchange of Thailand (SET) and Thailand Futures Exchange (TFEX)
|
|
5
5
|
Project-URL: Homepage, https://github.com/lumduan/settfex
|
|
6
6
|
Project-URL: Documentation, https://github.com/lumduan/settfex#readme
|
|
@@ -38,8 +38,11 @@ Requires-Dist: matplotlib>=3.7.0; extra == 'examples'
|
|
|
38
38
|
Requires-Dist: notebook>=7.0.0; extra == 'examples'
|
|
39
39
|
Requires-Dist: pandas>=2.0.0; extra == 'examples'
|
|
40
40
|
Requires-Dist: tqdm>=4.66.0; extra == 'examples'
|
|
41
|
+
Requires-Dist: youtube-transcript-api>=1.0.0; extra == 'examples'
|
|
41
42
|
Provides-Extra: progress
|
|
42
43
|
Requires-Dist: tqdm>=4.66.0; extra == 'progress'
|
|
44
|
+
Provides-Extra: transcript
|
|
45
|
+
Requires-Dist: youtube-transcript-api>=1.0.0; extra == 'transcript'
|
|
43
46
|
Description-Content-Type: text/markdown
|
|
44
47
|
|
|
45
48
|
# settfex
|
|
@@ -98,6 +101,8 @@ All examples include beginner explanations, professional trading use cases, and
|
|
|
98
101
|
9. **[Trading Statistics](examples/set/09_trading_statistics.ipynb)** - Multi-period performance and volatility
|
|
99
102
|
10. **[Price Performance](examples/set/10_price_performance.ipynb)** - Sector comparison and alpha calculation
|
|
100
103
|
11. **[Financial Statements](examples/set/11_financial.ipynb)** - Balance sheet, income, cash flow analysis
|
|
104
|
+
12. **[Earnings Call (Opportunity Day)](examples/set/12_earnings_call.ipynb)** - OPPDAY calendar, YouTube links, Thai transcripts for AI
|
|
105
|
+
13. **[Chart Quotation & Latest Price](examples/set/13_chart_quotation.ipynb)** - Intraday series and the latest traded price relative to now
|
|
101
106
|
|
|
102
107
|
### 📈 TFEX Examples (Thailand Futures Exchange)
|
|
103
108
|
|
|
@@ -125,7 +130,7 @@ Want to dig deeper? Check out our detailed guides:
|
|
|
125
130
|
- **[Trading Statistics Service](docs/settfex/services/set/trading_stat.md)** - Historical trading performance and metrics
|
|
126
131
|
- **[Price Performance Service](docs/settfex/services/set/price_performance.md)** - Stock, sector, and market price performance comparison
|
|
127
132
|
- **[Financial Service](docs/settfex/services/set/financial.md)** - Balance sheet, income statement, and cash flow data
|
|
128
|
-
- **[Chart Quotation Service](docs/settfex/services/set/chart_quotation.md)** - Intraday
|
|
133
|
+
- **[Chart Quotation Service](docs/settfex/services/set/chart_quotation.md)** - Intraday/historical price chart series, plus the latest *traded* price relative to now
|
|
129
134
|
- **[Latest Historical Trading Service](docs/settfex/services/set/latest_historical_trading.md)** - Latest trading day summary with OHLCV and valuation metrics
|
|
130
135
|
- **[Earnings Call (Opportunity Day) Service](docs/settfex/services/set/earnings_call.md)** - OPPDAY earnings-call calendar with YouTube links, as models or a DataFrame
|
|
131
136
|
|
|
@@ -354,6 +359,27 @@ print(f"Market ({data.market.symbol}): {data.market.ytd_percent_change:+.2f}%")
|
|
|
354
359
|
|
|
355
360
|
---
|
|
356
361
|
|
|
362
|
+
#### ⏱️ Get the Latest Traded Price (Intraday)
|
|
363
|
+
|
|
364
|
+
Fetch the intraday chart-quotation series, or jump straight to the latest *traded* price relative to now (the null future/lunch/no-trade buckets are excluded automatically):
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
from settfex.services.set import get_latest_price, get_chart_quotation
|
|
368
|
+
|
|
369
|
+
# The latest TRADED quotation right now (or None if nothing has traded yet)
|
|
370
|
+
quote = await get_latest_price("CPALL")
|
|
371
|
+
if quote:
|
|
372
|
+
print(f"{quote.local_datetime}: {quote.price} (vol {quote.volume:,.0f})")
|
|
373
|
+
|
|
374
|
+
# Or work with the full series; the model also exposes a prior-fallback scalar
|
|
375
|
+
data = await get_chart_quotation("CPALL", period="1D")
|
|
376
|
+
print(f"Prior close: {data.prior}, latest price: {data.get_latest_price()}")
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**👉 [Learn more about Chart Quotation & Latest Price](docs/settfex/services/set/chart_quotation.md)**
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
357
383
|
#### 💰 Get Financial Statements
|
|
358
384
|
|
|
359
385
|
Fetch comprehensive financial data including balance sheet, income statement, and cash flow:
|
|
@@ -448,8 +474,19 @@ hann = await get_earnings_calls(keyword="HANN")
|
|
|
448
474
|
# (pip install "settfex[progress]"):
|
|
449
475
|
from settfex.services.set import get_all_earnings_calls
|
|
450
476
|
everything = await get_all_earnings_calls(progress=True) # ~9520 records in ~15s (~10x faster)
|
|
477
|
+
|
|
478
|
+
# Thai subtitles as raw text for AI/LLM use (pip install "settfex[transcript]"):
|
|
479
|
+
from settfex.services.set import fetch_transcripts, get_earnings_call_transcript
|
|
480
|
+
await fetch_transcripts(hann.items) # fills item.transcript (Thai) per video
|
|
481
|
+
text = await get_earnings_call_transcript(6319) # …or one presentation's transcript by id
|
|
451
482
|
```
|
|
452
483
|
|
|
484
|
+
> ⚠️ **Transcripts & YouTube limits.** YouTube rate-limits / IP-blocks aggressively (especially
|
|
485
|
+
> from cloud servers), so transcript fetching is built for a **filtered** set (a company /
|
|
486
|
+
> quarter) — not the full ~9,520-video archive — and defaults to low concurrency (3). A
|
|
487
|
+
> blocked/missing/disabled transcript simply comes back as `None`; if your host IP is blocked,
|
|
488
|
+
> pass `proxies={"http": ..., "https": ...}`. Results can vary by IP.
|
|
489
|
+
|
|
453
490
|
**👉 [Learn more about the Earnings Call Service](docs/settfex/services/set/earnings_call.md)**
|
|
454
491
|
|
|
455
492
|
---
|
|
@@ -54,6 +54,8 @@ All examples include beginner explanations, professional trading use cases, and
|
|
|
54
54
|
9. **[Trading Statistics](examples/set/09_trading_statistics.ipynb)** - Multi-period performance and volatility
|
|
55
55
|
10. **[Price Performance](examples/set/10_price_performance.ipynb)** - Sector comparison and alpha calculation
|
|
56
56
|
11. **[Financial Statements](examples/set/11_financial.ipynb)** - Balance sheet, income, cash flow analysis
|
|
57
|
+
12. **[Earnings Call (Opportunity Day)](examples/set/12_earnings_call.ipynb)** - OPPDAY calendar, YouTube links, Thai transcripts for AI
|
|
58
|
+
13. **[Chart Quotation & Latest Price](examples/set/13_chart_quotation.ipynb)** - Intraday series and the latest traded price relative to now
|
|
57
59
|
|
|
58
60
|
### 📈 TFEX Examples (Thailand Futures Exchange)
|
|
59
61
|
|
|
@@ -81,7 +83,7 @@ Want to dig deeper? Check out our detailed guides:
|
|
|
81
83
|
- **[Trading Statistics Service](docs/settfex/services/set/trading_stat.md)** - Historical trading performance and metrics
|
|
82
84
|
- **[Price Performance Service](docs/settfex/services/set/price_performance.md)** - Stock, sector, and market price performance comparison
|
|
83
85
|
- **[Financial Service](docs/settfex/services/set/financial.md)** - Balance sheet, income statement, and cash flow data
|
|
84
|
-
- **[Chart Quotation Service](docs/settfex/services/set/chart_quotation.md)** - Intraday
|
|
86
|
+
- **[Chart Quotation Service](docs/settfex/services/set/chart_quotation.md)** - Intraday/historical price chart series, plus the latest *traded* price relative to now
|
|
85
87
|
- **[Latest Historical Trading Service](docs/settfex/services/set/latest_historical_trading.md)** - Latest trading day summary with OHLCV and valuation metrics
|
|
86
88
|
- **[Earnings Call (Opportunity Day) Service](docs/settfex/services/set/earnings_call.md)** - OPPDAY earnings-call calendar with YouTube links, as models or a DataFrame
|
|
87
89
|
|
|
@@ -310,6 +312,27 @@ print(f"Market ({data.market.symbol}): {data.market.ytd_percent_change:+.2f}%")
|
|
|
310
312
|
|
|
311
313
|
---
|
|
312
314
|
|
|
315
|
+
#### ⏱️ Get the Latest Traded Price (Intraday)
|
|
316
|
+
|
|
317
|
+
Fetch the intraday chart-quotation series, or jump straight to the latest *traded* price relative to now (the null future/lunch/no-trade buckets are excluded automatically):
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from settfex.services.set import get_latest_price, get_chart_quotation
|
|
321
|
+
|
|
322
|
+
# The latest TRADED quotation right now (or None if nothing has traded yet)
|
|
323
|
+
quote = await get_latest_price("CPALL")
|
|
324
|
+
if quote:
|
|
325
|
+
print(f"{quote.local_datetime}: {quote.price} (vol {quote.volume:,.0f})")
|
|
326
|
+
|
|
327
|
+
# Or work with the full series; the model also exposes a prior-fallback scalar
|
|
328
|
+
data = await get_chart_quotation("CPALL", period="1D")
|
|
329
|
+
print(f"Prior close: {data.prior}, latest price: {data.get_latest_price()}")
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**👉 [Learn more about Chart Quotation & Latest Price](docs/settfex/services/set/chart_quotation.md)**
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
313
336
|
#### 💰 Get Financial Statements
|
|
314
337
|
|
|
315
338
|
Fetch comprehensive financial data including balance sheet, income statement, and cash flow:
|
|
@@ -404,8 +427,19 @@ hann = await get_earnings_calls(keyword="HANN")
|
|
|
404
427
|
# (pip install "settfex[progress]"):
|
|
405
428
|
from settfex.services.set import get_all_earnings_calls
|
|
406
429
|
everything = await get_all_earnings_calls(progress=True) # ~9520 records in ~15s (~10x faster)
|
|
430
|
+
|
|
431
|
+
# Thai subtitles as raw text for AI/LLM use (pip install "settfex[transcript]"):
|
|
432
|
+
from settfex.services.set import fetch_transcripts, get_earnings_call_transcript
|
|
433
|
+
await fetch_transcripts(hann.items) # fills item.transcript (Thai) per video
|
|
434
|
+
text = await get_earnings_call_transcript(6319) # …or one presentation's transcript by id
|
|
407
435
|
```
|
|
408
436
|
|
|
437
|
+
> ⚠️ **Transcripts & YouTube limits.** YouTube rate-limits / IP-blocks aggressively (especially
|
|
438
|
+
> from cloud servers), so transcript fetching is built for a **filtered** set (a company /
|
|
439
|
+
> quarter) — not the full ~9,520-video archive — and defaults to low concurrency (3). A
|
|
440
|
+
> blocked/missing/disabled transcript simply comes back as `None`; if your host IP is blocked,
|
|
441
|
+
> pass `proxies={"http": ..., "https": ...}`. Results can vary by IP.
|
|
442
|
+
|
|
409
443
|
**👉 [Learn more about the Earnings Call Service](docs/settfex/services/set/earnings_call.md)**
|
|
410
444
|
|
|
411
445
|
---
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Stock Chart Quotation Service
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Stock Chart Quotation Service fetches the **intraday (and historical) chart-quotation feed**
|
|
6
|
+
for an individual SET stock symbol — the per-interval price / volume / value / change series shown
|
|
7
|
+
on the SET price chart. On top of that raw series it provides a first-class way to retrieve the
|
|
8
|
+
**latest *traded* price relative to now**, correctly excluding the null-valued buckets the API
|
|
9
|
+
pre-populates for the remainder of the trading day.
|
|
10
|
+
|
|
11
|
+
Endpoint: `GET /api/set/stock/{symbol}/chart-quotation?period=1D&accumulated=false`
|
|
12
|
+
|
|
13
|
+
## Key Features
|
|
14
|
+
|
|
15
|
+
- **Intraday + historical** — `period` of `1D`, `5D`, `1M`, `3M`, `6M`, `1Y`, `3Y`, `5Y`, `MAX`.
|
|
16
|
+
- **Latest traded price vs. now** — `get_latest_price()` returns the most recent quotation that
|
|
17
|
+
actually traded (non-null `volume`), skipping future / lunch-break / no-trade buckets.
|
|
18
|
+
- **Timezone-correct** — quotation timestamps are tz-aware (`+07:00`); `as_of` inputs (naive or any
|
|
19
|
+
zone) are normalized to Asia/Bangkok before comparison.
|
|
20
|
+
- **Hyphen-safe symbols** — warrant symbols like `JAS-W4` are preserved (only upper-cased/trimmed).
|
|
21
|
+
- **Type-safe** — full Pydantic models with camelCase→snake_case aliases.
|
|
22
|
+
- **Bot-detection handled** — `SessionManager` warms cookies and builds the symbol-specific referer
|
|
23
|
+
automatically (Incapsula bypass); no manual cookie params.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install settfex
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### Latest traded price (the headline capability)
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import asyncio
|
|
37
|
+
from settfex.services.set import get_latest_price
|
|
38
|
+
|
|
39
|
+
async def main():
|
|
40
|
+
quotation = await get_latest_price("CPALL") # latest TRADED point, relative to now
|
|
41
|
+
if quotation:
|
|
42
|
+
print(f"{quotation.local_datetime}: {quotation.price} (vol {quotation.volume})")
|
|
43
|
+
else:
|
|
44
|
+
print("Nothing has traded yet today.")
|
|
45
|
+
|
|
46
|
+
asyncio.run(main())
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Using the Stock class
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from settfex.services.set import Stock
|
|
53
|
+
|
|
54
|
+
stock = Stock("CPALL")
|
|
55
|
+
data = await stock.get_chart_quotation(period="1D") # full ChartQuotation series
|
|
56
|
+
latest = await stock.get_latest_price() # latest traded Quotation (or None)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Using the convenience function
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from settfex.services.set import get_chart_quotation
|
|
63
|
+
|
|
64
|
+
data = await get_chart_quotation("JAS-W4", period="1D") # hyphenated warrant symbol is fine
|
|
65
|
+
print(f"Prior close: {data.prior}, points: {len(data.quotations)}")
|
|
66
|
+
print(f"Latest price (scalar, prior-fallback): {data.get_latest_price()}")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Using the service class
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from settfex.services.set.stock import ChartQuotationService
|
|
73
|
+
|
|
74
|
+
service = ChartQuotationService()
|
|
75
|
+
data = await service.fetch_chart_quotation("CPALL", period="1D", accumulated=False)
|
|
76
|
+
raw = await service.fetch_chart_quotation_raw("CPALL") # unvalidated dict
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Latest-price selection rule
|
|
80
|
+
|
|
81
|
+
`ChartQuotation.get_latest_quotation(as_of=None)` returns the quotation with the **greatest
|
|
82
|
+
timestamp that is `<= as_of` and has a non-null `volume`** — i.e. the latest minute that actually
|
|
83
|
+
traded. This matters because the API returns one bucket per minute for the **whole session up to
|
|
84
|
+
end of day**; minutes that are in the future, fall in the lunch `intermission`, or simply had no
|
|
85
|
+
trade carry `volume = null` (the `price` may still be carried forward).
|
|
86
|
+
|
|
87
|
+
- **`as_of`** defaults to **now in `Asia/Bangkok`**. A naive `as_of` is treated as Bangkok local
|
|
88
|
+
time; an aware `as_of` (e.g. UTC) is converted. Naive and aware datetimes are never compared
|
|
89
|
+
directly — both sides are normalized to Bangkok first.
|
|
90
|
+
- **`get_latest_price(as_of=None)`** (on the model) returns that quotation's `price`, **falling
|
|
91
|
+
back to `prior`** (the previous session's close) when nothing has traded yet, or `None` if
|
|
92
|
+
`prior` is also unavailable.
|
|
93
|
+
- **Top-level `get_latest_price(symbol, …)`** returns the full **`Quotation`** object (time, price,
|
|
94
|
+
volume, change), or `None` — a single request, an O(n) scan, no re-fetching.
|
|
95
|
+
|
|
96
|
+
| Situation | `get_latest_quotation` | `model.get_latest_price` |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| Mid-session, after trades | latest traded `Quotation` | its `price` |
|
|
99
|
+
| Before the open / all-null series | `None` | `prior` (fallback) |
|
|
100
|
+
| `as_of` inside the lunch break | last morning trade | its `price` |
|
|
101
|
+
| Empty series, `prior` set | `None` | `prior` |
|
|
102
|
+
| Empty series, `prior` is `None` | `None` | `None` |
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
### Models
|
|
107
|
+
|
|
108
|
+
#### `Quotation`
|
|
109
|
+
|
|
110
|
+
| Field | Type | JSON alias | Description |
|
|
111
|
+
|-------|------|-----------|-------------|
|
|
112
|
+
| `quote_datetime` | `datetime` | `datetime` | Timestamp, timezone-aware (`+07:00`) |
|
|
113
|
+
| `local_datetime` | `datetime` | `localDatetime` | Same instant, naive Bangkok local time |
|
|
114
|
+
| `price` | `float \| None` | `price` | Trade price (may be carried forward on no-trade) |
|
|
115
|
+
| `volume` | `float \| None` | `volume` | Trade volume (shares); `None` = no trade this bucket |
|
|
116
|
+
| `value` | `float \| None` | `value` | Trade value (THB) |
|
|
117
|
+
| `change` | `float \| None` | `change` | Price change from prior close |
|
|
118
|
+
| `percent_change` | `float \| None` | `percentChange` | Percentage change from prior close |
|
|
119
|
+
|
|
120
|
+
#### `Intermission`
|
|
121
|
+
|
|
122
|
+
| Field | Type | Description |
|
|
123
|
+
|-------|------|-------------|
|
|
124
|
+
| `begin` | `datetime` | Intermission start (e.g. lunch break), naive Bangkok |
|
|
125
|
+
| `end` | `datetime` | Intermission end, naive Bangkok |
|
|
126
|
+
|
|
127
|
+
#### `ChartQuotation`
|
|
128
|
+
|
|
129
|
+
| Field / Method | Type | Description |
|
|
130
|
+
|----------------|------|-------------|
|
|
131
|
+
| `prior` | `float \| None` | Prior trading day's close |
|
|
132
|
+
| `intermissions` | `list[Intermission]` | Trading intermission periods |
|
|
133
|
+
| `quotations` | `list[Quotation]` | Per-interval quotation points |
|
|
134
|
+
| `get_latest_quotation(as_of=None)` | `Quotation \| None` | Latest *traded* quotation at/before `as_of` |
|
|
135
|
+
| `get_latest_price(as_of=None)` | `float \| None` | Latest traded price, falling back to `prior` |
|
|
136
|
+
|
|
137
|
+
### Service Class — `ChartQuotationService`
|
|
138
|
+
|
|
139
|
+
| Method | Returns | Description |
|
|
140
|
+
|--------|---------|-------------|
|
|
141
|
+
| `fetch_chart_quotation(symbol, period="1D", accumulated=False)` | `ChartQuotation` | Validated model |
|
|
142
|
+
| `fetch_chart_quotation_raw(symbol, period="1D", accumulated=False)` | `dict` | Unvalidated raw dict |
|
|
143
|
+
|
|
144
|
+
### Convenience Functions
|
|
145
|
+
|
|
146
|
+
| Function | Returns | Description |
|
|
147
|
+
|----------|---------|-------------|
|
|
148
|
+
| `get_chart_quotation(symbol, period="1D", accumulated=False, config=None)` | `ChartQuotation` | One-line fetch |
|
|
149
|
+
| `get_latest_price(symbol, period="1D", accumulated=False, as_of=None, config=None)` | `Quotation \| None` | Latest traded quotation vs `as_of` |
|
|
150
|
+
|
|
151
|
+
## Usage Examples
|
|
152
|
+
|
|
153
|
+
### Example 1 — "What's the price right now?"
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from settfex.services.set import get_latest_price
|
|
157
|
+
|
|
158
|
+
q = await get_latest_price("PTT")
|
|
159
|
+
print("No trades yet" if q is None else f"{q.price} @ {q.local_datetime} (vol {q.volume})")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Example 2 — Price as of a specific instant
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from datetime import datetime
|
|
166
|
+
from settfex.services.set import get_latest_price
|
|
167
|
+
|
|
168
|
+
# Naive datetime is interpreted as Asia/Bangkok local time
|
|
169
|
+
q = await get_latest_price("CPALL", as_of=datetime(2026, 6, 19, 11, 0))
|
|
170
|
+
print(q.price if q else "Nothing traded by 11:00")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Example 3 — Build a simple intraday table
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from settfex.services.set import get_chart_quotation
|
|
177
|
+
|
|
178
|
+
data = await get_chart_quotation("CPALL", period="1D")
|
|
179
|
+
for q in data.quotations:
|
|
180
|
+
if q.volume is not None: # skip null/no-trade buckets
|
|
181
|
+
print(f"{q.local_datetime:%H:%M} {q.price:>8} {q.volume:>12,.0f}")
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Error Handling & Troubleshooting
|
|
185
|
+
|
|
186
|
+
- **Empty symbol** → `ValueError`.
|
|
187
|
+
- **Non-200 HTTP response** → `Exception` with the status code (e.g. `HTTP 403`).
|
|
188
|
+
- **`get_latest_price` returns `None`** → nothing has traded by `as_of` (before the open, a holiday,
|
|
189
|
+
or an all-null series). Use the model's `get_latest_price()` for the `prior`-fallback scalar.
|
|
190
|
+
- **Markets closed** → the `1D` series for the most recent session is returned; the latest traded
|
|
191
|
+
point is that session's last trade.
|