{"id":"f4961981-f578-4acf-bfa3-81997ab7557b","slug":"idx","kind":"mcp","name":"idx","summary":"Analyze the Indonesian stock market (IDX/IHSG) from web terminal, CLI, or any AI agents via MCP server. IHSG Prices, Fundamentals, and Screening. Free and No API keys required. Changelog: https://gith","owner":"fzn0x","repoUrl":"https://github.com/fzn0x/idx","license":"MIT","licenseTier":"permissive","trust":"community","stars":205,"match":null,"fewStars":false,"format":"mcp","createdAt":"2026-10-16T17:51:21.000Z","addedAt":"2026-10-21T07:39:13.196Z","logoUrl":"https://avatars.githubusercontent.com/u/50759463?u=4de407c994cacaa94e0d1b236b39881380fa63b1&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["data-analytics","developer-tools","cloud-infra"],"tags":["cli","finance","idx","ihsg","indonesia","rust","stocks","analytics"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-11-02T07:03:56.266Z","sample":false,"url":"https://conduits.dev/tools/idx","language":"Rust","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"assets/logo-dark.svg\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"assets/logo-light.svg\">\n    <img src=\"assets/logo.svg\" alt=\"IDX: Indonesian market data for people and agents\" width=\"480\">\n  </picture>\n</p>\n\n<p align=\"center\">\n  Indonesian stock market (IDX / IHSG) analysis for people and AI agents.<br>\n  One binary: a CLI that prints tables, a JSON mode for scripts, and an MCP server for agents.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/fzn0x/idx/actions/workflows/ci.yml\"><img alt=\"CI\" src=\"https://github.com/fzn0x/idx/actions/workflows/ci.yml/badge.svg\"></a>\n  <a href=\"LICENSE\"><img alt=\"MIT\" src=\"https://img.shields.io/badge/license-MIT-16a34a.svg\"></a>\n  <a href=\"https://ihsg.midoriresearch.com\"><img alt=\"Web terminal\" src=\"https://img.shields.io/badge/web-ihsg.midoriresearch.com-123a64.svg\"></a>\n</p>\n\n---\n\n<p align=\"center\">\n  <a href=\"https://ihsg.midoriresearch.com\"><img src=\"docs/screenshots/landing.png\" alt=\"ihsg.midoriresearch.com: IHSG with the day's chart, stocks up and down, and a stock search, in Bahasa Indonesia and English\" width=\"100%\"></a>\n</p>\n\n```sh\nidx stocks quote BBCA,BBRI,TLKM\n```\n\n```\n╭────────┬──────────────────────────────┬───────┬──────┬─────────┬───────┬───────┬───────┬───────────────┬─────────┬───────┬────────╮\n│ Symbol ┆ Name                         ┆ Price ┆ Chg  ┆ Chg %   ┆ Open  ┆ High  ┆ Low   ┆ Volume        ┆ Mkt Cap ┆ P/E   ┆ Div %  │\n╞════════╪══════════════════════════════╪═══════╪══════╪═════════╪═══════╪═══════╪═══════╪═══════════════╪═════════╪═══════╪════════╡\n│ BBCA   ┆ PT Bank Central Asia Tbk     ┆ 5,175 ┆ -900 ┆ -14.81% ┆ 5,825 ┆ 5,825 ┆ 5,175 ┆ 1,241,098,200 ┆  635.7T ┆ 10.96 ┆  6.27% │\n│ BBRI   ┆ PT Bank Rakyat Indonesia (P… ┆ 2,640 ┆ -460 ┆ -14.84% ┆ 2,640 ┆ 2,890 ┆ 2,640 ┆ 1,193,916,400 ┆  397.8T ┆  6.49 ┆ 13.48% │\n│ TLKM   ┆ Perusahaan Perseroan (Perse… ┆ 1,900 ┆ -330 ┆ -14.80% ┆ 1,900 ┆ 1,985 ┆ 1,900 ┆   963,614,200 ┆  187.3T ┆ 10.98 ┆ 10.01% │\n╰────────┴──────────────────────────────┴───────┴──────┴─────────┴───────┴───────┴───────┴───────────────┴─────────┴───────┴────────╯\nLast trade 2026-10-16 16:14 WIB · market closed · delayed data\n```\n\n```sh\nidx -o json stocks quote BBCA | jq '.data[0] | {price, change_pct, pe_trailing, dividend_yield_pct}'\n```\n\n```json\n{ \"price\": 5175.0, \"change_pct\": -14.814815, \"pe_trailing\": 10.963983, \"dividend_yield_pct\": 6.27 }\n```\n\n<p align=\"center\">\n  <img src=\"docs/screenshots/cli.png\" alt=\"idx stocks quote and idx stocks compare in a terminal\" width=\"100%\">\n</p>\n\n- **Free data, no keys.** Yahoo Finance and MSN Money for prices and\n  fundamentals, the exchange's own idx.co.id summaries for foreign flow,\n  member activity, indices and the corporate-action calendar, IDX's monthly\n  KSEI register for ownership, Indo Premier's broker summary for who bought\n  and sold (read as bandarmology by `idx bandar`) and for foreign flow in\n  rupiah (`stocks foreign --source ipot`), its corporate-action\n  pages, Kontan and CNBC Indonesia feeds for news. Every source sits behind a `Provider` trait, so\n  one breaking does not take the tool down, and `auto` fills gaps in one\n  source's fundamentals from the other.\n- **Built for agents.** `-o json` emits a stable, schema-documented envelope\n  (`idx schema quotes`), `-o jsonl` one record per line. Exit codes mean\n  something, and `-` reads tickers from stdin so commands chain. `idx mcp`\n  exposes every command as a typed MCP tool that returns the very same JSON.\n- **Works offline.** Responses are cached with per-endpoint TTLs; `--offline`\n  serves the cache instead of failing when the network is gone.\n- **Ownership, not just prices.** KSEI's monthly register of holders above 1 %\n  is imported into local SQLite with normalised entity names, so \"who owns\n  more than 1 % of BBCA\" and \"what did this fund change last month\" are one\n  command each. The monthly IDX files are mirrored to GitHub, so neither you\n  nor the web terminal imports anything by hand: `idx ownership sync`\n  installs every month in one download.\n- **Single static binary.** Rust, no runtime, Linux / macOS / Windows.\n\n## Install\n\nThe package is called **`ihsg-cli`**; the command it installs is **`idx`**\n(npm also adds `ihsg-cli` as an alias).\n\n```sh\ncargo install --locked ihsg-cli           # crates.io, builds from source (Rust 1.89+)\nnpm install -g ihsg-cli                   # npm\nnpx ihsg-cli stocks quote BBCA            # npm, no install\ncurl -fsSL https://raw.githubusercontent.com/fzn0x/idx/main/install.sh | sh   # Linux, macOS\nnix profile install github:fzn0x/idx      # Nix (Linux; Apple silicon macOS untested), with shell completions\nnix run github:fzn0x/idx -- stocks quote BBCA   # Nix, no install\n```\n\nnpm installs a small launcher; the first `idx` command downloads the\nprebuilt binary for your platform from the matching GitHub release and checks\nit against the release's `SHA256SUMS` (so does `install.sh`). The binary is\nstored next to the launcher, or in a per-user cache when the global npm\nfolder is not writable. `npm install -g github:fzn0x/idx` installs the same\nlauncher from this repository's `main`; npm 12 only installs from git with\n`--allow-git=all`.\n\nWindows without npm: download `idx-<version>-x86_64-pc-windows-msvc.zip` from\nthe [releases page](https://github.com/fzn0x/idx/releases). Static Linux\nbinaries are musl builds and run on any distribution. The development version installs\nwith `cargo install --git https://github.com/fzn0x/idx`.\n\nNix builds idx from source on your machine (there is no binary cache), so the\nfirst `nix profile install` or `nix run` takes several minutes. The bare\n`github:fzn0x/idx` ref follows `main`; add a release tag to pin one, as in\n`github:fzn0x/idx/v0.8.2` (the first release with the flake). For MCP clients,\ninstall once and register `$HOME/.nix-profile/bin/idx`, written as an absolute\npath, with the argument `mcp`: `idx mcp setup` prints the binary's resolved\n`/nix/store` path, which an upgrade followed by garbage collection removes.\n`nix run github:fzn0x/idx#mcp` starts the same server without installing, for\ntrying it from a terminal; do not make it a client's server command, because a\ncold start (the first run, or the first after `main` moves) builds idx before\nthe server answers, which takes longer than clients wait. `overlays.default`\nadds `pkgs.idx` for NixOS and home-manager configurations, and `nix develop` in\na clone gives the toolchain from `rust-toolchain.toml` plus Node for `web/`. On\nNixOS, point `idx stockbit login` at a browser with\n`IDX_BROWSER=$(command -v chromium)`.\n\nShell completions:\n\n```sh\nidx completions bash > ~/.local/share/bash-completion/completions/idx\nidx completions zsh  > ~/.zfunc/_idx\nidx completions fish > ~/.config/fish/completions/idx.fish\nidx completions powershell | Out-String | Invoke-Expression\n```\n\n## Commands\n\n| command | what it does |\n| --- | --- |\n| `stocks quote <SYMBOLS>` | Delayed quote for one or many tickers (`BBCA,BBRI` or `BBCA BBRI`; `.JK` optional; `-` reads them from stdin; another Yahoo Finance market by its suffix, e.g. `D05.SI`) |\n| `stocks history <SYMBOL> [--period 1y] [--interval 1d] [--limit 30]` | OHLCV candles with a summary line. Periods `1d 5d 1mo 3mo 6mo 1y 2y 5y 10y ytd max`, intervals `1m 2m 5m 15m 30m 1h 1d 1wk 1mo` (`1m` covers at most `5d`, `2m` at most `1mo`) |\n| `stocks intraday <SYMBOL> [--interval 1m] [--limit 15]` | The latest session by the minute (today's while the market trades, otherwise the last one): OHLCV bars, the quote with its previous close and change, and a summary of the bars with VWAP computed by idx. Yahoo's chart, no login, about 10 minutes behind the market; works for IHSG (`^JKSE`) too. `--provider stockbit` makes the bars realtime |\n| `stocks profile <SYMBOL>` | Sector, industry, description, size, headquarters, management |\n| `stocks financials <SYMBOL> [--period annual\\|quarterly\\|ttm] [--statement income,balance,cash]` | Income statement, balance sheet, cash flow; `--statement` (comma-separated or repeated, `all` by default) picks the tables shown |\n| `stocks earnings <SYMBOL> [--annual\\|--quarterly] [--reported\\|--estimates]` | Quarterly EPS vs estimates with surprise, yearly and quarterly revenue/earnings, analyst consensus for this and next quarter and fiscal year, next earnings date; the flags keep only those rows in every format (`--history`/`--forecast` are aliases) |\n| `stocks dividends <SYMBOL>` | Every cash dividend (ex-date, split-adjusted amount) and split; yearly totals, TTM total and yield, payout streak |\n| `stocks returns <SYMBOLS> [--benchmark ^JKSE]` | 1w/1m/3m/6m/YTD/1y/3y/5y returns, CAGR, volatility, max drawdown, best/worst day, 52-week position, and excess over IHSG |\n| `stocks brief <SYMBOL>` | One page: quote, verdict, signal, returns vs IHSG, dividends, top holders, headlines, with plain-language key points |\n| `stocks fundamental <SYMBOL>` | P/E, P/B, PEG, EV/EBITDA, ROE, ROA, margins, leverage, growth, dividend, analyst targets, plus a plain-language verdict that lists every rule it applied (negative equity counts against a company; multiples that mix USD statements with an IDR price are marked and not scored) |\n| `stocks growth <SYMBOL>` | Growth lens: latest-quarter revenue and earnings growth, forward EPS against trailing, and revenue and net income across the annual statements (CAGR, how often revenue grew, a turn from profit to loss); every rule with its points, every skipped rule with the reason |\n| `stocks valuation <SYMBOL>` | Valuation lens: P/E, PEG, P/B, P/S, EV/EBITDA, dividend yield and the analyst target (3 or more analysts), each scored; multiples that mix USD statements with an IDR price are shown, not scored |\n| `stocks risk <SYMBOL>` | Risk lens: negative equity, debt/equity, current ratio, losses, and beta, volatility and largest fall against IHSG over a year of daily closes (computed by idx, not the source's beta). `stocks fundamental <SYMBOL> --lens growth\\|valuation\\|risk` gives the same three readings |\n| `stocks technical <SYMBOL> [--period 1y]` | RSI, MACD, SMA/EMA 20/50/200, Bollinger, ATR, volume, support/resistance, trend, buy/sell/neutral signal with reasons |\n| `stocks compare <SYMBOLS>` | Side-by-side fundamentals for two or more tickers |\n| `stocks screen [--preset P] [-f \"pe<15,roe>15,sector=financial\"] [--sort KEY] [--asc\\|--desc] [--returns]` | Screener over ~950 IDX tickers: one batch quote for valuation/size/dividend keys and `range` (position in the 52-week range), one request for MSN's whole-market list for the return keys `ret1w ret1m ret3m ret6m retytd ret1y`, per-company fundamentals for the rest (capped, largest first). Presets defined by idx: `top-performers`, `worst-performers`, `high-dividend`, `low-pe`, `52w-high`, `52w-low`, `high-volume`, `large-cap`. A P/B over statements in another currency than the price (USD reporters such as BSSR) is marked with `*`, and `pb` and `ps` filters and sorts read such P/B and P/S as missing |\n| `stocks news [SYMBOL] [--company-only] [--source kontan\\|cnbcindonesia\\|all]` | Headlines from Kontan and CNBC Indonesia RSS. With a ticker, the company's headlines come first, then market context fills the limit |\n| `stocks sentiment <SYMBOL>` | Aggregate lexicon sentiment over the matching headlines, with counts, terms and the strongest headlines |\n| `stocks insights <SYMBOL> [--all]` | MSN Money's rule-based statements (about 30, in Valuation, Earnings, Growth, Performance, Health), each rated good, bad, neutral or not rated against the peers, the industry, the company's own history or the price, with the peer tickers |\n| `stocks actions <SYMBOL> [--since D]` | Corporate actions from IPOT, upcoming ones included: cash and stock dividends, splits, bonus shares, rights issues (amount, ratio old:new, cum, ex, recording and payment dates, status, announcement), shareholders' meetings (RUPS: time, venue, agenda), listed warrants and rights |\n| `stocks foreign <SYMBOL> [--days 20 \\| --from D --to D] [--source idx\\|ipot]` | Foreign buying and selling per session, totals and the net-buy/net-sell streak: from idx.co.id by default (shares on the regular board, as IDX publishes them, net and net × close), or from IPOT with `--source ipot` (rupiah across all boards, value traded and foreign share; works where idx.co.id refuses the connection) |\n| `stocks list [--board B] [--search S]` | Listed stocks from idx.co.id with listing board, listing date and shares; the bundled universe stands in offline |\n| `ownership import --latest\\|--all [--family composition]\\|--file F\\|--url U` | Import KSEI's monthly \">1 % holders\" register (XLSX or PDF) into local SQLite. `--latest`/`--all` read idx.co.id and fall back to the CI-maintained GitHub mirror. `--family composition`, or a ZIP/TXT given to `--file`/`--url`, imports KSEI's composition archive into its own tables |\n| `ownership sync [--force]` · `ownership status` | Install the prebuilt SQLite snapshot CI builds from the mirrored files (one download, SHA-256 checked), then any newer month; a database holding months the snapshot lacks is kept unless `--force` (a `.bak` copy is kept). `status` shows the local database, the mirror and what sync would do |\n| `ownership ticker <SYMBOL> [--as-of D]` | Holders above 1 % of a ticker (a holder's accounts summed; controller/affiliate flag from the 30 Oct 2026 register) |\n| `ownership entity \"<NAME>\" [--as-of D] [--exact]` | Every position of a person or institution, spelling variants grouped (`--exact`: one holder by its key) |\n| `ownership search \"<NAME>\" [--limit N] [--as-of D]` | Find holders by name in every imported month: every spelling KSEI printed, words in any order, with the key for `entity --exact` |\n| `ownership cross-holders [--min-issuers N] [--type T] [--origin foreign\\|local]` | Holders ranked by the number of issuers they own more than 1 % of, with every position |\n| `ownership concentration [SYMBOLS] [--by hhi\\|top1\\|top3] [--ascending]` | Issuers ranked by disclosed ownership concentration: HHI over the listed holders, largest and top-3 stakes, undisclosed percent, controller stake |\n| `ownership graph <ROOT> [--depth N] [--min-pct P] [--max-nodes N] [--dot]` | Holder-issuer network around a ticker or holder, as a table, JSON or Graphviz DOT |\n| `ownership resolve candidates\\|merge\\|map\\|list\\|undo` | Suggest, merge and split holder spellings KSEI printed differently between months (rules stored in the local database, undoable) |\n| `broker <SYMBOLS> [--date D \\| --from D --to D] [--investor all\\|foreign\\|domestic] [--board all\\|regular\\|cash\\|negotiated] [--source auto\\|ipot\\|stockbit]` | Broker summary: which brokers bought and sold a ticker, ranked by lots, with broker name, class (local, foreign, BUMN), lots, value and average price, plus total value and foreign net (for one investor side, what it bought and sold). IPOT publishes the top 10 per side; once `idx stockbit login` has stored a working session, `auto` (the default) lists every broker from Stockbit instead, and JSON says how many brokers each side had and whether its list is complete. Latest session with trades by default; `-o csv` gives one row per broker |\n| `bandar detect <SYMBOLS> [--period 1d\\|1w\\|1m\\|3m] [--top 1\\|3\\|5\\|10] [--daily]` | Bandarmology: are the top brokers accumulating or distributing? The top 1/3/5/10 buyers against as many sellers (bandar value and volume), net as a share of the value traded, the phase against the price move, the top buyers' average against the price, foreign flow; `--daily` adds the bandar movement session by session. See [Bandarmology](#bandarmology) |\n| `bandar screen [--preset P] [-f FILTER] [--period P] [--scan N]` | Bandarmology screener over the 100 most traded stocks by value: presets `big-accumulation`, `accumulation`, `distribution`, `big-distribution`, `quiet-accumulation`, `quiet-distribution`, `foreign-accumulation`, `below-bandar`, or filters on `acc`, `net`, `fnet`, `fpct`, `gain`, `chg`, `turnover`, `conc`, `signal`, `phase` |\n| `bandar broker <CODE> [--period P] [--scan N \\| --symbols S]` | Broker activity: where one broker (e.g. `YP`) sits among the top 10 buyers or sellers of the 100 most traded stocks, with rank, lots, value, average, share of the stock's value and net where both sides are visible |\n| `market overview [--top 10]` | IHSG, breadth, turnover, gainers, losers, most traded, heavyweights from one pass over the universe |\n| `market foreign [--date D] [--top 10] [--sort value\\|shares] [--source idx\\|ipot]` | Top foreign net buying and net selling across every stock for one session (idx.co.id, in shares), with the market's foreign totals; `--source ipot` ranks the 100 most traded stocks (not every stock) by each one's foreign net in rupiah from IPOT's broker summaries, for the latest session IPOT has published, with the market's value traded and foreign buying, selling and net from IPOT's market statistics (its own shape, `idx.market.foreign.value.v1`) |\n| `market brokers [--date D] [--top 20] [--sort value\\|volume\\|frequency] [--source auto\\|idx\\|ipot]` | Every exchange member's trading for one session, ranked, with each member's share of all members' value: from idx.co.id, or from IPOT when idx.co.id refuses, cannot be reached, fails or has not published the session yet and IPOT holds that session (`auto`, the default), or with `--source ipot` (its latest session), which adds each member's class and net to the table, and its value bought and sold to `-o json`, `csv` and `jsonl` |\n| `market indices [CODE...] [--date D]` | Every IDX index (IHSG, LQ45, IDX30, sector and thematic) for one session: close, change, range, constituents, value, market cap |\n| `market calendar [--from D --to D \\| --date D] [--symbol S] [--type T]` | The exchange calendar: dividend and rights dates (cum, ex, recording, payment), RUPS, public exposes, listings; bonds and buyback reports with `--type all`. Default: the next 14 days |\n| `market holidays [--year Y \\| --all] [--upcoming]` | Trading holidays (Kalender Libur Bursa, bundled), the year's trading days, whether today is a trading day and the next one |\n| `market astro [--date D] [--days N] [--natal launch\\|base\\|none] [--backtest [--period P]]` | Traditional astrology for IHSG, computed offline: the Sun, Moon and planets (sign, degree, speed, retrograde) at 16:00 WIB, Moon phase, Mercury retrograde, Javanese weton, Chinese shio, phases, stations and ingresses ahead, aspects to IHSG's chart; `--backtest` tests them on IHSG's daily returns. See [Astrology](#astrology) |\n| `market members [--search S]` | Exchange members (broker codes) with names and licences |\n| `ownership changes --from D --to D [--symbol S] [--min-change PP] [--limit N --offset N]` | New holders, exits and position changes between two months, paged, with a summary (counts, foreign vs local net, by investor type, issuer flow, top moves) over every match |\n| `ownership foreign [SYMBOL] [--from D --to D]` | Disclosed foreign (KSEI `A`) vs local stake: one issuer month by month, or every issuer's change between two months |\n| `ownership composition <SYMBOL> [--as-of D]` | KSEI's monthly \"Kepemilikan Efek\" archive: local vs foreign shares in nine investor types, as a percent of the scripless shares, with the scripless share of listed shares and the trend over imported months. Downloads the newest month on first use |\n| `ownership institutions <SYMBOL>` | Institutional and fund holders from MSN Money (live, no import): shares, % of shares outstanding, the change since each holder's last report and a `new`/`buyer`/`seller`/`exited`/`unchanged` signal, with institutional, fund and free-float percentages and counts per list. See [docs/ownership.md](docs/ownership.md#institutional-and-fund-holders-msn) |\n| `stockbit login [--page [--port N]] \\| status \\| logout` | Opt-in: store your own Stockbit session, signed in in a browser idx opens or, over SSH and for Google sign-in, through a page on 127.0.0.1 for your own browser (idx never sees the password; every session is checked before it is stored); check when its tokens expire; delete it. See [docs/stockbit.md](docs/stockbit.md) |\n| `stockbit orderbook <SYMBOL>` | The live depth ladder and session foreign flow (your Stockbit session) |\n| `stockbit actions <SYMBOL> [--kind K] [--from D --to D]` · `stockbit calendar [--from D --to D \\| --days N] [--kind K]` | Corporate actions from your Stockbit session, one structured row each: a stock's whole history (dividends since 2009 for BBCA, splits, bonus, rights, warrants, RUPS, public exposes, tender offers), or the market's calendar with rights, tender and IPO offers still open |\n| `stockbit movements [SYMBOL] [--from D --to D] [--holder ID] [--action A] [--source idx\\|ksei] [--pages N]` | Insider and 5 % holder movements from your Stockbit session: IDX insider disclosures (with the trade price) and KSEI's 5 % holder changes (Stockbit's estimated price kept apart), shares exact and signed |\n| `watch [add\\|rm\\|list\\|clear\\|returns]` | Plain-text watchlist next to the config; `idx watch` quotes it |\n| `ownership releases\\|discover [--family F --limit N]\\|path` | Imported months, files published on idx.co.id and ksei.co.id (families `above1`, `above5`, `investor-type`, `classification`, `composition`, `all`, each marked `importable`), database path |\n| `mcp [--http ADDR --token T]` · `mcp setup <client>` | MCP server over stdio or streamable HTTP, and the registration recipe for 25 agent harnesses. 47 tools: `idx_brief`, `idx_market`, `idx_quote`, `idx_history`, `idx_intraday`, `idx_profile`, `idx_financials`, `idx_earnings`, `idx_dividends`, `idx_returns`, `idx_fundamental`, `idx_technical`, `idx_compare`, `idx_screen`, `idx_news`, `idx_sentiment`, `idx_insights`, `idx_corporate_actions`, `idx_foreign`, `idx_instruments`, `idx_market_foreign`, `idx_market_brokers`, `idx_indices`, `idx_calendar`, `idx_holidays`, `idx_astro`, `idx_members`, `idx_broker`, `idx_bandar`, `idx_bandar_screen`, `idx_bandar_broker`, `idx_ownership_ticker`, `idx_ownership_entity`, `idx_ownership_changes`, `idx_ownership_foreign`, `idx_ownership_releases`, `idx_ownership_search`, `idx_ownership_rankings`, `idx_ownership_graph`, `idx_ownership_composition`, `idx_ownership_snapshot`, `idx_ownership_institutions`, `idx_orderbook`, `idx_stockbit_status`, `idx_stockbit_login`, `idx_stockbit_actions`, `idx_stockbit_movements` |\n| `schema [name]` | JSON Schema for every payload (`quotes`, `history`, `intraday`, `profile`, `fundamentals`, `growth`, `valuation`, `risk`, `compare`, `financials`, `earnings`, `dividends`, `returns`, `market`, `brief`, `technical`, `screen`, `news`, `sentiment`, `insights`, `actions`, `foreign`, `foreign-value`, `instruments`, `market-foreign`, `market-brokers`, `indices`, `calendar`, `holidays`, `astro`, `members`, `broker`, `bandar`, `bandar-screen`, `bandar-broker`, `ownership-*`, `stockbit-*`, `watchlist`, `error`, ...) |\n| `config init\\|set\\|get\\|path` | Config file management |\n| `cache stats\\|clear\\|path` | Response cache management |\n| `completions <shell>` | bash, zsh, fish, powershell, elvish |\n| `version` | Version, build and default paths |\n\nGlobal flags work anywhere on the line:\n\n| flag | env | meaning |\n| --- | --- | --- |\n| `-o, --output table\\|json\\|jsonl\\|csv\\|auto` | `IDX_OUTPUT` | Output format: tables for people, the JSON envelope for programs, one JSON record per line (`jsonl`), CSV rows of the payload's main list for spreadsheets; `auto` is a table on a terminal and the JSON envelope when piped (opt-in: the default stays `table`) |\n| `--provider auto\\|yahoo\\|msn\\|stockbit` | `IDX_PROVIDER` | Data source (`auto` tries Yahoo, then MSN, and merges fundamentals; `stockbit` is opt-in realtime from your own session, see [docs/stockbit.md](docs/stockbit.md)) |\n| `--history-provider auto\\|yahoo\\|stockbit` | `IDX_HISTORY_PROVIDER` | Where candles come from; `auto` follows `--provider` (Yahoo for `msn`; under `stockbit`, Stockbit where its bars match the request and Yahoo otherwise or when Stockbit fails). See [docs/configuration.md](docs/configuration.md#history-provider) |\n| `--offline` | `IDX_OFFLINE=1` | Never touch the network |\n| `--no-cache` | `IDX_NO_CACHE=1` | Ignore cached responses |\n| `--timeout SECS` | `IDX_TIMEOUT_SECS` | Per-request timeout (default 15) |\n| `--no-color` | `NO_COLOR` | Plain tables |\n| `-v, -vv, -vvv` | `IDX_LOG` | Diagnostics on stderr, including every URL requested |\n| `-q, --quiet` | `IDX_QUIET=1` | stderr carries errors only: no warnings, notes or loading line. stdout is unchanged and `-o json` keeps `warnings` in the envelope |\n| | `IDX_EXCHANGE=SI` | Yahoo Finance suffix a bare ticker gets in `stocks quote`, `history`, `profile`, `financials`, `earnings`, `dividends`, `returns`, `fundamental`, `technical`, `compare` and `watch` (default `JK`, IDX). An explicit suffix (`D05.SI`) always works there; every other command reads IDX tickers only |\n| | `IDX_PROGRESS=never\\|always` | The loading line on stderr (`⠹ 6s · waiting on investasi.kontan.co.id 6s`) shows on a terminal only; `never` hides it, `always` forces it |\n\n## For scripts and agents\n\n```sh\nidx -o json stocks history TLKM --period 1y --interval 1wk \\\n  | jq '.data.summary.change_pct'\nidx -o jsonl market holidays --year 2026 | jq -r .date      # one record per line\nidx -o jsonl stocks screen -f 'pe<10,roe>15' -n 5 | idx stocks compare -\necho BBCA BBRI | idx stocks quote -                         # tickers from stdin\nidx -q -o csv stocks quote BBCA BBRI > quotes.csv           # stderr: errors only\n```\n\n- stdout carries exactly one JSON document (`-o json`) or one JSON record per\n  line (`-o jsonl`); everything else goes to stderr. Errors are one JSON line\n  on stderr in both modes.\n- `-o auto` (or `IDX_OUTPUT=auto`) prints a table on a terminal and the JSON\n  envelope when stdout is piped or captured.\n- `-` in a ticker list (`stocks quote|returns|compare`, `broker`, `bandar`,\n  `watch add|rm`) reads tickers from stdin: plain text (spaces, commas or lines,\n  `#` comments) or idx's own `-o json` / `-o jsonl` output.\n- The envelope records `provider`, `fetched_at`, `cached`, `stale`, `stale_reason` (`offline` or `fetch_failed`, only when stale) and `warnings`.\n- Fields the source did not return are `null`. Nothing is estimated.\n- Quotes carry `behind_session`: `true` when the last trade is dated before the latest IDX session that has ended (a suspended stock, one that did not trade, or a source that lags the close), using the bundled holiday calendar.\n- Exit codes: `0` ok · `2` usage · `3` network/provider (JSON code `blocked` when the source's bot protection, Cloudflare, refused the request) · `4` not found · `5` offline cache miss · `6` config.\n\nFull field reference: [docs/json-output.md](docs/json-output.md).\nConfiguration and cache: [docs/configuration.md](docs/configuration.md).\nCalling idx from an agent (rules, exit codes, recipes, an agent-led install):\n[docs/agent-guide.md](docs/agent-guide.md).\n\n### MCP server\n\n`idx mcp` serves the Model Context Protocol over stdio, which is what every\nlocal agent launches. `idx mcp --http` serves the same tools over streamable\nHTTP for products that only connect to a URL. `idx mcp setup <client>` prints\nthe exact command or config snippet for your agent with the absolute path of\nthe binary filled in, because GUI apps rarely inherit your shell `PATH`:\n\n```sh\nidx mcp setup --list          # every supported client\nidx mcp setup codex           # prints: codex mcp add idx -- /abs/path/idx mcp\nidx mcp setup muse            # prints the settings.json block Meta's Muse Code expects\n```\n\n| agent | register idx |\n| --- | --- |\n| Claude Code | `claude mcp add --scope user idx -- idx mcp` |\n| Claude Desktop | `claude_desktop_config.json` → `\"mcpServers\": {\"idx\": {\"command\": \"idx\", \"args\": [\"mcp\"]}}` |\n| OpenAI Codex (CLI, IDE extension, app) | `codex mcp add idx -- idx mcp`, or `[mcp_servers.idx]` with `command`/`args` in `~/.codex/config.toml` |\n| Meta Muse Code | `~/.config/muse/settings.json`: `{\"schema_version\": 1, \"mcp_servers\": {\"idx\": {\"transport\": \"stdio\", \"command\": \"idx\", \"args\": [\"mcp\"], \"mode\": \"optional\"}}}` |\n| Google Antigravity (IDE, CLI) | `~/.gemini/config/mcp_config.json` → `mcpServers.idx` with an absolute `command` and no `type` field |\n| Gemini CLI | `gemini mcp add -s user idx idx mcp` |\n| GitHub Copilot CLI | `copilot mcp add idx -- idx mcp` |\n| VS Code (Copilot agent mode) | `code --add-mcp '{\"name\":\"idx\",\"command\":\"idx\",\"args\":[\"mcp\"]}'`, or `.vscode/mcp.json` → `servers.idx` with `\"type\": \"stdio\"` |\n| Cursor · Windsurf · JetBrains · Warp · Trae · Augment | paste `{\"mcpServers\": {\"idx\": {\"command\": \"idx\", \"args\": [\"mcp\"]}}}` into the MCP settings (file paths in `idx mcp setup`) |\n| Kiro (IDE and CLI, formerly Amazon Q Developer CLI) | `~/.kiro/settings/mcp.json`, or `kiro-cli mcp add --name idx --command idx --args mcp` |\n| Zed | `settings.json` → `\"context_servers\": {\"idx\": {\"command\": \"idx\", \"args\": [\"mcp\"], \"env\": {}}}` |\n| Cline · Roo Code · Kilo Code | MCP Servers → Configure → `mcpServers.idx` with `\"type\": \"stdio\"` |\n| Continue | `~/.continue/mcpServers/idx.yaml` (`idx mcp setup continue` prints it) |\n| OpenCode | `opencode.json` → `\"mcp\": {\"idx\": {\"type\": \"local\", \"command\": [\"idx\", \"mcp\"], \"enabled\": true}}` |\n| Goose | `goose configure` → Add Extension → Command-line Extension → `idx mcp` |\n| Factory Droid | `droid mcp add idx \"idx mcp\"` |\n| Mistral Vibe | `~/.vibe/config.toml` → `[[mcp_servers]]` with `name = \"idx\"`, `transport = \"stdio\"`, `command`, `args` |\n| Qwen Code | `qwen mcp add idx idx mcp` |\n| Crush | `crush.json` → `\"mcp\": {\"idx\": {\"type\": \"stdio\", \"command\": \"idx\", \"args\": [\"mcp\"]}}` |\n| ChatGPT · Claude.ai · Perplexity · Devin · Manus | remote only: `idx mcp --http 127.0.0.1:8765 --token T --allowed-host '*'` behind an HTTPS tunnel, then add `https://<host>/mcp` as a connector with header `Authorization: Bearer T` |\n\nA bare `idx` in the table assumes the binary is on `PATH`; `idx mcp setup`\nsubstitutes the absolute path, and `--npx` swaps in `npx -y ihsg-mcp`: the\nnpm package [`ihsg-mcp`](npm-mcp/README.md) runs `idx mcp` without installing\nidx, fetching the release binary on first start and checking it against the\nrelease's `SHA256SUMS` (`cmd /c npx -y ihsg-mcp` on Windows; from this\nrepository `npx -y github:fzn0x/idx mcp` still works). Each entry's remote\n(HTTP) form and the config file locations are in [docs/mcp.md](docs/mcp.md).\n\n```sh\nclaude mcp add --scope user idx -- npx -y ihsg-mcp   # no idx install needed (Node 18+)\nnpx -y ihsg-mcp setup cursor                         # any client's recipe, in the npx form\n```\n\nThe server lists 47 tools with typed input schemas kept to the subset every\nharness accepts (no nullable type arrays, no `format` or `default` keywords)\nand one-paragraph descriptions. `idx_brief` is the one-call starting point;\n`idx_fundamental` takes an optional `lens` (`growth`, `valuation` or `risk`)\nfor the focused readings of `stocks growth|valuation|risk`;\neach tool returns the same envelope as `-o json`, so an agent that learned the\nCLI already knows the tools. Failures come back as tool errors carrying\n`{error: {code, message}, exit_code}`.\n\n## Bandarmology\n\n\"Bandar\" is market slang for the big, concentrated money that moves a stock.\n`idx bandar` reads it from the IPOT broker summary, whose top 10 buyers and\ntop 10 sellers are ranked by lots. For N = 1, 3, 5 and 10 the top N buyers'\nvalue minus the top N sellers' value is the bandar value (in lots, the bandar\nvolume); its share of all value traded is the reading, with the headline on\nthe top 5 (`--top`):\n\n| Top-N net, % of value | Signal | Phase when the price over the range is... |\n|---|---|---|\n| +15 or more | `big_accumulation` | up more than 2 %: `markup`; otherwise `quiet_accumulation` |\n| +5 to +15 | `accumulation` | as above |\n| -5 to +5 | `neutral` | `neutral` |\n| -15 to -5 | `distribution` | down more than 2 %: `markdown`; otherwise `quiet_distribution` |\n| -15 or less | `big_distribution` | as above |\n\nQuiet accumulation is the usual \"bandar buying quietly\": concentrated buying\nwhile the price stalls or falls. Each reading also gives the top buyers'\naverage price against the last price (`bandar_gain_pct`: below zero they sit\nunder water), how much of the value the top buyers took, the foreign net\nflow, and the rules in plain words (`reasons`). `--daily` adds the session\nseries with its cumulative net and streak. The thresholds were set against\nthe 40 most traded stocks in October 2026, where about half of single\nsessions read neutral. They are heuristics, not advice.\n\n```sh\nidx bandar detect BBCA                         # last week, top 5 vs top 5\nidx bandar detect BBRI --period 1m --daily     # with the bandar movement\nidx bandar screen --preset quiet-accumulation  # buying while the price stalls\nidx bandar screen -f 'acc>=5,fpct>=5,gain<0'   # with foreigners, under the bandar's average\n```\n\n## Astrology\n\n`idx market astro` is traditional astrology done carefully: the positions are\nastronomy, computed on this machine from JPL's Keplerian elements (Standish),\nSchlyter's Jupiter-Saturn terms and Meeus's lunar theory, and checked in the\ntests against JPL Horizons (Sun and Moon within 0.01°, planets within 0.04°,\n1800-2050) and the USNO moon phases (within 2 minutes). The Javanese weton\n(day, pasaran, neptu) and the Chinese shio (Imlek computed from new moons and\nsolar terms, checked against the published dates 1924-2050) come with it, and\nIHSG's own chart is cast for noon WIB on its launch (1983-04-01) or base date\n(1982-08-10, value 100), dates from secondary sources (Antara, Bisnis.com,\nCEIC) not yet confirmed against an IDX publication.\n\n`--backtest` asks whether any of it shows up in the index: IHSG's daily\nreturns grouped by lunar phase, Mercury retrograde or direct, pasaran and Sun\nsign, each group tested against all other days (Welch's t, Bonferroni over the\n27 groups). On every session from 1990-04-09 to 2026-11-04 (8,904 returns) no\ngroup passes Bonferroni; the nominal p < 0.05 groups (full moon -0.07 % a day,\nCapricorn +0.19 %, Pisces -0.10 %, Leo -0.09 %) are the lunar-effect and\nmonth-of-year patterns the finance literature already knows, since Sun signs\nfollow the calendar. v1 prints no interpretations; it is not a forecast or\ninvestment advice.\n\n```sh\nidx market astro                          # today: planets, Moon, Mercury, weton, shio\nidx market astro --date 2026-12-24 --days 7\nidx market astro --backtest               # every session since 1990\nidx -o json market astro --backtest --period 10y | jq '.data.backtest.factors[0]'\n```\n\n## Data sources\n\n| source | used for | auth | notes |\n| --- | --- | --- | --- |\n| Yahoo Finance `v8/finance/chart` | history, intraday minute bars, quote fallback | none | IDX tickers carry the `.JK` suffix; another market's suffix (`D05.SI`) passes through for the Yahoo-backed commands. One-minute bars for up to 8 days per request and two-minute bars for the last 60 days (HTTP 422 beyond); minutes without a trade have no bar and the opening auction's volume is in no bar. `range=max` comes back monthly, so `--period max` with daily or weekly bars asks for `period1=0&period2=9999999999` |\n| Yahoo Finance `v7/finance/quote` | quotes | anonymous cookie + crumb, cached 6 h | Falls back to the chart endpoint (fewer fields) if rejected |\n| Yahoo Finance `v10/finance/quoteSummary` | profile, fundamentals, earnings | same session | Statement modules are empty at the source, so they are not used |\n| Yahoo Finance `fundamentals-timeseries` | financials | none | Annual, quarterly and TTM statement items |\n| idx.co.id \"Data Kepemilikan Saham\" page + `Media/*.xlsx` | ownership (KSEI monthly register) | none, but Cloudflare bot protection | Mirrored three times a day by GitHub Actions to the [`ownership-data`](https://github.com/fzn0x/idx/releases/tag/ownership-data) release (byte-identical files, SHA-256 in `index.json`). The CLI tries idx.co.id first, then the mirror; a Cloudflare refusal is the JSON error code `blocked`; `idx ownership sync` installs the prebuilt SQLite snapshot published next to the files. See [docs/ownership.md](docs/ownership.md) |\n| ksei.co.id \"Kepemilikan Efek\" page (`id/publikasi/data-dan-statistik/kepemilikan-efek`) + `storage/Download/BalanceposEfek*.zip` | ownership composition (local vs foreign by investor type, monthly) | none, no bot protection | One ZIP a month (about 125 KB, one pipe-separated TXT), scripless holdings only. The listing is cached 12 h; archives go into the local ownership database. CLI and MCP. See [docs/ownership.md](docs/ownership.md#ksei-composition-kepemilikan-efek) |\n| Indo Premier (IPOT) `module/saham/include/data-brokersummary.php` | broker summary, foreign flow (`--source ipot`) | none | Top 10 buyers and sellers per range, ranked by lots; figures of a million and more are rounded to one decimal by the source (`rounded: true`). Its nginx answers spurious 404s in bursts, so a 404 is retried with backoff. Over a range that reaches its latest session the source's foreign net counts that session twice, so idx rebuilds a range's foreign net from single sessions. Broker names come from IDX's member list, bundled as `data/brokers.csv`; `bandar` computes its readings from these summaries |\n| idx.co.id JSON endpoints behind its pages (`primary/TradingSummary/GetStockSummary`, `GetBrokerSummary`, `GetIndexSummary`, `ListedCompany/GetTradingInfoSS`, `Home/GetCalendar`, `StockData/GetSecuritiesStock`, `ExchangeMember/GetBrokerSearch`) | foreign flow, member activity, indices, the exchange calendar, listed stocks, members | none, but Cloudflare bot protection | Foreign trading is published in shares only; `foreign_net_value_at_close` (net shares × close) is a rupiah scale, not the traded value. The bot protection now and then refuses a request from any network, and data centres and CI runners more often: a challenged request is asked again for 30 s (under `market brokers --source auto`, only when IPOT cannot stand in), then a cached copy is served marked stale, and with none the `blocked` error says what to do. Closed sessions are cached for 30 days |\n| Indo Premier (IPOT) `module/saham/include/corporateaction.php` | corporate actions, RUPS, warrants and rights | none | Same module and spurious-404 retry as the broker summary; repeated rows are dropped |\n| Indo Premier (IPOT) `module/saham/include/ma-topbroker.php`, dated by `statistic.php?code=BBCA` | member activity (`market brokers --source ipot`, and `auto` when idx.co.id refuses, cannot be reached, fails or has not published the session; the web terminal's `brokers`) | none | Every broker of IPOT's latest session, ranked by value, with value bought, sold and net, shares, trades and class. The page takes no date, so past sessions cannot be asked for: the session comes from a second page, the `Today` row of BBCA's statistics, read together with the ranking. Figures of a million and more are abbreviated to two decimals (`rounded: true`); same spurious-404 retry. When the two pages roll over to a new session has not been measured, so a ranking read on a trading day from 08:58 to 19:00 WIB is refused rather than given a date it may not have, and in those hours IPOT is not asked unless a copy read outside them is cached. Until it is measured, both pages are assumed to have rolled over by 19:00 WIB: a date page naming an older session than the latest that has ended is refused (unless the pair is the one accepted for that session before), and a ranking that has not rolled over by then while its date page has is caught only when this cache remembers the previous session's ranking, from an earlier IPOT read. A broker IPOT lists apart under another member's code (`JB`, `[CC] BJB Sekuritas Jawa Barat`) is counted in that member, as idx.co.id counts it. On 2026-11-06 the two sources listed the same 88 members with trades and the same trades for each; values and volumes agreed within about one unit of IPOT's second decimal |\n| Indo Premier (IPOT) `module/saham/include/ma-marketstatistic.php`, dated by `statistic.php?code=BBCA` | the market's foreign totals in rupiah (`market foreign --source ipot`) | none | IPOT's latest session summed up by board (regular, negotiated, cash, total) and by foreign investors' side (bought, sold, net), in value, lots and trades; figures of a million and more are abbreviated to two decimals. Like the broker ranking it takes no date and is dated by BBCA's statistics page under the same rules (refused on a trading day from 08:58 to 19:00 WIB), with its own copy of that page. For 2026-11-06 its board values matched idx.co.id's; it does not say which boards its foreign figures cover, and its foreign lots were above idx.co.id's regular-board foreign shares |\n| computed on this machine: JPL Keplerian elements (Standish, Table 1; Pluto from JPL's archived table), Schlyter (Jupiter, Saturn), Meeus (Moon, nutation, aberration) | `market astro` | none | Checked against JPL Horizons and USNO recordings in `tests/fixtures/astro/`; the `--backtest` reads `^JKSE` daily history from Yahoo's chart endpoint |\n| idx.co.id trading-holiday calendar (published as an image per year) | trading holidays | none | Transcribed into `data/holidays.csv` (2026, 2027) and checked against IDX's monthly trading-day counts; `IDX_HOLIDAYS` overrides it |\n| Kontan RSS (`investasi`, `keuangan`, `industri`, `insight`) and CNBC Indonesia RSS (`market`, `research`, `news`) | news, sentiment | none | Bisnis.com refuses non-browser clients; listed as an opt-in source |\n| MSN Money `Finance/Quotes`, `Finance/Equities` | quotes, profile, fundamentals, analyst targets, institutional and fund holders (`ownershipSummary`) | MSN's own public web key (shipped; override with `IDX_MSN_API_KEY`) | Tickers resolve to MSN ids through its autosuggest service, filtered to exchange `XIDX` |\n| MSN Money `pages/finance/insights` (api.msn.com) | stocks insights | same public web key | Rule-based statements, refreshed about once a day (cached for the fundamentals TTL, 6 h by default); peers resolve to tickers through `Finance/Quotes`. MSN's crowd sentiment is not used: it has no data for IDX tickers |\n| MSN Money `Finance/Screener` (POST, whole Indonesian market) | screen return windows (`ret1w`..`ret1y`) | same public web key | One request, ~887 stocks, cached 15 minutes; values as MSN reports them. MSN's own preset lists are not used (stale or empty for IDX) |\n| Stockbit `exodus.stockbit.com` (`--provider stockbit`, opt-in) | realtime quote, history, live order book + foreign flow; corporate actions and calendar; insider and 5 % holder movements; the full broker summary (`broker`, by default once logged in) | **your own Stockbit session**, taken in by `idx stockbit login` (a browser or a local page; never the password) and stored locally | The one source that needs a credential and the one not in the `auto` provider chain; `broker --source auto` reads it only while a stored session works. Volume is realtime; the order book carries the full depth ladder. Foreign flow is left empty, with a warning, while Stockbit marks it as not current (seen during trading). Candles alone with `--history-provider stockbit`. The broker summary (`/marketdetectors/`, gross figures) lists every broker that bought and sold, exact; ordered by lots, its first ten were IPOT's top 10 on the day and the range recorded, and a second read of foreign investors gives the foreign net. Not read for domestic investors or the cash and negotiated boards yet. See [docs/stockbit.md](docs/stockbit.md) |\n\nPrices from the free sources are delayed as published; Stockbit is realtime for\nyour own account. This tool is for research and tooling; it is not investment\nadvice and not affiliated with IDX, KSEI, Yahoo, Microsoft or Stockbit.\n\n## Web terminal\n\n**https://ihsg.midoriresearch.com** is the market page: IHSG and its session,\nbreadth, the heavyweights and the most active stocks, refreshed every minute, with\na stock search. It speaks Bahasa Indonesia at `/` and English at `/en/`, with the\nnumbers in each language's style, and follows the device's light or dark setting.\n\n**https://ihsg.midoriresearch.com/terminal** (English: `/en/terminal`) runs the same commands in a\nbrowser, no install: `brief BBCA`, `quote BBCA,BBRI`, `intraday BBCA`, `returns BBCA BBRI`,\n`screen pe<10,div>5,mcap>5T --sort div`, `screen --preset top-performers`,\n`insights BBCA`, `news ASII`, `ownership ticker BBCA`,\n`ownership foreign`, `bandar BBCA --daily`, `bandar screen --preset\nbig-accumulation`, `bandar broker YP`, `actions BBCA`, `foreign BBCA` (foreign\nflow from IPOT in rupiah, up to 20 sessions), `holidays --upcoming`, `astro`,\n`list --search bank`, `members`, `risk TLKM` (also `growth` and `valuation`), `brokers`\n(`market brokers` from IPOT's ranking of its latest session, with each member's class and\nnet), `market foreign` (`market foreign --source ipot`: the session's 100 most traded\nstocks by their foreign net in rupiah, from the nightly bandar scan, with IPOT's market\ntotals), and `tools` (every MCP tool as a button). The idx.co.id\ncommands (market-wide foreign flow in shares, the calendar, indices, and `market brokers\n--source idx`) are CLI and MCP only, because idx.co.id refuses requests from Cloudflare Workers; so,\nfor now, is `ownership institutions`, until MSN is checked from the Worker. Typed into the terminal,\nany of these, and the CLI-only commands (`watch`, `mcp`, `ownership import`, ...), answer\nwith a notice instead of an error: where the command runs and why, its CLI line with your\nown arguments, and the nearest command the page has. A line pasted from the CLI, such as\n`idx stocks quote BBCA` or `idx -o json market overview`, runs as the page's own command.\n`quote` with one symbol also draws a price chart (1D, 5D,\n1M, 6M, YTD, 1Y, 5Y) with volume, the previous close and a crosshair readout.\nThe web `bandar screen` and `bandar broker` read a scan the Worker runs after\neach close, once IPOT publishes the session's broker lists, over the session's\n100 most traded stocks; their warnings say when an older session is shown. The\nCLI scans live.\nA Cloudflare Worker refreshes a D1 snapshot of the whole\nIDX board every minute in trading hours and serves `/api/*` with the exact JSON\nenvelopes the CLI prints; everything else is fetched on demand and cached. The\nWorker lives in [`web/`](web/) and answers on the custom domain only\n(`workers_dev = false`, Host check in the Worker).\n\nThe same site serves this README and `docs/` as\n**https://ihsg.midoriresearch.com/docs/**, every page also as Markdown at its\n`.md` address (`/docs/mcp.md`), with\n[`/llms.txt`](https://ihsg.midoriresearch.com/llms.txt) and `/llms-full.txt`\nfor language models. They are built from the repository at deploy time.\n\n## Ownership data pipeline\n\nNobody imports the KSEI register by hand, and nothing outside the repository\ncan write it:\n\n1. [`.github/workflows/ownership.yml`](.github/workflows/ownership.yml) runs at\n   08:17, 14:17 and 20:17 WIB. It fetches the IDX \"Data Kepemilikan Saham\" page\n   through a browser-impersonating client, downloads any new or re-published\n   \"Pemegang Saham di Atas 1%\" workbook, imports every month with `idx` to\n   prove it parses, and publishes the files plus `index.json` and parsed JSON\n   parts to the [`ownership-data`](https://github.com/fzn0x/idx/releases/tag/ownership-data)\n   prerelease. idx.co.id blocks cloud networks, GitHub's own runners included,\n   so the job runs on a self-hosted runner (an always-on machine with an\n   ordinary connection; set the repository variable `OWNERSHIP_RUNNER` to\n   `self-hosted`). On GitHub's runners it rebuilds the parts and the snapshot\n   from the files already mirrored, and turns red once the newest register is\n   45 days old. From the same database it builds a prebuilt SQLite snapshot\n   (`snapshot-v{V}.json` plus a gzip of the file) and publishes it whenever its\n   content changes, the manifest last.\n2. The web terminal's Worker reads that release on its minute cron: it checks\n   `index.json` every 30 minutes, verifies each part's SHA-256 and loads one\n   part per minute into D1. A month appears only once all its rows are in and\n   the count matches. The newest six months are kept. The Worker has no write\n   endpoint at all.\n3. `idx ownership sync` on your machine installs that snapshot (a few MB,\n   SHA-256 checked, swapped in as one SQLite transaction) and imports any\n   newer month from the raw files; a database holding months the snapshot\n   lacks is kept unless `--force`.\n4. `idx ownership import --latest` tries idx.co.id and, when Cloudflare\n   refuses it, downloads the same file from the mirror and checks its SHA-256.\n   `IDX_OWNERSHIP_SOURCE=mirror` skips idx.co.id.\n\n<p align=\"center\">\n  <a href=\"https://ihsg.midoriresearch.com/terminal\"><img src=\"docs/screenshots/terminal.png\" alt=\"The idx web terminal showing technical BBRI\" width=\"100%\"></a>\n</p>\n\n## Development\n\n```sh\ncargo build\ncargo test                                 # recorded fixtures, no network\ncargo clippy --all-targets -- -D warnings\n./scripts/smoke.sh                         # live providers\n```\n\nArchitecture, conventions and \"how to add a provider\" live in\n[AGENTS.md](AGENTS.md). Contributions: [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE)\n"}