feat: foreign flow from IPOT by default (--source ipot|idx)

`idx stocks flow` now reads IPOT by default, which needs nothing installed:
per session, the all-investor broker summary gives the value traded and
the foreign one gives foreign buying (its footer), the foreign net and
selling (buying less the net), across all boards, through the broker
summary cache. IPOT's foreign net matched IDX's estimate within 0.3 B on
BBCA 2026-10-12 to 16.

`--source idx` (and `source` on get_foreign_flow) keeps IDX's trading
summary: exact share counts, close and the non-regular share, behind
curl-impersonate and Cloudflare.

- ForeignFlowDay gains foreign_buy_value, foreign_sell_value and
  foreign_share_bps for both sources (estimated at the session average
  for IDX); ForeignFlow gains source, the value totals and rounded.
- The table lays out each source's figures; the live smoke runs both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
hermes 2026-10-18 06:19:02 +00:00
commit 8b507d78e2
37 changed files with 2080 additions and 143 deletions

View file

@ -433,7 +433,7 @@ Examples:
### `idx stocks flow`
```text
Foreign buying and selling of a stock, session by session, from IDX's daily trading summary: foreign buy, sell and net volume, the net value at the session's average price, and the share of value traded outside the regular board. End of day. Each session is one request to idx.co.id, which needs curl-impersonate.
Foreign buying and selling of a stock, session by session, end of day. By default from Indo Premier (IPOT): foreign buy, sell and net value across all boards and foreign investors' share of trading, two plain requests per session. `--source idx` reads IDX's own trading summary instead: exact foreign share counts, the close and the share traded outside the regular board, one request per session to idx.co.id, which needs curl-impersonate and a network IDX's Cloudflare lets through.
Usage: idx stocks flow [OPTIONS] <SYMBOL>
@ -462,6 +462,15 @@ Options:
--to <TO>
Last session of the range, YYYY-MM-DD. Default: today
--source <SOURCE>
Where the figures come from
Possible values:
- ipot: Indo Premier's broker summary: foreign buy, sell and net value across all boards, two plain requests per session. Figures of a million and more are rounded to one decimal
- idx: IDX's own trading summary: exact foreign share counts, close and the non-regular share, regular board. Needs curl-impersonate and a network IDX's Cloudflare lets through
[default: ipot]
-v, --verbose...
Increase verbosity (-v, -vv)
@ -478,7 +487,8 @@ Examples:
idx stocks flow BBCA
idx stocks flow BBCA --days 20
idx stocks flow BBCA --from 2026-10-01 --to 2026-10-16
idx -o json stocks flow BBCA | jq '.foreign_net'
idx stocks flow BBCA --source idx
idx -o json stocks flow BBCA | jq '.foreign_net_value'
```
### `idx stocks profile`

View file

@ -55,18 +55,25 @@ million and more to one decimal, and the output says so.
idx stocks flow BBCA
idx stocks flow BBCA --days 20
idx stocks flow BBCA --from 2026-10-01 --to 2026-10-16
idx stocks flow BBCA --source idx
```
What foreign investors bought and sold, session by session: buy, sell and
net volume in shares, the net value at each session's average price (an
estimate), and the share of all value traded outside the regular board.
Volume, value and the foreign figures are IDX's regular-board figures. The
footer gives the net over the range and foreign investors' share of trading.
Without dates it shows the latest 10 sessions (`--days`, at most 60).
What foreign investors bought and sold, session by session, with the totals
over the range. Without dates it shows the latest 10 sessions (`--days`, at
most 60).
The data is IDX's own daily trading summary. Each session is one request to
idx.co.id, cached afterwards for every stock, and needs
[curl-impersonate](/start/install/) to pass the site's bot protection.
By default the figures come from Indo Premier (IPOT), like `broker`: foreign
buy, sell and net value across all boards and foreign investors' share of
trading. It needs nothing installed, takes two requests per session, and
rounds figures of a million and more to one decimal.
`--source idx` reads IDX's own daily trading summary instead: exact foreign
share counts, the close and day change, and the share of value traded
outside the regular board. Those figures are the regular board, and values
are estimated at each session's average price. Each session is one request
to idx.co.id, cached for every stock, and needs
[curl-impersonate](/start/install/) and a network IDX's Cloudflare lets
through; even then it can answer 403.
## Bandarmology

View file

@ -65,12 +65,13 @@ Company data for one IDX ticker: profile, financial statements, earnings, analys
## `get_foreign_flow`
Foreign investors' buying and selling of one IDX ticker, session by session (oldest first), from IDX's daily trading summary: foreign buy, sell and net volume in shares, `foreign_net_value` (net shares at the session's average price, an estimate, IDR), `change_bps`, and `non_regular_bps` (share of all value traded outside the regular board). Volume, value and foreign figures are the regular board. Totals cover the whole range; `foreign_share_bps` is foreign investors' share of regular-board trading. End of day.
Foreign investors' buying and selling of one IDX ticker, session by session (oldest first), with range totals; end of day. With `source` "ipot" (default): `foreign_buy_value`, `foreign_sell_value` and `foreign_net_value` in IDR across all boards, `value` traded, and `foreign_share_bps` (foreign share of trading); share counts, close and `non_regular_bps` are null, and `rounded` = true means one decimal of precision. With "idx": exact foreign share counts on the regular board, values estimated at the session's average price, the close, `change_bps` and `non_regular_bps`.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | no | Latest sessions (default 10, at most 60). Omit when giving from/to. |
| `from` | string | no | First session of a range, YYYY-MM-DD (at most 92 days). |
| `source` | `ipot` / `idx` | no | Where the figures come from (default "ipot"): "ipot" gives foreign buy, sell and net value across all boards; "idx" gives exact share counts, the close and the non-regular share, but needs curl-impersonate and may be blocked (HTTP 403). |
| `symbol` | string | yes | IDX ticker, e.g. "BBCA". |
| `to` | string | no | Last session of the range, YYYY-MM-DD (default today). |

View file

@ -74,13 +74,14 @@ idx version
## Helper tools
Everyday use needs nothing else: stock data with the default MSN provider,
price history, broker summaries and bandarmology, `ownership sync` and every
price history, broker summaries, bandarmology and foreign flow, `ownership sync`
and every
local ownership query. A few commands call external tools:
| Tool | Needed for |
| --- | --- |
| `curl_chrome*` from [curl-impersonate](https://github.com/lexiforest/curl-impersonate), or `IDX_CURL_IMPERSONATE_BIN` | `ownership discover` and `ownership import --url` (IDX sits behind Cloudflare), and Yahoo fundamentals with `IDX_PROVIDER=yahoo` |
| the full curl-impersonate set, with its `curl_safari*` and `curl_firefox*` profiles | `stocks flow`: IDX's Cloudflare rules pass only some browser fingerprints, and idx tries each installed profile |
| the full curl-impersonate set, with its `curl_safari*` and `curl_firefox*` profiles | `stocks flow --source idx`: IDX's Cloudflare rules pass only some browser fingerprints, and idx tries each installed profile (the default source, IPOT, needs nothing) |
| `mutool` from MuPDF | importing legacy PDF ownership reports |
## The MCP server