docs(stock): add price provider research

This commit is contained in:
2026-06-25 17:17:08 +07:00
parent 3800b27954
commit 949bb01427
2 changed files with 456 additions and 0 deletions
@@ -0,0 +1,292 @@
---
type: research-report
topic: stable-vn-stock-price-provider
created: 2026-06-25 09:36 UTC
status: done
---
# Research Report: Stable Free VN Stock Price Provider
## Executive Summary
Best fit: **EODHD free plan as primary EOD provider**, with DynamoDB daily cache. It is the only provider found with explicit Vietnam exchange support (`VN` / MIC `XSTC`) and a real free API-key plan. It is not real-time on free tier, but this bot is paper trading, so previous/official-ish EOD close is acceptable if rendered as EOD/last close.
Do not depend on SSI/KBS broker-app endpoints as primary. Live evidence from Lambda: SSI returns Cloudflare security pages, KBS times out from AWS egress. Yahoo works today, but it is not a stable contracted API for VN stocks. Keep Yahoo/SSI/KBS only as emergency fallback.
If user wants intraday/current price with a stable contract, no truly free option found. The free stable path is EOD only. Paid floor appears around $19.99/mo for EODHD all-world EOD; intraday/live is higher.
## Methodology
- Date: 2026-06-25.
- Scope: Vietnam listed equities for `stock` module: `/stock_stats`, `/stock_buy`, `/stock_sell`.
- Criteria: explicit VN coverage, free API key, documented REST API, low-volume Lambda-safe, legal/terms risk, implementation effort.
- Sources consulted: EODHD, Marketstack, Twelve Data, FMP, Alpha Vantage, existing production probes.
- Direct probes:
- Twelve Data `/stocks?country=Vietnam`, `/stocks?country=Viet%20Nam`, `/stocks?exchange=HOSE` returned empty arrays.
- Twelve Data `/stocks?symbol=FPT` resolved Thailand `FPT`, not Vietnam FPT Corp.
- EODHD demo token returned `403 Forbidden` for `exchange-symbol-list/VN`; expected, demo token not real free key.
- Marketstack without key returned `missing_access_key`; expected.
## Key Findings
### 1. EODHD
EODHD explicitly lists **Vietnam Stocks - VN**, country Vietnam, MIC `XSTC`, timezone `Asia/Ho_Chi_Minh`, and 593 active tickers. Example listed format is `AAA.VN`, `ACB.VN`, etc. Source: https://eodhd.com/exchange/VN
Free plan:
- $0/mo.
- 20 calls/day.
- EOD data only, past-year depth.
- Personal use.
- Requires registration/API key.
Docs/pricing sources:
- https://eodhd.com/financial-apis/api-for-historical-data-and-volumes
- https://eodhd.com/pricing
- https://eodhd.com/list-of-stock-markets
Pros:
- Explicit VN coverage.
- Documented REST API.
- Free key allowed.
- Enough for bot if we cache daily and request only held/watchlist symbols.
- Symbol format simple: `FPT.VN`, `TCB.VN`, etc.
Cons:
- Free quota low: 20/day.
- EOD, not live current price.
- Terms disclaim data may not be real-time/accurate and is indicative.
- Commercial/display use may require paid/commercial agreement.
Verdict: **Primary recommendation**.
### 2. Marketstack
Marketstack has a documented free plan and stable API infra, but no confirmed Vietnam symbol coverage from public unauthenticated probe. It advertises global EOD data, stock tickers info, and exchange info.
Free plan:
- 100 requests/month.
- EOD data and up to 12 months history.
- API key required.
Sources:
- https://marketstack.com/
- https://marketstack.com/pricing
- https://docs.apilayer.com/marketstack/docs/api-endpoints-v1
Pros:
- Documented.
- Stable SaaS provider.
- Supports multi-symbol EOD requests via `symbols` parameter according to docs/search result.
Cons:
- Free quota worse than EODHD for daily bot use unless multi-symbol request covers all holdings.
- VN coverage not verified without real key.
- Intraday/real-time mostly paid/US-oriented.
Verdict: **Candidate backup only after registering a free key and proving `FPT`, `TCB`, `HPG`, `MSN`, `MWG`, `SSI`, `VND` coverage**.
### 3. Twelve Data
Twelve Data has good free quota reputation and strong docs, but public reference data did not show Vietnam equities in probes.
Source:
- https://support.twelvedata.com/en/articles/5620513-how-to-find-all-available-symbols-at-twelve-data
- https://twelvedata.com/
Probe results:
- `https://api.twelvedata.com/stocks?country=Vietnam` -> `[]`
- `https://api.twelvedata.com/stocks?country=Viet%20Nam` -> `[]`
- `https://api.twelvedata.com/stocks?exchange=HOSE` -> `[]`
- `https://api.twelvedata.com/stocks?symbol=FPT` -> Thailand `FPT`, not Vietnam.
Verdict: **Reject for now** unless support confirms VN coverage on a plan/add-on.
### 4. Financial Modeling Prep
FMP free plan is attractive on paper: 250 calls/day and EOD/reference data. But the public docs/pricing suggest global coverage only at higher tiers, and demo API key did not allow exchange/symbol coverage validation.
Sources:
- https://site.financialmodelingprep.com/developer/docs
- https://site.financialmodelingprep.com/pricing-plans
Pros:
- 250 calls/day free.
- Strong docs/API shape.
Cons:
- Vietnam coverage not verified.
- Pricing page implies Basic/free has limited symbols and EOD only; global coverage appears in Ultimate paid tier.
- Display/redistribution needs licensing agreement.
Verdict: **Reject until a real free key confirms VN symbols**.
### 5. Alpha Vantage
Alpha Vantage is reputable and free-key based, but no evidence found for Vietnam exchange coverage. It is more useful for US/global large markets than VN equities.
Source:
- https://www.alphavantage.co/
Verdict: **Reject for VN stocks**.
### 6. Broker/App Internal Endpoints
Current/existing providers:
- SSI direct quote: `iboard-query.ssi.com.vn`.
- SSI chart history: `iboard-api.ssi.com.vn`.
- KBS data-day: `kbbuddywts.kbsec.com.vn`.
- Yahoo chart: `query1.finance.yahoo.com`.
Findings:
- SSI now returns 403 Cloudflare security page from this workspace and Lambda.
- KBS returns locally but times out from Lambda.
- Yahoo works from Lambda today, but is not a contracted API.
Verdict: **fallback only, never primary**.
## Comparative Analysis
| Provider | VN coverage proven | Free key | Free quota | Current price | Stable API | Fit |
|---|---:|---:|---:|---:|---:|---|
| EODHD | yes | yes | 20/day | EOD only | yes | best |
| Marketstack | unknown | yes | 100/month | EOD free | yes | backup candidate |
| Twelve Data | no in probes | yes | likely generous | unknown | yes | reject |
| FMP | unknown | yes | 250/day | EOD free | yes | reject until proven |
| Alpha Vantage | no evidence | yes | free | unknown | yes | reject |
| SSI/KBS/Yahoo | yes-ish | no | unlimited-ish | yes | no | fallback only |
## Implementation Recommendation
### Provider Order
```text
EODHD EOD cache -> Yahoo emergency -> SSI emergency -> KBS emergency -> no price
```
Do not put Yahoo before EODHD once EODHD key exists. Yahoo is useful operationally but not stable.
### Data Model
```go
type StockQuote struct {
Symbol string
PriceVND float64
Source string // eodhd, yahoo, ssi, kbs
AsOfDate string // YYYY-MM-DD
RetrievedAt int64
Stale bool
}
```
Cache keys:
```text
stock-price:FPT:2026-06-25
stock-price:TCB:2026-06-25
```
### API Shape
EODHD EOD endpoint:
```text
GET https://eodhd.com/api/eod/FPT.VN?api_token=$EODHD_API_KEY&fmt=json&period=d&from=YYYY-MM-DD&to=YYYY-MM-DD
```
Use latest returned row:
```json
{
"date": "2026-06-25",
"open": 71000,
"high": 71700,
"low": 70800,
"close": 71000,
"adjusted_close": 71000,
"volume": 6592100
}
```
For portfolio valuation, use `close`. For paper buy/sell, either:
- Use same `close` and label trade as "last close", or
- Keep Yahoo as live-ish fallback only for trade commands.
### Quota Strategy
Free EODHD quota is 20/day. Therefore:
1. Cache per symbol per trading date.
2. Fetch only missing cache entries.
3. For `/stock_stats`, batch unique held symbols but call EODHD sequentially under a short timeout.
4. Do not refresh more than once/day per symbol.
5. If quota exceeded, use stale cache and label `(stale YYYY-MM-DD)`.
With current portfolio of 7 symbols, daily refresh costs 7 calls/day. Fits.
### Env Config
Add:
```text
STOCK_EODHD_API_KEY_PARAMETER_NAME=/miti99bot/prod/eodhd-api-key
STOCK_EODHD_API_URL=https://eodhd.com/api
```
Store key in SSM SecureString, same pattern as Telegram/Gemini secrets.
### User-Facing Copy
When using EODHD:
```text
FPT x2300 @ 71.000 VND (EOD 2026-06-25) = ...
```
When fallback/stale:
```text
FPT x2300 @ 71.000 VND (stale EOD 2026-06-24) = ...
```
## Common Pitfalls
- Calling EODHD on every `/stock_stats`: burns quota fast.
- Treating EOD price as live market price without label.
- Assuming Marketstack/Twelve/FMP support Vietnam because marketing says "global".
- Keeping broker-app endpoints as primary.
- No stale cache. The bot should degrade to cached prices, not zero portfolio value.
## Recommended Next Steps
1. Register EODHD free key.
2. Manually verify these URLs with real key:
- `FPT.VN`
- `HPG.VN`
- `MSN.VN`
- `MWG.VN`
- `SSI.VN`
- `TCB.VN`
- `VND.VN`
3. If all seven work, implement `EODHDPriceProvider` + daily cache.
4. Change `/stock_stats` labels to show source/date.
5. Keep Yahoo/SSI/KBS emergency fallbacks after EODHD/cache.
## Resources
- EODHD Vietnam exchange: https://eodhd.com/exchange/VN
- EODHD supported exchanges: https://eodhd.com/list-of-stock-markets
- EODHD EOD API: https://eodhd.com/financial-apis/api-for-historical-data-and-volumes
- EODHD pricing: https://eodhd.com/pricing
- Marketstack pricing: https://marketstack.com/pricing
- Marketstack docs: https://docs.apilayer.com/marketstack/docs/api-endpoints-v1
- Twelve Data symbol reference: https://support.twelvedata.com/en/articles/5620513-how-to-find-all-available-symbols-at-twelve-data
- FMP docs: https://site.financialmodelingprep.com/developer/docs
- FMP pricing: https://site.financialmodelingprep.com/pricing-plans
- Alpha Vantage: https://www.alphavantage.co/
## Unresolved Questions
- Does EODHD free key return all current holdings (`FPT.VN`, `HPG.VN`, `MSN.VN`, `MWG.VN`, `SSI.VN`, `TCB.VN`, `VND.VN`) via API, not just website pages?
- Is "last close" acceptable for `/stock_buy` and `/stock_sell`, or should those commands keep live-ish Yahoo fallback?
- Is bot use personal/non-commercial under EODHD terms, or does Telegram display to group chats require paid/commercial terms?
@@ -0,0 +1,164 @@
---
type: research-report
topic: kbs-vs-vci-price-provider
created: 2026-06-25 17:04 UTC
status: done
---
# Research Report: KBS vs VCI Price Provider
## Executive Summary
For this bot, if we only need **current price**, the comparison is **KBS `/stock/iss` vs VCI `/price/symbols/getList`**. Both return all needed symbols in one batch request and returned identical prices in live probe.
**Best practical answer: test KBS price-board first, then VCI fallback.** KBS current-price response is simpler and is vnstock's default for Vietnam market quote. Our previous Lambda failure was against KBS `data_day` history endpoint with weaker headers, not the KBS current price-board endpoint. Therefore KBS current quote is still worth a Lambda probe before replacing it.
Do not treat either as stable. Both are unofficial web/app endpoints, not contracted API-key services. Both rejected raw curl fingerprints during probe: KBS returned `400 Request Blocked`, VCI returned `403 Error Page`. With browser-like headers, both worked. That means both can break by WAF/fingerprint changes.
Best stable architecture remains: **EODHD EOD/cache primary when key exists -> KBS current quote after Lambda proof -> VCI current quote fallback -> SSI direct fallback -> no price**.
## Research Methodology
- Timestamp: 2026-06-25 17:04 UTC.
- Scope: VN equity **current price only** for `miti99bot` stock module on AWS Lambda.
- Sources consulted:
- `vnstock` source at commit `5bf05e3e494b1d109750c143e964945ed7be3f7d`.
- Live endpoint probes from this workspace.
- Prior production evidence: KBS timed out from Lambda during `/stock_stats` probe.
- Key terms: KBS, VCI, Vietcap, vnstock quote, price board, OHLCChart, Lambda egress.
## Key Findings
### KBS
vnstock default Vietnam market quote route uses KBS:
```text
Market().quote("SSI")
-> Trading.price_board()
-> POST https://kbbuddywts.kbsec.com.vn/iis-server/investment/stock/iss
-> {"code":"SSI"}
```
Live probe with browser headers:
| Probe | Result |
|---|---:|
| Current quote, 7 symbols | `200`, `0.297s` |
| Raw/default curl | `400 Request Blocked` |
Pros:
- Simple payload.
- Flat fields: `CP` close/current price, `B1..B3` bid, `S1..S3` ask.
- vnstock uses it as default for unified market quote.
Cons:
- KBS `data_day` history endpoint timed out from our AWS Lambda environment; `stock/iss` current quote still needs Lambda proof.
- Current Go fallback currently uses per-symbol `data_day`, not the KBS batch price-board endpoint.
- WAF/fingerprint sensitive. Needs browser-like headers.
- No official read-only API key contract.
### VCI
vnstock supports VCI/Vietcap current quote endpoint:
```text
POST https://trading.vietcap.com.vn/api/price/symbols/getList
```
Live probe with browser headers:
| Probe | Result |
|---|---:|
| Current quote, 7 symbols | `200`, `0.357s` |
| Raw/default curl | `403 Error Page` |
Current quote returned same current prices as KBS:
| Symbol | KBS | VCI |
|---|---:|---:|
| FPT | 71000 | 71000 |
| HPG | 23400 | 23400 |
| MSN | 71500 | 71500 |
| MWG | 77200 | 77200 |
| SSI | 26500 | 26500 |
| TCB | 33400 | 33400 |
| VND | 17350 | 17350 |
Pros:
- Better fit for `/stock_stats`: one batch request for all current quotes.
- Rich structured response: `listingInfo`, `bidAsk`, `matchPrice`.
- Gives matched price, bid/ask, session, reference, floor, ceiling, volume.
- Same observed prices as KBS.
Cons:
- 403 without browser-like headers.
- More nested parsing than KBS.
- No official read-only API key contract.
- Needs Lambda proof before promoting.
## Comparative Analysis
| Criteria | KBS | VCI | Winner |
|---|---|---|---|
| Current quote batch | Yes | Yes | Tie |
| Payload simplicity | Flat fields | Nested objects | KBS |
| Parser safety | Easy | Moderate | KBS |
| Lambda evidence | History endpoint timed out; price-board not tested | Not tested yet | Unknown |
| Browser/WAF sensitivity | Yes | Yes | Tie |
| vnstock default | Yes | No | KBS |
| Current price richness | Good | Better structured | VCI |
| Official/stable API | No | No | Neither |
| Best for current price only | First Lambda probe | Fallback probe | KBS |
## Recommendation
For current price only, use **KBS price-board first**, but only after a Lambda probe passes. Use **VCI second**.
Recommended order:
```text
EODHD daily cache -> KBS stock/iss current quote -> VCI current quote -> SSI direct quote -> no price
```
If no EODHD key yet:
```text
KBS stock/iss -> VCI price/symbols/getList -> SSI direct quote
```
Implementation notes:
- Replace current KBS `data_day` fallback with KBS `stock/iss` batch endpoint for current price.
- Add VCI only as current quote fallback: `price/symbols/getList`.
- Add browser-like headers exactly; `Mozilla/5.0 (miti99bot)` is probably too bot-looking.
- Cache result briefly per command or per minute to avoid repeated WAF pressure.
- Log provider/source/date in stock output.
## Common Pitfalls
- Assuming vnstock's default means stable in Lambda. It does not.
- Calling KBS `data_day` per symbol when price-board can batch current price.
- Promoting either provider without Lambda proof.
- Calling either "official API". These are web/app backend endpoints.
- Using weak headers. Both rejected raw/default curl.
## Resources & References
- vnstock KBS constants: https://github.com/thinh-vu/vnstock/blob/5bf05e3e494b1d109750c143e964945ed7be3f7d/vnstock/explorer/kbs/const.py
- vnstock KBS price board: https://github.com/thinh-vu/vnstock/blob/5bf05e3e494b1d109750c143e964945ed7be3f7d/vnstock/explorer/kbs/trading.py
- vnstock VCI price board: https://github.com/thinh-vu/vnstock/blob/5bf05e3e494b1d109750c143e964945ed7be3f7d/vnstock/explorer/vci/trading.py
- vnstock UI routing defaults: https://github.com/thinh-vu/vnstock/blob/5bf05e3e494b1d109750c143e964945ed7be3f7d/vnstock/ui/_registry.py
## Next Steps
1. Add KBS `stock/iss` batch current-price provider behind env flag or provider order config.
2. Deploy to Lambda and fake-probe `/stock_stats`.
3. If KBS price-board passes Lambda, place it before VCI/SSI.
4. If KBS fails from Lambda, add VCI current-price fallback and probe.
5. Keep EODHD as stable primary when key is available.
## Unresolved Questions
- Does KBS `stock/iss` work from AWS Lambda `ap-southeast-1` with browser-like headers?
- Does VCI work from AWS Lambda `ap-southeast-1` with browser-like headers if KBS fails?
- Is live-ish price necessary for `/stock_buy` and `/stock_sell`, or is EOD close acceptable?