mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
feat: foreign flow (idx stocks flow, get_foreign_flow)
`idx stocks flow SYMBOL` shows what foreign investors bought and sold of a stock, session by session, from IDX's own daily trading summary: foreign buy, sell and net volume, net value at the session's average price, and the share of value traded outside the regular board, with range totals. Latest 10 sessions by default (--days, at most 60) or --from/--to. The MCP server exposes it as get_foreign_flow. - api/idx: pure parser for GetStockSummary (rows dated another session are dropped) and a provider over curl-impersonate's profile fallback with a 30 s limit; Cloudflare passes only some fingerprints. - flow: each session is cached whole by date, so other stocks' flows for the same sessions need no request. Weekends are never requested. - Idx::idx_code is shared by broker and flow for the .JK-only check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
parent
4c2f0acff3
commit
29f143fa1b
40 changed files with 1278 additions and 29 deletions
|
|
@ -244,7 +244,7 @@ name = "yahoo-fundamental"
|
|||
args = ["stocks", "fundamental", "BBCA"]
|
||||
env = { IDX_PROVIDER = "yahoo" }
|
||||
|
||||
# --- stocks: broker summaries (IPOT) -------------------------------------------
|
||||
# --- stocks: broker summaries (IPOT) and foreign flow (IDX) --------------------
|
||||
|
||||
[[case]]
|
||||
name = "stocks-broker"
|
||||
|
|
@ -267,6 +267,16 @@ name = "stocks-bandar-screen"
|
|||
formats = ["table", "json", "jsonl"]
|
||||
args = ["stocks", "bandar", "BBCA,BBRI,TLKM", "--date", "2026-10-16", "--top", "3"]
|
||||
|
||||
[[case]]
|
||||
name = "stocks-flow"
|
||||
formats = ["table", "json", "jsonl"]
|
||||
args = ["stocks", "flow", "BBCA", "--from", "2026-10-12", "--to", "2026-10-16"]
|
||||
|
||||
[[case]]
|
||||
name = "stocks-flow-latest"
|
||||
args = ["stocks", "flow", "TLKM", "--days", "3"]
|
||||
env = { IDX_MOCK_TODAY = "2026-10-18" }
|
||||
|
||||
[[case]]
|
||||
name = "error-broker-unknown-symbol"
|
||||
args = ["stocks", "broker", "ZZZZ", "--date", "2026-10-16"]
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@ _idx() {
|
|||
idx__help__stocks,financials)
|
||||
cmd="idx__help__stocks__financials"
|
||||
;;
|
||||
idx__help__stocks,flow)
|
||||
cmd="idx__help__stocks__flow"
|
||||
;;
|
||||
idx__help__stocks,fundamental)
|
||||
cmd="idx__help__stocks__fundamental"
|
||||
;;
|
||||
|
|
@ -357,6 +360,9 @@ _idx() {
|
|||
idx__stocks,financials)
|
||||
cmd="idx__stocks__financials"
|
||||
;;
|
||||
idx__stocks,flow)
|
||||
cmd="idx__stocks__flow"
|
||||
;;
|
||||
idx__stocks,fundamental)
|
||||
cmd="idx__stocks__fundamental"
|
||||
;;
|
||||
|
|
@ -411,6 +417,9 @@ _idx() {
|
|||
idx__stocks__help,financials)
|
||||
cmd="idx__stocks__help__financials"
|
||||
;;
|
||||
idx__stocks__help,flow)
|
||||
cmd="idx__stocks__help__flow"
|
||||
;;
|
||||
idx__stocks__help,fundamental)
|
||||
cmd="idx__stocks__help__fundamental"
|
||||
;;
|
||||
|
|
@ -1209,7 +1218,7 @@ _idx() {
|
|||
return 0
|
||||
;;
|
||||
idx__help__stocks)
|
||||
opts="quote history technical growth valuation risk fundamental broker bandar profile financials earnings sentiment insights news screen compare"
|
||||
opts="quote history technical growth valuation risk fundamental broker bandar flow profile financials earnings sentiment insights news screen compare"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -1292,6 +1301,20 @@ _idx() {
|
|||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__help__stocks__flow)
|
||||
opts=""
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__help__stocks__fundamental)
|
||||
opts=""
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
|
||||
|
|
@ -2239,7 +2262,7 @@ _idx() {
|
|||
return 0
|
||||
;;
|
||||
idx__stocks)
|
||||
opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help quote history technical growth valuation risk fundamental broker bandar profile financials earnings sentiment insights news screen compare help"
|
||||
opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help quote history technical growth valuation risk fundamental broker bandar flow profile financials earnings sentiment insights news screen compare help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -2418,6 +2441,40 @@ _idx() {
|
|||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__stocks__flow)
|
||||
opts="-o -q -v -h --days --from --to --output --no-color --quiet --verbose --offline --no-cache --help <SYMBOL>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
--days)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--from)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--to)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--output)
|
||||
COMPREPLY=($(compgen -W "auto table json jsonl" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
-o)
|
||||
COMPREPLY=($(compgen -W "auto table json jsonl" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__stocks__fundamental)
|
||||
opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help <SYMBOL>"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
|
|
@ -2463,7 +2520,7 @@ _idx() {
|
|||
return 0
|
||||
;;
|
||||
idx__stocks__help)
|
||||
opts="quote history technical growth valuation risk fundamental broker bandar profile financials earnings sentiment insights news screen compare help"
|
||||
opts="quote history technical growth valuation risk fundamental broker bandar flow profile financials earnings sentiment insights news screen compare help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
@ -2546,6 +2603,20 @@ _idx() {
|
|||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__stocks__help__flow)
|
||||
opts=""
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
;;
|
||||
idx__stocks__help__fundamental)
|
||||
opts=""
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Commands:
|
|||
fundamental Run full fundamental analysis on a stock
|
||||
broker Which brokers bought and sold a stock
|
||||
bandar Bandarmology: is buying more concentrated than selling
|
||||
flow Foreign buying and selling of a stock, session by session
|
||||
profile Get company profile
|
||||
financials Get financial statements
|
||||
earnings Get earnings report
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
exit: 0
|
||||
--- stdout
|
||||
{"symbol":"TLKM.JK","from":"2026-10-14","to":"2026-10-16","days":[{"date":"2026-10-14","close":2250,"change_bps":45,"volume":87159900,"value":197725738000,"foreign_buy":63030200,"foreign_sell":60811200,"foreign_net":2219000,"foreign_net_value":5033890729,"non_regular_bps":1085},{"date":"2026-10-15","close":2230,"change_bps":-89,"volume":150485300,"value":333287991000,"foreign_buy":97385100,"foreign_sell":105521800,"foreign_net":-8136700,"foreign_net_value":-18020792704,"non_regular_bps":78},{"date":"2026-10-16","close":1900,"change_bps":-1480,"volume":963614200,"value":1855341292500,"foreign_buy":679004400,"foreign_sell":727289800,"foreign_net":-48285400,"foreign_net_value":-92968634589,"non_regular_bps":1744}],"foreign_net":-54203100,"foreign_net_value":-105955536564,"foreign_share_bps":7213}
|
||||
--- stderr
|
||||
15
crates/idx-cli/tests/golden/stocks-flow-latest.table.golden
Normal file
15
crates/idx-cli/tests/golden/stocks-flow-latest.table.golden
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
exit: 0
|
||||
--- stdout
|
||||
Foreign flow for TLKM.JK, 2026-10-14 to 2026-10-16 (3 sessions)
|
||||
┌────────────┬───────┬────────┬────────┬─────────────┬──────────────┬─────────────┬───────────┬─────────┐
|
||||
│ DATE ┆ CLOSE ┆ CHG% ┆ VALUE ┆ FOREIGN BUY ┆ FOREIGN SELL ┆ FOREIGN NET ┆ NET VALUE ┆ NON-REG │
|
||||
╞════════════╪═══════╪════════╪════════╪═════════════╪══════════════╪═════════════╪═══════════╪═════════╡
|
||||
│ 2026-10-14 ┆ 2,250 ┆ +0.5% ┆ 197.7B ┆ 63.0M ┆ 60.8M ┆ +2.2M ┆ +5.0B ┆ 10.8% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-15 ┆ 2,230 ┆ -0.9% ┆ 333.3B ┆ 97.4M ┆ 105.5M ┆ -8.1M ┆ -18.0B ┆ 0.8% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-16 ┆ 1,900 ┆ -14.8% ┆ 1.9T ┆ 679.0M ┆ 727.3M ┆ -48.3M ┆ -93.0B ┆ 17.4% │
|
||||
└────────────┴───────┴────────┴────────┴─────────────┴──────────────┴─────────────┴───────────┴─────────┘
|
||||
Net foreign: -54.2M shares, about -106.0B IDR; foreign investors made up 72.1% of trading.
|
||||
Volumes are shares. Net value is estimated at each session's average price; NON-REG is the share of value traded outside the regular board (negotiated and cash).
|
||||
--- stderr
|
||||
4
crates/idx-cli/tests/golden/stocks-flow.json.golden
Normal file
4
crates/idx-cli/tests/golden/stocks-flow.json.golden
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
exit: 0
|
||||
--- stdout
|
||||
{"symbol":"BBCA.JK","from":"2026-10-12","to":"2026-10-16","days":[{"date":"2026-10-12","close":5950,"change_bps":-165,"volume":97965100,"value":586491892500,"foreign_buy":51499200,"foreign_sell":60883100,"foreign_net":-9383900,"foreign_net_value":-56178999154,"non_regular_bps":14},{"date":"2026-10-13","close":5950,"change_bps":0,"volume":122716900,"value":726575115000,"foreign_buy":88772100,"foreign_sell":83738900,"foreign_net":5033200,"foreign_net_value":29800279088,"non_regular_bps":162},{"date":"2026-10-14","close":6125,"change_bps":294,"volume":136303100,"value":828862772500,"foreign_buy":119876900,"foreign_sell":84298000,"foreign_net":35578900,"foreign_net_value":216356236186,"non_regular_bps":3178},{"date":"2026-10-15","close":6075,"change_bps":-82,"volume":134763200,"value":816554920000,"foreign_buy":120850000,"foreign_sell":73638900,"foreign_net":47211100,"foreign_net_value":286060704877,"non_regular_bps":1168},{"date":"2026-10-16","close":5175,"change_bps":-1481,"volume":1241098200,"value":6474934315000,"foreign_buy":309392800,"foreign_sell":757227900,"foreign_net":-447835100,"foreign_net_value":-2336400823441,"non_regular_bps":155}],"foreign_net":-369395800,"foreign_net_value":-1860362602444,"foreign_share_bps":5050}
|
||||
--- stderr
|
||||
4
crates/idx-cli/tests/golden/stocks-flow.jsonl.golden
Normal file
4
crates/idx-cli/tests/golden/stocks-flow.jsonl.golden
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
exit: 0
|
||||
--- stdout
|
||||
{"symbol":"BBCA.JK","from":"2026-10-12","to":"2026-10-16","days":[{"date":"2026-10-12","close":5950,"change_bps":-165,"volume":97965100,"value":586491892500,"foreign_buy":51499200,"foreign_sell":60883100,"foreign_net":-9383900,"foreign_net_value":-56178999154,"non_regular_bps":14},{"date":"2026-10-13","close":5950,"change_bps":0,"volume":122716900,"value":726575115000,"foreign_buy":88772100,"foreign_sell":83738900,"foreign_net":5033200,"foreign_net_value":29800279088,"non_regular_bps":162},{"date":"2026-10-14","close":6125,"change_bps":294,"volume":136303100,"value":828862772500,"foreign_buy":119876900,"foreign_sell":84298000,"foreign_net":35578900,"foreign_net_value":216356236186,"non_regular_bps":3178},{"date":"2026-10-15","close":6075,"change_bps":-82,"volume":134763200,"value":816554920000,"foreign_buy":120850000,"foreign_sell":73638900,"foreign_net":47211100,"foreign_net_value":286060704877,"non_regular_bps":1168},{"date":"2026-10-16","close":5175,"change_bps":-1481,"volume":1241098200,"value":6474934315000,"foreign_buy":309392800,"foreign_sell":757227900,"foreign_net":-447835100,"foreign_net_value":-2336400823441,"non_regular_bps":155}],"foreign_net":-369395800,"foreign_net_value":-1860362602444,"foreign_share_bps":5050}
|
||||
--- stderr
|
||||
19
crates/idx-cli/tests/golden/stocks-flow.table.golden
Normal file
19
crates/idx-cli/tests/golden/stocks-flow.table.golden
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
exit: 0
|
||||
--- stdout
|
||||
Foreign flow for BBCA.JK, 2026-10-12 to 2026-10-16 (5 sessions)
|
||||
┌────────────┬───────┬────────┬────────┬─────────────┬──────────────┬─────────────┬───────────┬─────────┐
|
||||
│ DATE ┆ CLOSE ┆ CHG% ┆ VALUE ┆ FOREIGN BUY ┆ FOREIGN SELL ┆ FOREIGN NET ┆ NET VALUE ┆ NON-REG │
|
||||
╞════════════╪═══════╪════════╪════════╪═════════════╪══════════════╪═════════════╪═══════════╪═════════╡
|
||||
│ 2026-10-12 ┆ 5,950 ┆ -1.6% ┆ 586.5B ┆ 51.5M ┆ 60.9M ┆ -9.4M ┆ -56.2B ┆ 0.1% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-13 ┆ 5,950 ┆ +0.0% ┆ 726.6B ┆ 88.8M ┆ 83.7M ┆ +5.0M ┆ +29.8B ┆ 1.6% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-14 ┆ 6,125 ┆ +2.9% ┆ 828.9B ┆ 119.9M ┆ 84.3M ┆ +35.6M ┆ +216.4B ┆ 31.8% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-15 ┆ 6,075 ┆ -0.8% ┆ 816.6B ┆ 120.8M ┆ 73.6M ┆ +47.2M ┆ +286.1B ┆ 11.7% │
|
||||
├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
|
||||
│ 2026-10-16 ┆ 5,175 ┆ -14.8% ┆ 6.5T ┆ 309.4M ┆ 757.2M ┆ -447.8M ┆ -2.3T ┆ 1.6% │
|
||||
└────────────┴───────┴────────┴────────┴─────────────┴──────────────┴─────────────┴───────────┴─────────┘
|
||||
Net foreign: -369.4M shares, about -1.9T IDR; foreign investors made up 50.5% of trading.
|
||||
Volumes are shares. Net value is estimated at each session's average price; NON-REG is the share of value traded outside the regular board (negotiated and cash).
|
||||
--- stderr
|
||||
Loading…
Add table
Add a link
Reference in a new issue