settfex 0.6.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.6.0 → settfex-0.7.0}/.gitignore +1 -1
- {settfex-0.6.0 → settfex-0.7.0}/CHANGELOG.md +20 -0
- settfex-0.7.0/CLAUDE.md +234 -0
- {settfex-0.6.0 → settfex-0.7.0}/PKG-INFO +31 -2
- {settfex-0.6.0 → settfex-0.7.0}/README.md +30 -1
- settfex-0.7.0/docs/settfex/services/set/chart_quotation.md +191 -0
- settfex-0.7.0/examples/set/13_chart_quotation.ipynb +221 -0
- {settfex-0.6.0 → settfex-0.7.0}/pyproject.toml +1 -1
- {settfex-0.6.0 → settfex-0.7.0}/settfex/__init__.py +1 -1
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/__init__.py +13 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/__init__.py +2 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/chart_quotation.py +108 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/stock.py +34 -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.6.0 → settfex-0.7.0}/uv.lock +1 -1
- {settfex-0.6.0 → settfex-0.7.0}/.editorconfig +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/copilot-instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/dependabot.yml +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/instructions/core-architectrual-principles.instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/instructions/documentation-standards.instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/instructions/file-organization.instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/instructions/git-commit.instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/instructions/python-dependency-management.instructions.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/prompts/Coding.prompt.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/prompts/Git-Commit-Reviewer.prompt.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/prompts/Prompt-Engineer.prompt.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/prompts/Python-Architect.prompt.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/workflows/ci.yml +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/workflows/release.yml +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.github/workflows/security.yml +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.pre-commit-config.yaml +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/.python-version +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/COMPREHENSIVE_AUDIT.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/CONTRIBUTING.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/LICENSE +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/MANIFEST.in +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/RELEASING.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/SECURITY.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/guide/PYTHON_LIBRARY_BEST_PRACTICES.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/index.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/API_PROTECTION_NOTE.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/board_of_director.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/corporate_action.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/earnings_call.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/financial.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/highlight_data.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/list.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/nvdr_holder.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/price_performance.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/profile_company.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/profile_stock.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/shareholder.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/set/trading_stat.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/tfex/list.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/services/tfex/trading_statistics.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/utils/data_fetcher.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/settfex/utils/session_caching.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/solution/FINDINGS.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/solution/Performance Boost Session Caching Implementation.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/solution/SESSION_CACHE_SUMMARY.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/docs/solution/SOLUTION_100_PERCENT.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/README.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/01_stock_list.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/02_highlight_data.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/03_stock_profile.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/04_company_profile.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/05_corporate_action.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/06_shareholder.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/07_nvdr_holder.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/08_board_of_director.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/09_trading_statistics.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/10_price_performance.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/11_financial.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/12_earnings_call.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/README.md +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/cpall_balance_sheet.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/market_dashboard_20251005_173221.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/market_dashboard_20251005_210737.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/market_dashboard_20251006_091440.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/thailand_banking_stocks.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/thailand_set_main_board.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/set/thailand_stock_universe.csv +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/tfex/01_series_list.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/tfex/02_trading_statistics.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/examples/tfex/03_underlying_price.ipynb +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/py.typed +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/constants.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/earnings_call.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/list.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/board_of_director.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/corporate_action.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/financial/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/financial/financial.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/highlight_data.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/latest_historical_trading.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/nvdr_holder.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/price_performance.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/profile_company.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/profile_stock.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/shareholder.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/trading_stat.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/set/stock/utils.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/tfex/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/tfex/constants.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/tfex/list.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/tfex/trading_statistics.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/services/tfex/underlying_price.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/data_fetcher.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/http.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/logging.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/parsing.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/session_cache.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/session_manager.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/settfex/utils/youtube_transcript.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/conftest.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/stock/financial/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/stock/financial/test_financial.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_board_of_director.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_client.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_corporate_action.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_earnings_call.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_historical.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_realtime.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/set/test_shareholder.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_client.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_historical.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_list.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_realtime.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_trading_statistics.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/services/tfex/test_underlying_price.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/__init__.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_data_fetcher.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_formatting.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_http.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_logging.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_parsing.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_session_manager.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_validation.py +0 -0
- {settfex-0.6.0 → settfex-0.7.0}/tests/utils/test_youtube_transcript.py +0 -0
|
@@ -7,6 +7,26 @@ 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
|
+
|
|
10
30
|
## [0.6.0] - 2026-06-19
|
|
11
31
|
|
|
12
32
|
### 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
|
|
@@ -101,6 +101,8 @@ All examples include beginner explanations, professional trading use cases, and
|
|
|
101
101
|
9. **[Trading Statistics](examples/set/09_trading_statistics.ipynb)** - Multi-period performance and volatility
|
|
102
102
|
10. **[Price Performance](examples/set/10_price_performance.ipynb)** - Sector comparison and alpha calculation
|
|
103
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
|
|
104
106
|
|
|
105
107
|
### 📈 TFEX Examples (Thailand Futures Exchange)
|
|
106
108
|
|
|
@@ -128,7 +130,7 @@ Want to dig deeper? Check out our detailed guides:
|
|
|
128
130
|
- **[Trading Statistics Service](docs/settfex/services/set/trading_stat.md)** - Historical trading performance and metrics
|
|
129
131
|
- **[Price Performance Service](docs/settfex/services/set/price_performance.md)** - Stock, sector, and market price performance comparison
|
|
130
132
|
- **[Financial Service](docs/settfex/services/set/financial.md)** - Balance sheet, income statement, and cash flow data
|
|
131
|
-
- **[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
|
|
132
134
|
- **[Latest Historical Trading Service](docs/settfex/services/set/latest_historical_trading.md)** - Latest trading day summary with OHLCV and valuation metrics
|
|
133
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
|
|
134
136
|
|
|
@@ -357,6 +359,27 @@ print(f"Market ({data.market.symbol}): {data.market.ytd_percent_change:+.2f}%")
|
|
|
357
359
|
|
|
358
360
|
---
|
|
359
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
|
+
|
|
360
383
|
#### 💰 Get Financial Statements
|
|
361
384
|
|
|
362
385
|
Fetch comprehensive financial data including balance sheet, income statement, and cash flow:
|
|
@@ -458,6 +481,12 @@ await fetch_transcripts(hann.items) # fills item.transcript (Thai)
|
|
|
458
481
|
text = await get_earnings_call_transcript(6319) # …or one presentation's transcript by id
|
|
459
482
|
```
|
|
460
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
|
+
|
|
461
490
|
**👉 [Learn more about the Earnings Call Service](docs/settfex/services/set/earnings_call.md)**
|
|
462
491
|
|
|
463
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:
|
|
@@ -411,6 +434,12 @@ await fetch_transcripts(hann.items) # fills item.transcript (Thai)
|
|
|
411
434
|
text = await get_earnings_call_transcript(6319) # …or one presentation's transcript by id
|
|
412
435
|
```
|
|
413
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
|
+
|
|
414
443
|
**👉 [Learn more about the Earnings Call Service](docs/settfex/services/set/earnings_call.md)**
|
|
415
444
|
|
|
416
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.
|