From fe627496ddeadc8a9bc736027e2991d61c416b82 Mon Sep 17 00:00:00 2001 From: hermes Date: Wed, 14 Oct 2026 08:33:25 +0000 Subject: [PATCH] test: add golden output parity gate and live A/B compare Phase 0 of docs/DESIGN.md, before any refactor code: - tests/golden.rs runs every case in tests/golden/cases.toml (126 outputs across help, completions, config, cache, all stocks commands on MSN and Yahoo mocks, error paths, and ownership queries on a seeded fixture DB) and compares exit code, stdout, and stderr byte for byte. Regenerate only with UPDATE_GOLDEN=1 for a reviewed output change. - scripts/live-smoke.sh --compare runs each live case with both binaries and fails on differing exit codes, added/missing/retyped JSON keys, or changed values for slow-moving data. - Exclude golden files from the whitespace/EOF pre-commit fixers, which would otherwise rewrite pinned help output. Both gates were fault-injected: a one-word table change fails exactly the three history goldens, and a binary dropping profile.long_name fails only the profile live comparison. Co-Authored-By: Claude Opus 5.5 --- .pre-commit-config.yaml | 3 + AGENTS.md | 4 +- docs/DESIGN.md | 15 +- docs/SMOKE.md | 15 +- scripts/live-smoke.sh | 97 +- tests/golden.rs | 208 ++ tests/golden/cache-clear.json.golden | 7 + tests/golden/cache-clear.table.golden | 4 + tests/golden/cache-info-empty.json.golden | 10 + tests/golden/cache-info-empty.table.golden | 8 + tests/golden/cases.toml | 327 ++ tests/golden/completions-bash.golden | 2851 +++++++++++++++++ tests/golden/config-get.golden | 4 + tests/golden/config-init.golden | 4 + tests/golden/config-path.golden | 4 + tests/golden/config-set.golden | 4 + .../error-compare-total-failure.json.golden | 8 + .../error-compare-total-failure.table.golden | 4 + .../error-fetch-bing-unsupported.json.golden | 8 + .../error-fetch-bing-unsupported.table.golden | 4 + ...ror-industry-only-fundamentals.json.golden | 8 + ...or-industry-only-fundamentals.table.golden | 4 + .../golden/error-invalid-provider.json.golden | 8 + .../error-invalid-provider.table.golden | 4 + .../error-offline-cache-miss.json.golden | 8 + .../error-offline-cache-miss.table.golden | 4 + .../error-offline-no-cache-flags.json.golden | 8 + .../error-offline-no-cache-flags.table.golden | 4 + .../golden/error-provider-failure.json.golden | 8 + .../error-provider-failure.table.golden | 4 + tests/golden/error-screen-region.json.golden | 8 + tests/golden/error-screen-region.table.golden | 4 + .../error-usage-missing-symbol.json.golden | 9 + .../error-usage-missing-symbol.table.golden | 9 + ...rror-yahoo-profile-unsupported.json.golden | 8 + ...ror-yahoo-profile-unsupported.table.golden | 4 + tests/golden/help-cache.golden | 20 + tests/golden/help-config.golden | 22 + tests/golden/help-ownership.golden | 58 + tests/golden/help-root.golden | 40 + tests/golden/help-stocks.golden | 33 + tests/golden/ownership-changes.json.golden | 109 + tests/golden/ownership-changes.table.golden | 32 + .../ownership-concentration.json.golden | 45 + .../ownership-concentration.table.golden | 14 + .../ownership-cross-holders.json.golden | 175 + .../ownership-cross-holders.table.golden | 40 + tests/golden/ownership-entity.json.golden | 26 + tests/golden/ownership-entity.table.golden | 10 + tests/golden/ownership-flow.json.golden | 4 + tests/golden/ownership-flow.table.golden | 4 + tests/golden/ownership-graph-dot.json.golden | 58 + tests/golden/ownership-graph-dot.table.golden | 20 + tests/golden/ownership-graph.json.golden | 58 + tests/golden/ownership-graph.table.golden | 10 + .../ownership-import-archive-txt.json.golden | 11 + .../ownership-import-archive-txt.table.golden | 4 + .../golden/ownership-import-xlsx.json.golden | 11 + .../golden/ownership-import-xlsx.table.golden | 4 + .../ownership-releases-empty.json.golden | 4 + .../ownership-releases-empty.table.golden | 4 + tests/golden/ownership-releases.json.golden | 29 + tests/golden/ownership-releases.table.golden | 12 + .../golden/ownership-resolve-map.json.golden | 8 + .../golden/ownership-resolve-map.table.golden | 4 + .../ownership-resolve-merge.json.golden | 11 + .../ownership-resolve-merge.table.golden | 4 + .../ownership-resolve-unresolved.json.golden | 4 + .../ownership-resolve-unresolved.table.golden | 4 + tests/golden/ownership-search.json.golden | 11 + tests/golden/ownership-search.table.golden | 8 + .../golden/ownership-ticker-empty.json.golden | 8 + .../ownership-ticker-empty.table.golden | 4 + .../golden/ownership-ticker-ksei.json.golden | 67 + .../golden/ownership-ticker-ksei.table.golden | 16 + tests/golden/ownership-ticker.json.golden | 67 + tests/golden/ownership-ticker.table.golden | 16 + tests/golden/stocks-compare.json.golden | 81 + tests/golden/stocks-compare.table.golden | 23 + ...tocks-earnings-forecast-annual.json.golden | 22 + ...ocks-earnings-forecast-annual.table.golden | 10 + ...cks-earnings-history-quarterly.json.golden | 22 + ...ks-earnings-history-quarterly.table.golden | 9 + tests/golden/stocks-earnings.json.golden | 56 + tests/golden/stocks-earnings.table.golden | 20 + .../stocks-financials-income.json.golden | 21 + .../stocks-financials-income.table.golden | 12 + tests/golden/stocks-financials.json.golden | 36 + tests/golden/stocks-financials.table.golden | 28 + tests/golden/stocks-fundamental.json.golden | 41 + tests/golden/stocks-fundamental.table.golden | 49 + tests/golden/stocks-growth.json.golden | 12 + tests/golden/stocks-growth.table.golden | 13 + tests/golden/stocks-history-msn.json.golden | 29 + tests/golden/stocks-history-msn.table.golden | 13 + tests/golden/stocks-history.json.golden | 29 + tests/golden/stocks-history.table.golden | 14 + tests/golden/stocks-insights.json.golden | 16 + tests/golden/stocks-insights.table.golden | 10 + tests/golden/stocks-news.json.golden | 15 + tests/golden/stocks-news.table.golden | 8 + tests/golden/stocks-profile.json.golden | 27 + tests/golden/stocks-profile.table.golden | 28 + tests/golden/stocks-quote-multi.json.golden | 35 + tests/golden/stocks-quote-multi.table.golden | 10 + .../stocks-quote-offline-warm.json.golden | 20 + .../stocks-quote-offline-warm.table.golden | 8 + tests/golden/stocks-quote.json.golden | 20 + tests/golden/stocks-quote.table.golden | 8 + tests/golden/stocks-risk.json.golden | 12 + tests/golden/stocks-risk.table.golden | 15 + .../stocks-screen-high-volume.json.golden | 35 + .../stocks-screen-high-volume.table.golden | 10 + .../stocks-screen-large-cap.json.golden | 35 + .../stocks-screen-large-cap.table.golden | 10 + tests/golden/stocks-screen-worst.json.golden | 35 + tests/golden/stocks-screen-worst.table.golden | 10 + tests/golden/stocks-screen.json.golden | 35 + tests/golden/stocks-screen.table.golden | 10 + tests/golden/stocks-sentiment.json.golden | 20 + tests/golden/stocks-sentiment.table.golden | 10 + tests/golden/stocks-technical.json.golden | 28 + tests/golden/stocks-technical.table.golden | 26 + .../stocks-valuation-nonfinite.json.golden | 19 + .../stocks-valuation-nonfinite.table.golden | 21 + tests/golden/stocks-valuation.json.golden | 19 + tests/golden/stocks-valuation.table.golden | 21 + tests/golden/yahoo-fundamental.json.golden | 41 + tests/golden/yahoo-fundamental.table.golden | 49 + tests/golden/yahoo-history.json.golden | 29 + tests/golden/yahoo-history.table.golden | 13 + tests/golden/yahoo-quote.json.golden | 20 + tests/golden/yahoo-quote.table.golden | 8 + 133 files changed, 6033 insertions(+), 23 deletions(-) create mode 100644 tests/golden.rs create mode 100644 tests/golden/cache-clear.json.golden create mode 100644 tests/golden/cache-clear.table.golden create mode 100644 tests/golden/cache-info-empty.json.golden create mode 100644 tests/golden/cache-info-empty.table.golden create mode 100644 tests/golden/cases.toml create mode 100644 tests/golden/completions-bash.golden create mode 100644 tests/golden/config-get.golden create mode 100644 tests/golden/config-init.golden create mode 100644 tests/golden/config-path.golden create mode 100644 tests/golden/config-set.golden create mode 100644 tests/golden/error-compare-total-failure.json.golden create mode 100644 tests/golden/error-compare-total-failure.table.golden create mode 100644 tests/golden/error-fetch-bing-unsupported.json.golden create mode 100644 tests/golden/error-fetch-bing-unsupported.table.golden create mode 100644 tests/golden/error-industry-only-fundamentals.json.golden create mode 100644 tests/golden/error-industry-only-fundamentals.table.golden create mode 100644 tests/golden/error-invalid-provider.json.golden create mode 100644 tests/golden/error-invalid-provider.table.golden create mode 100644 tests/golden/error-offline-cache-miss.json.golden create mode 100644 tests/golden/error-offline-cache-miss.table.golden create mode 100644 tests/golden/error-offline-no-cache-flags.json.golden create mode 100644 tests/golden/error-offline-no-cache-flags.table.golden create mode 100644 tests/golden/error-provider-failure.json.golden create mode 100644 tests/golden/error-provider-failure.table.golden create mode 100644 tests/golden/error-screen-region.json.golden create mode 100644 tests/golden/error-screen-region.table.golden create mode 100644 tests/golden/error-usage-missing-symbol.json.golden create mode 100644 tests/golden/error-usage-missing-symbol.table.golden create mode 100644 tests/golden/error-yahoo-profile-unsupported.json.golden create mode 100644 tests/golden/error-yahoo-profile-unsupported.table.golden create mode 100644 tests/golden/help-cache.golden create mode 100644 tests/golden/help-config.golden create mode 100644 tests/golden/help-ownership.golden create mode 100644 tests/golden/help-root.golden create mode 100644 tests/golden/help-stocks.golden create mode 100644 tests/golden/ownership-changes.json.golden create mode 100644 tests/golden/ownership-changes.table.golden create mode 100644 tests/golden/ownership-concentration.json.golden create mode 100644 tests/golden/ownership-concentration.table.golden create mode 100644 tests/golden/ownership-cross-holders.json.golden create mode 100644 tests/golden/ownership-cross-holders.table.golden create mode 100644 tests/golden/ownership-entity.json.golden create mode 100644 tests/golden/ownership-entity.table.golden create mode 100644 tests/golden/ownership-flow.json.golden create mode 100644 tests/golden/ownership-flow.table.golden create mode 100644 tests/golden/ownership-graph-dot.json.golden create mode 100644 tests/golden/ownership-graph-dot.table.golden create mode 100644 tests/golden/ownership-graph.json.golden create mode 100644 tests/golden/ownership-graph.table.golden create mode 100644 tests/golden/ownership-import-archive-txt.json.golden create mode 100644 tests/golden/ownership-import-archive-txt.table.golden create mode 100644 tests/golden/ownership-import-xlsx.json.golden create mode 100644 tests/golden/ownership-import-xlsx.table.golden create mode 100644 tests/golden/ownership-releases-empty.json.golden create mode 100644 tests/golden/ownership-releases-empty.table.golden create mode 100644 tests/golden/ownership-releases.json.golden create mode 100644 tests/golden/ownership-releases.table.golden create mode 100644 tests/golden/ownership-resolve-map.json.golden create mode 100644 tests/golden/ownership-resolve-map.table.golden create mode 100644 tests/golden/ownership-resolve-merge.json.golden create mode 100644 tests/golden/ownership-resolve-merge.table.golden create mode 100644 tests/golden/ownership-resolve-unresolved.json.golden create mode 100644 tests/golden/ownership-resolve-unresolved.table.golden create mode 100644 tests/golden/ownership-search.json.golden create mode 100644 tests/golden/ownership-search.table.golden create mode 100644 tests/golden/ownership-ticker-empty.json.golden create mode 100644 tests/golden/ownership-ticker-empty.table.golden create mode 100644 tests/golden/ownership-ticker-ksei.json.golden create mode 100644 tests/golden/ownership-ticker-ksei.table.golden create mode 100644 tests/golden/ownership-ticker.json.golden create mode 100644 tests/golden/ownership-ticker.table.golden create mode 100644 tests/golden/stocks-compare.json.golden create mode 100644 tests/golden/stocks-compare.table.golden create mode 100644 tests/golden/stocks-earnings-forecast-annual.json.golden create mode 100644 tests/golden/stocks-earnings-forecast-annual.table.golden create mode 100644 tests/golden/stocks-earnings-history-quarterly.json.golden create mode 100644 tests/golden/stocks-earnings-history-quarterly.table.golden create mode 100644 tests/golden/stocks-earnings.json.golden create mode 100644 tests/golden/stocks-earnings.table.golden create mode 100644 tests/golden/stocks-financials-income.json.golden create mode 100644 tests/golden/stocks-financials-income.table.golden create mode 100644 tests/golden/stocks-financials.json.golden create mode 100644 tests/golden/stocks-financials.table.golden create mode 100644 tests/golden/stocks-fundamental.json.golden create mode 100644 tests/golden/stocks-fundamental.table.golden create mode 100644 tests/golden/stocks-growth.json.golden create mode 100644 tests/golden/stocks-growth.table.golden create mode 100644 tests/golden/stocks-history-msn.json.golden create mode 100644 tests/golden/stocks-history-msn.table.golden create mode 100644 tests/golden/stocks-history.json.golden create mode 100644 tests/golden/stocks-history.table.golden create mode 100644 tests/golden/stocks-insights.json.golden create mode 100644 tests/golden/stocks-insights.table.golden create mode 100644 tests/golden/stocks-news.json.golden create mode 100644 tests/golden/stocks-news.table.golden create mode 100644 tests/golden/stocks-profile.json.golden create mode 100644 tests/golden/stocks-profile.table.golden create mode 100644 tests/golden/stocks-quote-multi.json.golden create mode 100644 tests/golden/stocks-quote-multi.table.golden create mode 100644 tests/golden/stocks-quote-offline-warm.json.golden create mode 100644 tests/golden/stocks-quote-offline-warm.table.golden create mode 100644 tests/golden/stocks-quote.json.golden create mode 100644 tests/golden/stocks-quote.table.golden create mode 100644 tests/golden/stocks-risk.json.golden create mode 100644 tests/golden/stocks-risk.table.golden create mode 100644 tests/golden/stocks-screen-high-volume.json.golden create mode 100644 tests/golden/stocks-screen-high-volume.table.golden create mode 100644 tests/golden/stocks-screen-large-cap.json.golden create mode 100644 tests/golden/stocks-screen-large-cap.table.golden create mode 100644 tests/golden/stocks-screen-worst.json.golden create mode 100644 tests/golden/stocks-screen-worst.table.golden create mode 100644 tests/golden/stocks-screen.json.golden create mode 100644 tests/golden/stocks-screen.table.golden create mode 100644 tests/golden/stocks-sentiment.json.golden create mode 100644 tests/golden/stocks-sentiment.table.golden create mode 100644 tests/golden/stocks-technical.json.golden create mode 100644 tests/golden/stocks-technical.table.golden create mode 100644 tests/golden/stocks-valuation-nonfinite.json.golden create mode 100644 tests/golden/stocks-valuation-nonfinite.table.golden create mode 100644 tests/golden/stocks-valuation.json.golden create mode 100644 tests/golden/stocks-valuation.table.golden create mode 100644 tests/golden/yahoo-fundamental.json.golden create mode 100644 tests/golden/yahoo-fundamental.table.golden create mode 100644 tests/golden/yahoo-history.json.golden create mode 100644 tests/golden/yahoo-history.table.golden create mode 100644 tests/golden/yahoo-quote.json.golden create mode 100644 tests/golden/yahoo-quote.table.golden diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9b1d3c..dfb9713 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,11 @@ repos: - repo: builtin hooks: + # Golden files pin exact CLI output, including trailing spaces. - id: trailing-whitespace + exclude: ^tests/golden/.*\.golden$ - id: end-of-file-fixer + exclude: ^tests/golden/.*\.golden$ - id: check-merge-conflict - repo: local diff --git a/AGENTS.md b/AGENTS.md index b137161..70b7779 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ src/ ├── ownership/ xlsx + pdf parsers, archive (ZIP/TXT), remote discovery, │ snapshot sync, db (SQLite/FTS5), entities, search, graph └── output/ table.rs, json.rs -tests/cli.rs CLI integration tests; fixtures in tests/fixtures/ +tests/ cli.rs (behavior), golden.rs + golden/ (output parity), fixtures/ scripts/ smoke, installer, and ownership snapshot publisher ``` @@ -42,5 +42,5 @@ Docs: ```bash nix flake check -L # canonical gate (fmt, clippy, tests, package) nix develop --command cargo test # focused loop -scripts/live-smoke.sh # opt-in live provider drift check +scripts/live-smoke.sh --compare # live drift check, A/B against an older build ``` diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 1125aae..b126234 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -162,7 +162,8 @@ is another 20-30% smaller than pretty JSON. - stdout carries only data; diagnostics and warnings go to stderr. Already true. - Errors in `json`/`jsonl` mode are one JSON object on stderr with stdout empty. Already true. -- No ANSI color when stdout is not a terminal. +- No ANSI color when stdout is not a terminal. Today some table headers emit + bold codes even when piped. - Commands that take symbols accept `-` to read them from stdin, one per line or whitespace-separated: `idx stocks screen --filter low-pe | jq -r '.[].symbol' | idx stocks compare -` @@ -264,14 +265,16 @@ Before any refactor code lands, capture the current CLI as golden files: fixture-built ownership DB - the documented error cases (invalid input, unsupported provider, offline without cache, empty ownership DB) -- stored as `tests/golden/.{stdout,stderr,exit}` and compared byte for - byte by one data-driven test in `tests/golden.rs` +- cases listed in `tests/golden/cases.toml`; each result stored as + `tests/golden/..golden` (exit code, stdout, stderr) and + compared byte for byte by one data-driven test in `tests/golden.rs` Regenerating goldens (`UPDATE_GOLDEN=1`) is only allowed for an intentional, reviewed output change, and the diff must be in the same commit with the reason -in the message. Phases 1-3 must not change any golden file. Phase 0 changes only -what it documents: default format resolution, JSON whitespace when piped, and -exit codes; JSON content and table bytes stay identical. +in the message. Phases 2-4 must not change any golden file. Phase 1 changes only +what it documents: default format resolution, JSON whitespace when piped, ANSI +color when piped (table output emits bold headers into pipes today), and exit +codes; JSON content and table text stay identical. `tests/cli.rs` stays the behavioral suite and must keep passing unchanged. diff --git a/docs/SMOKE.md b/docs/SMOKE.md index 562f8ee..b8f97d3 100644 --- a/docs/SMOKE.md +++ b/docs/SMOKE.md @@ -8,7 +8,7 @@ offline with pinned tools: | Check | Covers | | --- | --- | | `fmt`, `clippy` | formatting and lints (`--all-targets`) | -| `test` | unit/module tests and `tests/cli.rs` CLI integration tests | +| `test` | unit/module tests, `tests/cli.rs` CLI integration tests, and `tests/golden.rs` output parity | | `default`, `package` | release build and `cargo package` verification | | `packaged-cli` | the wrapped Nix binary starts, emits JSON, and generates completions | | `ownership-publisher`, `publisher-test` | packaged publisher entry point and its regression scenarios | @@ -19,6 +19,12 @@ fixture-backed mock providers (`IDX_USE_MOCK_PROVIDER=1`) and covers output contracts, cache/offline transitions, provider routing, error codes, ownership imports and queries, and snapshot sync. +`tests/golden.rs` pins the exact output (exit code, stdout, stderr) of every +command listed in `tests/golden/cases.toml`, in table and JSON. A mismatch means +user-visible output changed. Regenerate with +`UPDATE_GOLDEN=1 cargo test --test golden` only when the change is intended, +and commit the golden diff with the reason. + For a focused loop, run one check or filter Cargo tests: ```bash @@ -39,6 +45,7 @@ validates each JSON output shape. It is opt-in and never part of the gate. nix develop --command scripts/live-smoke.sh # stocks + ownership nix develop --command scripts/live-smoke.sh --group stocks --symbol BBRI nix develop --command scripts/live-smoke.sh --bin ./result/bin/idx +nix develop --command scripts/live-smoke.sh --compare ./old/bin/idx # A/B against an older build scripts/live-smoke.sh --dry-run ``` @@ -48,6 +55,12 @@ scripts/live-smoke.sh --dry-run DB and lists `releases`. IDX sits behind Cloudflare and returns `403` to many datacenter IPs, including GitHub-hosted runners; run it from a network IDX accepts. +`--compare ` runs every case with both binaries back to back, each +with its own config and cache, and fails on a different exit code, any added, +missing, or retyped JSON key, or changed values for `profile`, `financials`, +`earnings`, and ownership results. Build the old side with, for example, +`nix build "git+file://$PWD?ref=main" -o old`. + Each run isolates config, cache, and data under `tmp/live-smoke//` and keeps per-case `.stdout`, `.stderr`, and `.validation` logs there. diff --git a/scripts/live-smoke.sh b/scripts/live-smoke.sh index 1bd4d3f..2aefa78 100755 --- a/scripts/live-smoke.sh +++ b/scripts/live-smoke.sh @@ -1,8 +1,12 @@ #!/usr/bin/env bash # Live provider drift check: runs shipped commands against real MSN, Yahoo, # and IDX endpoints and validates the JSON output shape. Deterministic CLI -# behavior is covered by tests/cli.rs; this only answers "do the upstreams -# still look the way our parsers expect?" +# behavior is covered by tests/cli.rs and tests/golden.rs; this answers "do the +# upstreams still look the way our parsers expect?" +# +# With --compare , every case also runs the old binary back to back +# and fails on any difference in exit code or JSON structure, and on any value +# difference for slow-moving data (see "Live A/B check" in docs/DESIGN.md). set -u set -o pipefail @@ -10,6 +14,7 @@ set -o pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SYMBOL="BBCA" BIN_PATH="" +COMPARE_BIN="" DRY_RUN=0 declare -a GROUPS_SELECTED=() declare -a FAILURES=() @@ -24,6 +29,7 @@ Options: --group Restrict to a group. Repeatable. Default: both --symbol Primary ticker. Default: BBCA --bin Use an existing idx binary (skips cargo build) + --compare Also run an older idx binary and diff the results --dry-run Print cases without running them --help Show this help EOF @@ -34,6 +40,7 @@ while [[ $# -gt 0 ]]; do --group) GROUPS_SELECTED+=("${2:-}"); shift 2 ;; --symbol) SYMBOL="${2:-}"; shift 2 ;; --bin) BIN_PATH="${2:-}"; shift 2 ;; + --compare) COMPARE_BIN="${2:-}"; shift 2 ;; --dry-run) DRY_RUN=1; shift ;; --help|-h) usage; exit 0 ;; *) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;; @@ -58,14 +65,36 @@ selected() { WORKDIR="$ROOT_DIR/tmp/live-smoke/$(date +%Y%m%d-%H%M%S)" LOG_DIR="$WORKDIR/logs" -BASE_ENV=( - "XDG_CONFIG_HOME=$WORKDIR/config" - "XDG_CACHE_HOME=$WORKDIR/cache" - "XDG_DATA_HOME=$WORKDIR/data" - "IDX_PROVIDER=msn" - "IDX_HISTORY_PROVIDER=auto" - "IDX_NO_COLOR=1" -) +COMMON_ENV=("IDX_PROVIDER=msn" "IDX_HISTORY_PROVIDER=auto" "IDX_NO_COLOR=1") + +# Each binary gets its own config, cache, and data so both really fetch. +side_env() { + local home="$WORKDIR/$1" + printf '%s\n' "XDG_CONFIG_HOME=$home/config" "XDG_CACHE_HOME=$home/cache" \ + "XDG_DATA_HOME=$home/data" "${COMMON_ENV[@]}" +} + +# Values of these commands should not move between two back-to-back runs. +stable_values() { + case "$1" in + "stocks profile "*|"stocks financials "*|"stocks earnings "*|"ownership ticker "*|"ownership releases") return 0 ;; + *) return 1 ;; + esac +} + +# Key paths (array indices collapsed) that disappeared or appeared, and keys +# whose non-null type changed. Empty lists mean the structure matches. +# shellcheck disable=SC2016 +SHAPE_DIFF=' +def norm: map(if type == "number" then "*" else . end) | join("."); +def keyset: [paths | norm] | unique; +def types: [paths(scalars) as $p | select(getpath($p) != null) + | {k: ($p | norm), t: (getpath($p) | type)}] + | group_by(.k) | map({key: .[0].k, value: (map(.t) | unique)}) | from_entries; +($old[0] | keyset) as $ko | ($new[0] | keyset) as $kn +| ($old[0] | types) as $to | ($new[0] | types) as $tn +| {missing: ($ko - $kn), added: ($kn - $ko), + type_changed: [$to | keys[] as $k | select($tn[$k] != null and $tn[$k] != $to[$k]) | $k]}' # jq filter describing the JSON shape each command must keep. contract() { @@ -103,8 +132,16 @@ contract() { esac } +run_bin() { + local side="$1" bin="$2" out="$3" + shift 3 + local -a envs + mapfile -t envs < <(side_env "$side") + env "${envs[@]}" "$bin" -o json "$@" >"$out.stdout" 2>"$out.stderr" +} + run_case() { - local name="$1" cmd="$2" filter log + local name="$1" cmd="$2" filter log status old_status diff local -a args TOTAL=$((TOTAL + 1)) printf '[%02d] %s\n' "$TOTAL" "$name" @@ -115,8 +152,19 @@ run_case() { log="$LOG_DIR/$(printf '%02d' "$TOTAL")_$name" read -r -a args <<< "$cmd" - if ! env "${BASE_ENV[@]}" "$BIN_PATH" -o json "${args[@]}" >"$log.stdout" 2>"$log.stderr"; then - FAILURES+=("$name: non-zero exit (see $log.stderr)") + run_bin new "$BIN_PATH" "$log" "${args[@]}" + status=$? + if [[ -n "$COMPARE_BIN" ]]; then + run_bin old "$COMPARE_BIN" "$log.old" "${args[@]}" + old_status=$? + if (( status != old_status )); then + FAILURES+=("$name: exit $status, old binary exited $old_status (see $log.stderr, $log.old.stderr)") + echo " FAIL exit differs from old binary" + return + fi + fi + if (( status != 0 )); then + FAILURES+=("$name: exit $status (see $log.stderr)") echo " FAIL exit" return fi @@ -126,6 +174,22 @@ run_case() { echo " FAIL shape" return fi + if [[ -n "$COMPARE_BIN" ]]; then + diff="$(jq -n --slurpfile old "$log.old.stdout" --slurpfile new "$log.stdout" "$SHAPE_DIFF")" + if [[ "$(jq -c '[.[] | length] | add' <<< "$diff")" != 0 ]]; then + printf '%s\n' "$diff" >"$log.diff" + FAILURES+=("$name: JSON structure differs from old binary (see $log.diff)") + echo " FAIL structure differs" + return + fi + if stable_values "$cmd" && ! cmp -s \ + <(jq -S 'del(.. | .imported_at?)' "$log.old.stdout") \ + <(jq -S 'del(.. | .imported_at?)' "$log.stdout"); then + FAILURES+=("$name: values differ from old binary (diff $log.old.stdout $log.stdout)") + echo " FAIL values differ" + return + fi + fi PASSED=$((PASSED + 1)) echo " PASS" } @@ -137,7 +201,10 @@ if (( ! DRY_RUN )); then BIN_PATH="$ROOT_DIR/target/debug/idx" (cd "$ROOT_DIR" && cargo build --quiet --bin idx) || { echo "cargo build failed" >&2; exit 1; } fi - env "${BASE_ENV[@]}" "$BIN_PATH" config set ownership.db_path "$WORKDIR/ownership.db" >/dev/null + if [[ -n "$COMPARE_BIN" && ! -x "$COMPARE_BIN" ]]; then + echo "--compare binary not found or not executable: $COMPARE_BIN" >&2 + exit 2 + fi fi if selected stocks; then @@ -158,10 +225,12 @@ if selected ownership; then url="$(jq -r '.[0].pdf_url' "$LOG_DIR/$(printf '%02d' "$TOTAL")_discover.stdout")" run_case import "ownership import --url $url" run_case releases "ownership releases" + run_case ticker "ownership ticker $SYMBOL" fi fi (( DRY_RUN )) && { echo "dry-run: $TOTAL case(s)"; exit 0; } +[[ -n "$COMPARE_BIN" ]] && printf '\ncompared against: %s\n' "$COMPARE_BIN" printf '\nsummary: %d passed, %d failed\nlogs: %s\n' "$PASSED" "$((TOTAL - PASSED))" "$LOG_DIR" if (( ${#FAILURES[@]} > 0 )); then printf ' - %s\n' "${FAILURES[@]}" diff --git a/tests/golden.rs b/tests/golden.rs new file mode 100644 index 0000000..884abf2 --- /dev/null +++ b/tests/golden.rs @@ -0,0 +1,208 @@ +//! Golden output tests: byte-for-byte parity of CLI stdout, stderr, and exit +//! code for every shipped command (see "Parity and Verification" in +//! docs/DESIGN.md). +//! +//! Cases live in `tests/golden/cases.toml`; expected output in +//! `tests/golden/.golden`. Regenerate only for an intentional, reviewed +//! output change, and commit the diff with the reason: +//! +//! UPDATE_GOLDEN=1 cargo test --test golden + +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::sync::OnceLock; + +use serde::Deserialize; + +const IDX: &str = env!("CARGO_BIN_EXE_idx"); +const REPO: &str = env!("CARGO_MANIFEST_DIR"); + +#[derive(Deserialize)] +struct CaseFile { + case: Vec, +} + +#[derive(Deserialize)] +struct Case { + name: String, + args: Vec, + /// Output formats to expand the case into (`-o ` is prepended and + /// `.` appended to the name). Empty means run the args as given. + #[serde(default = "default_formats")] + formats: Vec, + #[serde(default)] + env: BTreeMap, + /// Seed the fixture ownership DB before running. + #[serde(default)] + ownership: bool, + /// Commands run first in the same environment; their output is ignored. + #[serde(default)] + before: Vec>, +} + +fn default_formats() -> Vec { + vec!["table".into(), "json".into()] +} + +fn golden_dir() -> PathBuf { + Path::new(REPO).join("tests").join("golden") +} + +fn work_dir() -> PathBuf { + std::env::temp_dir().join(format!("idx-golden-{}", std::process::id())) +} + +/// Runs idx with an isolated home under `root` and only the given IDX_* env. +fn idx(root: &Path, args: &[String], env: &BTreeMap) -> std::process::Output { + let mut cmd = Command::new(IDX); + cmd.current_dir(REPO).args(args); + for (key, _) in std::env::vars_os() { + if key.to_string_lossy().starts_with("IDX_") || key == "NO_COLOR" { + cmd.env_remove(key); + } + } + cmd.env("HOME", root) + .env("XDG_CONFIG_HOME", root.join("config")) + .env("XDG_CACHE_HOME", root.join("cache")) + .env("XDG_DATA_HOME", root.join("data")) + .env("IDX_USE_MOCK_PROVIDER", "1") + .envs(env); + cmd.output().expect("run idx") +} + +/// Fixture ownership DB: the above-1% XLSX (2026-08-31) plus two KSEI archive +/// months (2026-01-30, 2026-02-27), with wall-clock columns pinned. +fn seeded_ownership_db() -> &'static Path { + static DB: OnceLock = OnceLock::new(); + DB.get_or_init(|| { + let root = work_dir().join("seed"); + let _ = fs::remove_dir_all(&root); + fs::create_dir_all(&root).expect("create seed dir"); + // The importer derives the release from the IDX file name. + let xlsx = root.join("peng-2026-08-00017-satu-persen.xlsx"); + fs::copy( + Path::new(REPO).join("tests/fixtures/ksei_above1_20260831_excerpt.xlsx"), + &xlsx, + ) + .expect("copy xlsx fixture"); + let inputs = [ + xlsx, + Path::new(REPO).join("tests/fixtures/ksei_balancepos_20260130_excerpt.txt"), + Path::new(REPO).join("tests/fixtures/ksei_balancepos_20260227_excerpt.txt"), + ]; + for input in inputs { + let args = ["ownership", "import", "--file", input.to_str().unwrap()] + .map(String::from) + .to_vec(); + let out = idx(&root, &args, &BTreeMap::new()); + assert!(out.status.success(), "seed import failed: {out:?}"); + } + let db = root.join("data/idx/ownership.db"); + let conn = rusqlite::Connection::open(&db).expect("open seed db"); + conn.execute_batch( + "UPDATE ownership_releases SET imported_at = 0; + UPDATE entities SET created_at = 0, updated_at = 0; + UPDATE bing_holdings SET fetched_at = 0;", + ) + .expect("pin timestamps"); + db + }) +} + +fn render(root: &Path, out: &std::process::Output) -> String { + let normalize = |bytes: &[u8]| { + String::from_utf8_lossy(bytes) + .replace(root.to_str().unwrap(), "") + .replace(REPO, "") + }; + format!( + "exit: {}\n--- stdout\n{}--- stderr\n{}", + out.status.code().map_or("signal".into(), |c| c.to_string()), + normalize(&out.stdout), + normalize(&out.stderr), + ) +} + +#[test] +fn cli_output_matches_golden_files() { + let raw = fs::read_to_string(golden_dir().join("cases.toml")).expect("read cases.toml"); + let cases: CaseFile = toml::from_str(&raw).expect("parse cases.toml"); + let update = std::env::var_os("UPDATE_GOLDEN").is_some(); + let mut seen = BTreeSet::new(); + let mut failures = Vec::new(); + + for case in &cases.case { + let runs: Vec<(String, Vec)> = if case.formats.is_empty() { + vec![(case.name.clone(), case.args.clone())] + } else { + case.formats + .iter() + .map(|format| { + let mut args = vec!["-o".to_string(), format.clone()]; + args.extend(case.args.iter().cloned()); + (format!("{}.{format}", case.name), args) + }) + .collect() + }; + + for (name, args) in runs { + assert!(seen.insert(name.clone()), "duplicate golden case {name}"); + let root = work_dir().join(&name); + let _ = fs::remove_dir_all(&root); + fs::create_dir_all(&root).expect("create case dir"); + if case.ownership { + let target = root.join("data/idx/ownership.db"); + fs::create_dir_all(target.parent().unwrap()).expect("create data dir"); + fs::copy(seeded_ownership_db(), &target).expect("copy seeded db"); + } + for before in &case.before { + idx(&root, before, &case.env); + } + + let actual = render(&root, &idx(&root, &args, &case.env)); + let path = golden_dir().join(format!("{name}.golden")); + if update { + fs::write(&path, &actual).expect("write golden"); + } else { + match fs::read_to_string(&path) { + Ok(expected) if expected == actual => {} + Ok(expected) => failures.push(format!( + "{name}: output differs\n--- expected\n{expected}\n--- actual\n{actual}" + )), + Err(_) => failures.push(format!("{name}: missing {}", path.display())), + } + } + } + } + + for entry in fs::read_dir(golden_dir()).expect("read golden dir") { + let path = entry.expect("dir entry").path(); + let Some(stem) = path + .file_name() + .unwrap() + .to_str() + .unwrap() + .strip_suffix(".golden") + else { + continue; + }; + if !seen.contains(stem) { + if update { + fs::remove_file(&path).expect("remove orphan golden"); + } else { + failures.push(format!("{stem}: golden file has no case in cases.toml")); + } + } + } + + let _ = fs::remove_dir_all(work_dir()); + assert!( + failures.is_empty(), + "{} golden mismatch(es); if the change is intentional, rerun with UPDATE_GOLDEN=1 \ + and commit the diff with the reason\n\n{}", + failures.len(), + failures.join("\n\n") + ); +} diff --git a/tests/golden/cache-clear.json.golden b/tests/golden/cache-clear.json.golden new file mode 100644 index 0000000..ac869ff --- /dev/null +++ b/tests/golden/cache-clear.json.golden @@ -0,0 +1,7 @@ +exit: 0 +--- stdout +{ + "failed": [], + "removed": 0 +} +--- stderr diff --git a/tests/golden/cache-clear.table.golden b/tests/golden/cache-clear.table.golden new file mode 100644 index 0000000..57c0377 --- /dev/null +++ b/tests/golden/cache-clear.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +cleared 0 files +--- stderr diff --git a/tests/golden/cache-info-empty.json.golden b/tests/golden/cache-info-empty.json.golden new file mode 100644 index 0000000..eb5a389 --- /dev/null +++ b/tests/golden/cache-info-empty.json.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +{ + "files": 0, + "newest": null, + "oldest": null, + "path": "/cache/idx", + "size_bytes": 0 +} +--- stderr diff --git a/tests/golden/cache-info-empty.table.golden b/tests/golden/cache-info-empty.table.golden new file mode 100644 index 0000000..4f864ba --- /dev/null +++ b/tests/golden/cache-info-empty.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +path: /cache/idx +files: 0 +size_bytes: 0 +oldest: - +newest: - +--- stderr diff --git a/tests/golden/cases.toml b/tests/golden/cases.toml new file mode 100644 index 0000000..f81a4f0 --- /dev/null +++ b/tests/golden/cases.toml @@ -0,0 +1,327 @@ +# Golden output cases for tests/golden.rs. +# +# Each case runs once per entry in `formats` (default: table and json) with +# `-o ` prepended; `formats = []` runs the args as given. Every run uses +# the mock provider (IDX_USE_MOCK_PROVIDER=1) and an isolated home directory. +# `ownership = true` seeds the fixture DB; `before` runs setup commands first. +# +# `idx version` is excluded on purpose: its output changes with every release. + +# --- help and shell completions --------------------------------------------- + +[[case]] +name = "help-root" +args = ["--help"] +formats = [] + +[[case]] +name = "help-stocks" +args = ["stocks", "--help"] +formats = [] + +[[case]] +name = "help-ownership" +args = ["ownership", "--help"] +formats = [] + +[[case]] +name = "help-config" +args = ["config", "--help"] +formats = [] + +[[case]] +name = "help-cache" +args = ["cache", "--help"] +formats = [] + +[[case]] +name = "completions-bash" +args = ["completions", "bash"] +formats = [] + +# --- config and cache --------------------------------------------------------- + +[[case]] +name = "config-path" +args = ["config", "path"] +formats = [] + +[[case]] +name = "config-init" +args = ["config", "init"] +formats = [] + +[[case]] +name = "config-get" +args = ["config", "get", "general.provider"] +formats = [] +before = [["config", "init"]] + +[[case]] +name = "config-set" +args = ["config", "set", "general.provider", "yahoo"] +formats = [] + +[[case]] +name = "cache-info-empty" +args = ["cache", "info"] + +[[case]] +name = "cache-clear" +args = ["cache", "clear"] + +# --- stocks: MSN (default provider) -------------------------------------------- + +[[case]] +name = "stocks-quote" +args = ["stocks", "quote", "BBCA"] + +[[case]] +name = "stocks-quote-multi" +args = ["stocks", "quote", "BBCA,BBRI"] + +[[case]] +name = "stocks-history" +args = ["stocks", "history", "BBCA"] + +[[case]] +name = "stocks-history-msn" +args = ["stocks", "history", "BBCA", "--history-provider", "msn"] + +[[case]] +name = "stocks-technical" +args = ["stocks", "technical", "BBCA"] + +[[case]] +name = "stocks-growth" +args = ["stocks", "growth", "BBCA"] + +[[case]] +name = "stocks-valuation" +args = ["stocks", "valuation", "BBCA"] + +[[case]] +name = "stocks-valuation-nonfinite" +args = ["stocks", "valuation", "BBCA"] +env = { IDX_MOCK_MSN_KEYRATIOS_FIXTURE = "tests/fixtures/msn_keyratios_infinity.json" } + +[[case]] +name = "stocks-risk" +args = ["stocks", "risk", "BBCA"] + +[[case]] +name = "stocks-fundamental" +args = ["stocks", "fundamental", "BBCA"] + +[[case]] +name = "stocks-compare" +args = ["stocks", "compare", "BBCA", "BBRI"] + +[[case]] +name = "stocks-profile" +args = ["stocks", "profile", "BBCA"] + +[[case]] +name = "stocks-financials" +args = ["stocks", "financials", "BBCA"] + +[[case]] +name = "stocks-financials-income" +args = ["stocks", "financials", "BBCA", "--statement", "income"] + +[[case]] +name = "stocks-earnings" +args = ["stocks", "earnings", "BBCA"] + +[[case]] +name = "stocks-earnings-history-quarterly" +args = ["stocks", "earnings", "BBCA", "--history", "--quarterly"] + +[[case]] +name = "stocks-earnings-forecast-annual" +args = ["stocks", "earnings", "BBCA", "--forecast", "--annual"] + +[[case]] +name = "stocks-sentiment" +args = ["stocks", "sentiment", "BBCA"] + +[[case]] +name = "stocks-insights" +args = ["stocks", "insights", "BBCA"] + +[[case]] +name = "stocks-news" +args = ["stocks", "news", "BBCA", "--limit", "5"] + +[[case]] +name = "stocks-screen" +args = ["stocks", "screen"] + +[[case]] +name = "stocks-screen-worst" +args = ["stocks", "screen", "--filter", "worst-performers", "--limit", "5"] + +[[case]] +name = "stocks-screen-high-volume" +args = ["stocks", "screen", "--filter", "high-volume"] + +[[case]] +name = "stocks-screen-large-cap" +args = ["stocks", "screen", "--filter", "large-cap"] + +[[case]] +name = "stocks-quote-offline-warm" +args = ["--offline", "stocks", "quote", "BBCA"] +before = [["stocks", "quote", "BBCA"]] + +# --- stocks: Yahoo provider ----------------------------------------------------- + +[[case]] +name = "yahoo-quote" +args = ["stocks", "quote", "BBCA"] +env = { IDX_PROVIDER = "yahoo" } + +[[case]] +name = "yahoo-history" +args = ["stocks", "history", "BBCA"] +env = { IDX_PROVIDER = "yahoo" } + +[[case]] +name = "yahoo-fundamental" +args = ["stocks", "fundamental", "BBCA"] +env = { IDX_PROVIDER = "yahoo" } + +# --- errors --------------------------------------------------------------------- + +[[case]] +name = "error-usage-missing-symbol" +args = ["stocks", "quote"] + +[[case]] +name = "error-invalid-provider" +args = ["stocks", "quote", "BBCA"] +env = { IDX_PROVIDER = "bogus" } + +[[case]] +name = "error-yahoo-profile-unsupported" +args = ["stocks", "profile", "BBCA"] +env = { IDX_PROVIDER = "yahoo" } + +[[case]] +name = "error-industry-only-fundamentals" +args = ["stocks", "valuation", "BBCA"] +env = { IDX_MOCK_MSN_KEYRATIOS_FIXTURE = "tests/fixtures/msn_keyratios_industry_only.json" } + +[[case]] +name = "error-screen-region" +args = ["stocks", "screen", "--region", "eu"] + +[[case]] +name = "error-offline-no-cache-flags" +args = ["--offline", "--no-cache", "stocks", "quote", "BBCA"] + +[[case]] +name = "error-offline-cache-miss" +args = ["--offline", "stocks", "quote", "BBCA"] + +[[case]] +name = "error-provider-failure" +args = ["stocks", "quote", "BBCA"] +env = { IDX_MOCK_ERROR = "1" } + +[[case]] +name = "error-compare-total-failure" +args = ["stocks", "compare", "BBCA", "BBRI"] +env = { IDX_MOCK_ERROR = "1" } + +[[case]] +name = "error-fetch-bing-unsupported" +args = ["ownership", "import", "--fetch-bing", "BBCA"] + +# --- ownership -------------------------------------------------------------------- + +[[case]] +name = "ownership-releases-empty" +args = ["ownership", "releases"] + +[[case]] +name = "ownership-ticker-empty" +args = ["ownership", "ticker", "BBCA"] + +[[case]] +name = "ownership-import-xlsx" +args = ["ownership", "import", "--file", "tests/fixtures/ksei_above1_20260831_excerpt.xlsx"] + +[[case]] +name = "ownership-import-archive-txt" +args = ["ownership", "import", "--file", "tests/fixtures/ksei_balancepos_20260227_excerpt.txt"] + +[[case]] +name = "ownership-releases" +args = ["ownership", "releases"] +ownership = true + +[[case]] +name = "ownership-ticker" +args = ["ownership", "ticker", "BBCA"] +ownership = true + +[[case]] +name = "ownership-ticker-ksei" +args = ["ownership", "ticker", "BBCA", "--source", "ksei"] +ownership = true + +[[case]] +name = "ownership-entity" +args = ["ownership", "entity", "ANTHONI SALIM"] +ownership = true + +[[case]] +name = "ownership-search" +args = ["ownership", "search", "salim"] +ownership = true + +[[case]] +name = "ownership-cross-holders" +args = ["ownership", "cross-holders", "--min-tickers", "1"] +ownership = true + +[[case]] +name = "ownership-concentration" +args = ["ownership", "concentration"] +ownership = true + +[[case]] +name = "ownership-flow" +args = ["ownership", "flow", "BBCA"] +ownership = true + +[[case]] +name = "ownership-changes" +args = ["ownership", "changes", "--from", "2026-01-30", "--to", "2026-02-27"] +ownership = true + +[[case]] +name = "ownership-graph" +args = ["ownership", "graph", "BBCA"] +ownership = true + +[[case]] +name = "ownership-graph-dot" +args = ["ownership", "graph", "BBCA", "--format", "dot"] +ownership = true + +[[case]] +name = "ownership-resolve-unresolved" +args = ["ownership", "resolve", "unresolved"] +ownership = true + +[[case]] +name = "ownership-resolve-map" +args = ["ownership", "resolve", "map", "SALIM ANTHONI", "ANTHONI SALIM"] +ownership = true + +[[case]] +name = "ownership-resolve-merge" +args = ["ownership", "resolve", "merge", "1", "2"] +ownership = true diff --git a/tests/golden/completions-bash.golden b/tests/golden/completions-bash.golden new file mode 100644 index 0000000..5cee459 --- /dev/null +++ b/tests/golden/completions-bash.golden @@ -0,0 +1,2851 @@ +exit: 0 +--- stdout +_idx() { + local i cur prev opts cmd + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + cur="$2" + else + cur="${COMP_WORDS[COMP_CWORD]}" + fi + prev="$3" + cmd="" + opts="" + + for i in "${COMP_WORDS[@]:0:COMP_CWORD}" + do + case "${cmd},${i}" in + ",$1") + cmd="idx" + ;; + idx,cache) + cmd="idx__cache" + ;; + idx,completions) + cmd="idx__completions" + ;; + idx,config) + cmd="idx__config" + ;; + idx,help) + cmd="idx__help" + ;; + idx,ownership) + cmd="idx__ownership" + ;; + idx,stocks) + cmd="idx__stocks" + ;; + idx,version) + cmd="idx__version" + ;; + idx__cache,clear) + cmd="idx__cache__clear" + ;; + idx__cache,help) + cmd="idx__cache__help" + ;; + idx__cache,info) + cmd="idx__cache__info" + ;; + idx__cache__help,clear) + cmd="idx__cache__help__clear" + ;; + idx__cache__help,help) + cmd="idx__cache__help__help" + ;; + idx__cache__help,info) + cmd="idx__cache__help__info" + ;; + idx__config,get) + cmd="idx__config__get" + ;; + idx__config,help) + cmd="idx__config__help" + ;; + idx__config,init) + cmd="idx__config__init" + ;; + idx__config,path) + cmd="idx__config__path" + ;; + idx__config,set) + cmd="idx__config__set" + ;; + idx__config__help,get) + cmd="idx__config__help__get" + ;; + idx__config__help,help) + cmd="idx__config__help__help" + ;; + idx__config__help,init) + cmd="idx__config__help__init" + ;; + idx__config__help,path) + cmd="idx__config__help__path" + ;; + idx__config__help,set) + cmd="idx__config__help__set" + ;; + idx__help,cache) + cmd="idx__help__cache" + ;; + idx__help,completions) + cmd="idx__help__completions" + ;; + idx__help,config) + cmd="idx__help__config" + ;; + idx__help,help) + cmd="idx__help__help" + ;; + idx__help,ownership) + cmd="idx__help__ownership" + ;; + idx__help,stocks) + cmd="idx__help__stocks" + ;; + idx__help,version) + cmd="idx__help__version" + ;; + idx__help__cache,clear) + cmd="idx__help__cache__clear" + ;; + idx__help__cache,info) + cmd="idx__help__cache__info" + ;; + idx__help__config,get) + cmd="idx__help__config__get" + ;; + idx__help__config,init) + cmd="idx__help__config__init" + ;; + idx__help__config,path) + cmd="idx__help__config__path" + ;; + idx__help__config,set) + cmd="idx__help__config__set" + ;; + idx__help__ownership,changes) + cmd="idx__help__ownership__changes" + ;; + idx__help__ownership,concentration) + cmd="idx__help__ownership__concentration" + ;; + idx__help__ownership,cross-holders) + cmd="idx__help__ownership__cross__holders" + ;; + idx__help__ownership,discover) + cmd="idx__help__ownership__discover" + ;; + idx__help__ownership,entity) + cmd="idx__help__ownership__entity" + ;; + idx__help__ownership,flow) + cmd="idx__help__ownership__flow" + ;; + idx__help__ownership,graph) + cmd="idx__help__ownership__graph" + ;; + idx__help__ownership,import) + cmd="idx__help__ownership__import" + ;; + idx__help__ownership,releases) + cmd="idx__help__ownership__releases" + ;; + idx__help__ownership,resolve) + cmd="idx__help__ownership__resolve" + ;; + idx__help__ownership,search) + cmd="idx__help__ownership__search" + ;; + idx__help__ownership,sync) + cmd="idx__help__ownership__sync" + ;; + idx__help__ownership,ticker) + cmd="idx__help__ownership__ticker" + ;; + idx__help__ownership__resolve,map) + cmd="idx__help__ownership__resolve__map" + ;; + idx__help__ownership__resolve,merge) + cmd="idx__help__ownership__resolve__merge" + ;; + idx__help__ownership__resolve,unresolved) + cmd="idx__help__ownership__resolve__unresolved" + ;; + idx__help__stocks,compare) + cmd="idx__help__stocks__compare" + ;; + idx__help__stocks,earnings) + cmd="idx__help__stocks__earnings" + ;; + idx__help__stocks,financials) + cmd="idx__help__stocks__financials" + ;; + idx__help__stocks,fundamental) + cmd="idx__help__stocks__fundamental" + ;; + idx__help__stocks,growth) + cmd="idx__help__stocks__growth" + ;; + idx__help__stocks,history) + cmd="idx__help__stocks__history" + ;; + idx__help__stocks,insights) + cmd="idx__help__stocks__insights" + ;; + idx__help__stocks,news) + cmd="idx__help__stocks__news" + ;; + idx__help__stocks,profile) + cmd="idx__help__stocks__profile" + ;; + idx__help__stocks,quote) + cmd="idx__help__stocks__quote" + ;; + idx__help__stocks,risk) + cmd="idx__help__stocks__risk" + ;; + idx__help__stocks,screen) + cmd="idx__help__stocks__screen" + ;; + idx__help__stocks,sentiment) + cmd="idx__help__stocks__sentiment" + ;; + idx__help__stocks,technical) + cmd="idx__help__stocks__technical" + ;; + idx__help__stocks,valuation) + cmd="idx__help__stocks__valuation" + ;; + idx__ownership,changes) + cmd="idx__ownership__changes" + ;; + idx__ownership,concentration) + cmd="idx__ownership__concentration" + ;; + idx__ownership,cross-holders) + cmd="idx__ownership__cross__holders" + ;; + idx__ownership,discover) + cmd="idx__ownership__discover" + ;; + idx__ownership,entity) + cmd="idx__ownership__entity" + ;; + idx__ownership,flow) + cmd="idx__ownership__flow" + ;; + idx__ownership,graph) + cmd="idx__ownership__graph" + ;; + idx__ownership,help) + cmd="idx__ownership__help" + ;; + idx__ownership,import) + cmd="idx__ownership__import" + ;; + idx__ownership,releases) + cmd="idx__ownership__releases" + ;; + idx__ownership,resolve) + cmd="idx__ownership__resolve" + ;; + idx__ownership,search) + cmd="idx__ownership__search" + ;; + idx__ownership,sync) + cmd="idx__ownership__sync" + ;; + idx__ownership,ticker) + cmd="idx__ownership__ticker" + ;; + idx__ownership__help,changes) + cmd="idx__ownership__help__changes" + ;; + idx__ownership__help,concentration) + cmd="idx__ownership__help__concentration" + ;; + idx__ownership__help,cross-holders) + cmd="idx__ownership__help__cross__holders" + ;; + idx__ownership__help,discover) + cmd="idx__ownership__help__discover" + ;; + idx__ownership__help,entity) + cmd="idx__ownership__help__entity" + ;; + idx__ownership__help,flow) + cmd="idx__ownership__help__flow" + ;; + idx__ownership__help,graph) + cmd="idx__ownership__help__graph" + ;; + idx__ownership__help,help) + cmd="idx__ownership__help__help" + ;; + idx__ownership__help,import) + cmd="idx__ownership__help__import" + ;; + idx__ownership__help,releases) + cmd="idx__ownership__help__releases" + ;; + idx__ownership__help,resolve) + cmd="idx__ownership__help__resolve" + ;; + idx__ownership__help,search) + cmd="idx__ownership__help__search" + ;; + idx__ownership__help,sync) + cmd="idx__ownership__help__sync" + ;; + idx__ownership__help,ticker) + cmd="idx__ownership__help__ticker" + ;; + idx__ownership__help__resolve,map) + cmd="idx__ownership__help__resolve__map" + ;; + idx__ownership__help__resolve,merge) + cmd="idx__ownership__help__resolve__merge" + ;; + idx__ownership__help__resolve,unresolved) + cmd="idx__ownership__help__resolve__unresolved" + ;; + idx__ownership__resolve,help) + cmd="idx__ownership__resolve__help" + ;; + idx__ownership__resolve,map) + cmd="idx__ownership__resolve__map" + ;; + idx__ownership__resolve,merge) + cmd="idx__ownership__resolve__merge" + ;; + idx__ownership__resolve,unresolved) + cmd="idx__ownership__resolve__unresolved" + ;; + idx__ownership__resolve__help,help) + cmd="idx__ownership__resolve__help__help" + ;; + idx__ownership__resolve__help,map) + cmd="idx__ownership__resolve__help__map" + ;; + idx__ownership__resolve__help,merge) + cmd="idx__ownership__resolve__help__merge" + ;; + idx__ownership__resolve__help,unresolved) + cmd="idx__ownership__resolve__help__unresolved" + ;; + idx__stocks,compare) + cmd="idx__stocks__compare" + ;; + idx__stocks,earnings) + cmd="idx__stocks__earnings" + ;; + idx__stocks,financials) + cmd="idx__stocks__financials" + ;; + idx__stocks,fundamental) + cmd="idx__stocks__fundamental" + ;; + idx__stocks,growth) + cmd="idx__stocks__growth" + ;; + idx__stocks,help) + cmd="idx__stocks__help" + ;; + idx__stocks,history) + cmd="idx__stocks__history" + ;; + idx__stocks,insights) + cmd="idx__stocks__insights" + ;; + idx__stocks,news) + cmd="idx__stocks__news" + ;; + idx__stocks,profile) + cmd="idx__stocks__profile" + ;; + idx__stocks,quote) + cmd="idx__stocks__quote" + ;; + idx__stocks,risk) + cmd="idx__stocks__risk" + ;; + idx__stocks,screen) + cmd="idx__stocks__screen" + ;; + idx__stocks,sentiment) + cmd="idx__stocks__sentiment" + ;; + idx__stocks,technical) + cmd="idx__stocks__technical" + ;; + idx__stocks,valuation) + cmd="idx__stocks__valuation" + ;; + idx__stocks__help,compare) + cmd="idx__stocks__help__compare" + ;; + idx__stocks__help,earnings) + cmd="idx__stocks__help__earnings" + ;; + idx__stocks__help,financials) + cmd="idx__stocks__help__financials" + ;; + idx__stocks__help,fundamental) + cmd="idx__stocks__help__fundamental" + ;; + idx__stocks__help,growth) + cmd="idx__stocks__help__growth" + ;; + idx__stocks__help,help) + cmd="idx__stocks__help__help" + ;; + idx__stocks__help,history) + cmd="idx__stocks__help__history" + ;; + idx__stocks__help,insights) + cmd="idx__stocks__help__insights" + ;; + idx__stocks__help,news) + cmd="idx__stocks__help__news" + ;; + idx__stocks__help,profile) + cmd="idx__stocks__help__profile" + ;; + idx__stocks__help,quote) + cmd="idx__stocks__help__quote" + ;; + idx__stocks__help,risk) + cmd="idx__stocks__help__risk" + ;; + idx__stocks__help,screen) + cmd="idx__stocks__help__screen" + ;; + idx__stocks__help,sentiment) + cmd="idx__stocks__help__sentiment" + ;; + idx__stocks__help,technical) + cmd="idx__stocks__help__technical" + ;; + idx__stocks__help,valuation) + cmd="idx__stocks__help__valuation" + ;; + *) + ;; + esac + done + + case "${cmd}" in + idx) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help stocks config cache ownership completions version help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__cache) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help info clear help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__cache__clear) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__cache__help) + opts="info clear help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__cache__help__clear) + 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__cache__help__help) + 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__cache__help__info) + 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__cache__info) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__completions) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help bash zsh fish" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help init get set path help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config__get) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config__help) + opts="init get set path help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config__help__get) + 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__config__help__help) + 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__config__help__init) + 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__config__help__path) + 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__config__help__set) + 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__config__init) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config__path) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__config__set) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help) + opts="stocks config cache ownership completions version help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__cache) + opts="info clear" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__cache__clear) + 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__cache__info) + 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__completions) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__config) + opts="init get set path" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__config__get) + 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__config__init) + 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__config__path) + 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__config__set) + 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__help) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__ownership) + opts="discover import sync ticker entity search cross-holders concentration flow changes graph resolve releases" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__ownership__changes) + 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__ownership__concentration) + 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__ownership__cross__holders) + 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__ownership__discover) + 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__ownership__entity) + 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__ownership__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__ownership__graph) + 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__ownership__import) + 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__ownership__releases) + 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__ownership__resolve) + opts="unresolved map merge" + 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__ownership__resolve__map) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__ownership__resolve__merge) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__ownership__resolve__unresolved) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__ownership__search) + 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__ownership__sync) + 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__ownership__ticker) + 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) + opts="quote history technical growth valuation risk fundamental profile financials earnings sentiment insights news screen compare" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__stocks__compare) + 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__earnings) + 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__financials) + 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 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__help__stocks__growth) + 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__history) + 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__insights) + 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__news) + 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__profile) + 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__quote) + 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__risk) + 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__screen) + 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__sentiment) + 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__technical) + 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__valuation) + 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__version) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help discover import sync ticker entity search cross-holders concentration flow changes graph resolve releases help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__changes) + opts="-o -q -v -h --from --to --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --to) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__concentration) + opts="-o -q -v -h --by --top --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --by) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --top) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__cross__holders) + opts="-o -q -v -h --top --min-tickers --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --top) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --min-tickers) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__discover) + opts="-o -q -v -h --family --limit --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --family) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --limit) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__entity) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__flow) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__graph) + opts="-o -q -v -h --depth --format --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --depth) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --format) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__help) + opts="discover import sync ticker entity search cross-holders concentration flow changes graph resolve releases help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__help__changes) + 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__ownership__help__concentration) + 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__ownership__help__cross__holders) + 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__ownership__help__discover) + 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__ownership__help__entity) + 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__ownership__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__ownership__help__graph) + 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__ownership__help__help) + 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__ownership__help__import) + 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__ownership__help__releases) + 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__ownership__help__resolve) + opts="unresolved map merge" + 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__ownership__help__resolve__map) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__help__resolve__merge) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__help__resolve__unresolved) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__help__search) + 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__ownership__help__sync) + 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__ownership__help__ticker) + 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__ownership__import) + opts="-o -q -v -h --url --file --fetch-bing --force --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --url) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --fetch-bing) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__releases) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help unresolved map merge help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__help) + opts="unresolved map merge help" + 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__ownership__resolve__help__help) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__help__map) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__help__merge) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__help__unresolved) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 5 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__map) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__merge) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__resolve__unresolved) + opts="-o -q -v -h --limit --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --limit) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__search) + opts="-o -q -v -h --limit --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --limit) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__sync) + opts="-o -q -v -h --manifest --force --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --manifest) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__ownership__ticker) + opts="-o -q -v -h --source --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --source) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + 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 profile financials earnings sentiment insights news screen compare help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__compare) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help ..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__earnings) + opts="-o -q -v -h --forecast --history --annual --quarterly --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__financials) + opts="-o -q -v -h --statement --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --statement) + COMPREPLY=($(compgen -W "income balance cashflow" -- "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${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 " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__growth) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__help) + opts="quote history technical growth valuation risk fundamental profile financials earnings sentiment insights news screen compare help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__help__compare) + 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__earnings) + 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__financials) + 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 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__help__growth) + 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__help) + 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__history) + 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__insights) + 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__news) + 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__profile) + 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__quote) + 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__risk) + 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__screen) + 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__sentiment) + 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__technical) + 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__valuation) + 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__history) + opts="-o -q -v -h --period --interval --history-provider --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --period) + COMPREPLY=($(compgen -W "1d 5d 1mo 3mo 6mo 1y 2y 5y" -- "${cur}")) + return 0 + ;; + --interval) + COMPREPLY=($(compgen -W "1d 1wk 1mo" -- "${cur}")) + return 0 + ;; + --history-provider) + COMPREPLY=($(compgen -W "auto yahoo msn" -- "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__insights) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__news) + opts="-o -q -v -h --limit --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --limit) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__profile) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__quote) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help ..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__risk) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__screen) + opts="-o -q -v -h --filter --region --limit --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --filter) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --region) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --limit) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__sentiment) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__technical) + opts="-o -q -v -h --history-provider --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --history-provider) + COMPREPLY=($(compgen -W "auto yahoo msn" -- "${cur}")) + return 0 + ;; + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__stocks__valuation) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + idx__version) + opts="-o -q -v -h --output --no-color --quiet --verbose --offline --no-cache --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --output) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -W "table json" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + esac +} + +if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then + complete -F _idx -o nosort -o bashdefault -o default idx +else + complete -F _idx -o bashdefault -o default idx +fi +--- stderr diff --git a/tests/golden/config-get.golden b/tests/golden/config-get.golden new file mode 100644 index 0000000..c9ca9de --- /dev/null +++ b/tests/golden/config-get.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +msn +--- stderr diff --git a/tests/golden/config-init.golden b/tests/golden/config-init.golden new file mode 100644 index 0000000..ab029c5 --- /dev/null +++ b/tests/golden/config-init.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +/config/idx/config.toml +--- stderr diff --git a/tests/golden/config-path.golden b/tests/golden/config-path.golden new file mode 100644 index 0000000..ab029c5 --- /dev/null +++ b/tests/golden/config-path.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +/config/idx/config.toml +--- stderr diff --git a/tests/golden/config-set.golden b/tests/golden/config-set.golden new file mode 100644 index 0000000..aecf1eb --- /dev/null +++ b/tests/golden/config-set.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +ok +--- stderr diff --git a/tests/golden/error-compare-total-failure.json.golden b/tests/golden/error-compare-total-failure.json.golden new file mode 100644 index 0000000..9c72e99 --- /dev/null +++ b/tests/golden/error-compare-total-failure.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "PROVIDERUNAVAILABLE", + "error": true, + "message": "provider unavailable" +} diff --git a/tests/golden/error-compare-total-failure.table.golden b/tests/golden/error-compare-total-failure.table.golden new file mode 100644 index 0000000..fc7d924 --- /dev/null +++ b/tests/golden/error-compare-total-failure.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: provider unavailable diff --git a/tests/golden/error-fetch-bing-unsupported.json.golden b/tests/golden/error-fetch-bing-unsupported.json.golden new file mode 100644 index 0000000..1c73d67 --- /dev/null +++ b/tests/golden/error-fetch-bing-unsupported.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "UNSUPPORTED", + "error": true, + "message": "unsupported: --fetch-bing import is not implemented yet" +} diff --git a/tests/golden/error-fetch-bing-unsupported.table.golden b/tests/golden/error-fetch-bing-unsupported.table.golden new file mode 100644 index 0000000..bf6ba34 --- /dev/null +++ b/tests/golden/error-fetch-bing-unsupported.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: unsupported: --fetch-bing import is not implemented yet diff --git a/tests/golden/error-industry-only-fundamentals.json.golden b/tests/golden/error-industry-only-fundamentals.json.golden new file mode 100644 index 0000000..d8fb2a5 --- /dev/null +++ b/tests/golden/error-industry-only-fundamentals.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "PARSEERROR", + "error": true, + "message": "parse error: unsupported: company fundamentals unavailable from MSN; industry fallback is disabled" +} diff --git a/tests/golden/error-industry-only-fundamentals.table.golden b/tests/golden/error-industry-only-fundamentals.table.golden new file mode 100644 index 0000000..b2bf359 --- /dev/null +++ b/tests/golden/error-industry-only-fundamentals.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: parse error: unsupported: company fundamentals unavailable from MSN; industry fallback is disabled diff --git a/tests/golden/error-invalid-provider.json.golden b/tests/golden/error-invalid-provider.json.golden new file mode 100644 index 0000000..cb67653 --- /dev/null +++ b/tests/golden/error-invalid-provider.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "CONFIGERROR", + "error": true, + "message": "config error: invalid provider 'bogus' (expected yahoo or msn)" +} diff --git a/tests/golden/error-invalid-provider.table.golden b/tests/golden/error-invalid-provider.table.golden new file mode 100644 index 0000000..8b5ee37 --- /dev/null +++ b/tests/golden/error-invalid-provider.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: config error: invalid provider 'bogus' (expected yahoo or msn) diff --git a/tests/golden/error-offline-cache-miss.json.golden b/tests/golden/error-offline-cache-miss.json.golden new file mode 100644 index 0000000..6006ad7 --- /dev/null +++ b/tests/golden/error-offline-cache-miss.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "CACHEMISS", + "error": true, + "message": "cache miss: msn-quote/BBCA.JK" +} diff --git a/tests/golden/error-offline-cache-miss.table.golden b/tests/golden/error-offline-cache-miss.table.golden new file mode 100644 index 0000000..27e5af2 --- /dev/null +++ b/tests/golden/error-offline-cache-miss.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: cache miss: msn-quote/BBCA.JK diff --git a/tests/golden/error-offline-no-cache-flags.json.golden b/tests/golden/error-offline-no-cache-flags.json.golden new file mode 100644 index 0000000..8d9b175 --- /dev/null +++ b/tests/golden/error-offline-no-cache-flags.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "INVALIDINPUT", + "error": true, + "message": "invalid input: cannot combine --offline with --no-cache" +} diff --git a/tests/golden/error-offline-no-cache-flags.table.golden b/tests/golden/error-offline-no-cache-flags.table.golden new file mode 100644 index 0000000..0dcead6 --- /dev/null +++ b/tests/golden/error-offline-no-cache-flags.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: invalid input: cannot combine --offline with --no-cache diff --git a/tests/golden/error-provider-failure.json.golden b/tests/golden/error-provider-failure.json.golden new file mode 100644 index 0000000..9c72e99 --- /dev/null +++ b/tests/golden/error-provider-failure.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "PROVIDERUNAVAILABLE", + "error": true, + "message": "provider unavailable" +} diff --git a/tests/golden/error-provider-failure.table.golden b/tests/golden/error-provider-failure.table.golden new file mode 100644 index 0000000..fc7d924 --- /dev/null +++ b/tests/golden/error-provider-failure.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: provider unavailable diff --git a/tests/golden/error-screen-region.json.golden b/tests/golden/error-screen-region.json.golden new file mode 100644 index 0000000..e3ad825 --- /dev/null +++ b/tests/golden/error-screen-region.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "INVALIDINPUT", + "error": true, + "message": "invalid input: invalid screener region 'eu' (expected one of: id, us, sg, hk, jp)" +} diff --git a/tests/golden/error-screen-region.table.golden b/tests/golden/error-screen-region.table.golden new file mode 100644 index 0000000..39ac5be --- /dev/null +++ b/tests/golden/error-screen-region.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: invalid input: invalid screener region 'eu' (expected one of: id, us, sg, hk, jp) diff --git a/tests/golden/error-usage-missing-symbol.json.golden b/tests/golden/error-usage-missing-symbol.json.golden new file mode 100644 index 0000000..e653302 --- /dev/null +++ b/tests/golden/error-usage-missing-symbol.json.golden @@ -0,0 +1,9 @@ +exit: 2 +--- stdout +--- stderr +error: the following required arguments were not provided: + ... + +Usage: idx stocks quote ... + +For more information, try '--help'. diff --git a/tests/golden/error-usage-missing-symbol.table.golden b/tests/golden/error-usage-missing-symbol.table.golden new file mode 100644 index 0000000..e653302 --- /dev/null +++ b/tests/golden/error-usage-missing-symbol.table.golden @@ -0,0 +1,9 @@ +exit: 2 +--- stdout +--- stderr +error: the following required arguments were not provided: + ... + +Usage: idx stocks quote ... + +For more information, try '--help'. diff --git a/tests/golden/error-yahoo-profile-unsupported.json.golden b/tests/golden/error-yahoo-profile-unsupported.json.golden new file mode 100644 index 0000000..237c99d --- /dev/null +++ b/tests/golden/error-yahoo-profile-unsupported.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "UNSUPPORTED", + "error": true, + "message": "unsupported: BBCA.JK: command requires --provider msn" +} diff --git a/tests/golden/error-yahoo-profile-unsupported.table.golden b/tests/golden/error-yahoo-profile-unsupported.table.golden new file mode 100644 index 0000000..762324c --- /dev/null +++ b/tests/golden/error-yahoo-profile-unsupported.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: unsupported: BBCA.JK: command requires --provider msn diff --git a/tests/golden/help-cache.golden b/tests/golden/help-cache.golden new file mode 100644 index 0000000..40f2b4f --- /dev/null +++ b/tests/golden/help-cache.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +Manage local cache + +Usage: idx cache [OPTIONS] + +Commands: + info Show cache usage information + clear Clear all cached files + help Print this message or the help of the given subcommand(s) + +Options: + -o, --output Output format [possible values: table, json] + --no-color Disable colored output + -q, --quiet Suppress non-essential output + -v, --verbose... Increase verbosity (-v, -vv) + --offline Run without network requests + --no-cache Bypass local cache + -h, --help Print help +--- stderr diff --git a/tests/golden/help-config.golden b/tests/golden/help-config.golden new file mode 100644 index 0000000..485641f --- /dev/null +++ b/tests/golden/help-config.golden @@ -0,0 +1,22 @@ +exit: 0 +--- stdout +Manage configuration + +Usage: idx config [OPTIONS] + +Commands: + init Create default configuration file + get Get a configuration value by key + set Set a configuration value by key + path Print configuration file path + help Print this message or the help of the given subcommand(s) + +Options: + -o, --output Output format [possible values: table, json] + --no-color Disable colored output + -q, --quiet Suppress non-essential output + -v, --verbose... Increase verbosity (-v, -vv) + --offline Run without network requests + --no-cache Bypass local cache + -h, --help Print help +--- stderr diff --git a/tests/golden/help-ownership.golden b/tests/golden/help-ownership.golden new file mode 100644 index 0000000..44bb1e8 --- /dev/null +++ b/tests/golden/help-ownership.golden @@ -0,0 +1,58 @@ +exit: 0 +--- stdout +Ownership intelligence (KSEI + Bing). + +Preferred bootstrap path: + 1. Run `idx ownership sync` to install or refresh a maintained SQLite snapshot. + 2. If no snapshot manifest is available, run `idx ownership discover` and then `idx ownership import --url `. + 3. Local `.xlsx`, `.pdf`, `.zip`, and `.txt` imports remain available for manual or fallback workflows. + +Usage: idx ownership [OPTIONS] + +Commands: + discover Discover the latest IDX-hosted ownership report URLs + import Import ownership data directly from source files + sync Install or refresh a maintained ownership SQLite snapshot + ticker Show all holders for a ticker (KSEI + Bing combined) + entity Show all holdings for an entity across tickers + search Search entities by name + cross-holders Rank entities by cross-ownership breadth + concentration Rank tickers by ownership concentration + flow Show Bing institutional flow for a ticker + changes Diff two KSEI releases + graph Ownership network graph + resolve Manual entity resolution workflow + releases List imported KSEI releases + help Print this message or the help of the given subcommand(s) + +Options: + -o, --output + Output format + + [possible values: table, json] + + --no-color + Disable colored output + + -q, --quiet + Suppress non-essential output + + -v, --verbose... + Increase verbosity (-v, -vv) + + --offline + Run without network requests + + --no-cache + Bypass local cache + + -h, --help + Print help (see a summary with '-h') + +Examples: + idx ownership sync + idx ownership sync --manifest /path/to/ownership-snapshot-manifest.json + idx ownership discover --limit 1 + idx ownership import --url + idx ownership releases +--- stderr diff --git a/tests/golden/help-root.golden b/tests/golden/help-root.golden new file mode 100644 index 0000000..6095643 --- /dev/null +++ b/tests/golden/help-root.golden @@ -0,0 +1,40 @@ +exit: 0 +--- stdout +idx-cli is a Rust command-line tool for Indonesian stock market (IDX) analysis. +It supports quote lookup, historical data, local caching, and output formats for both humans and AI agents. + +Usage: idx [OPTIONS] + +Commands: + stocks Stock data and analysis + config Manage configuration + cache Manage local cache + ownership Ownership intelligence (bootstrap with `ownership sync`) + completions Generate shell completions + version Show idx-cli version + help Print this message or the help of the given subcommand(s) + +Options: + -o, --output + Output format + + [possible values: table, json] + + --no-color + Disable colored output + + -q, --quiet + Suppress non-essential output + + -v, --verbose... + Increase verbosity (-v, -vv) + + --offline + Run without network requests + + --no-cache + Bypass local cache + + -h, --help + Print help (see a summary with '-h') +--- stderr diff --git a/tests/golden/help-stocks.golden b/tests/golden/help-stocks.golden new file mode 100644 index 0000000..1c51546 --- /dev/null +++ b/tests/golden/help-stocks.golden @@ -0,0 +1,33 @@ +exit: 0 +--- stdout +Stock data and analysis + +Usage: idx stocks [OPTIONS] + +Commands: + quote Get real-time stock quotes + history Get historical OHLC data + technical Run technical analysis on a stock + growth Run growth analysis on a stock + valuation Run valuation analysis on a stock + risk Run risk analysis on a stock + fundamental Run full fundamental analysis on a stock + profile Get company profile + financials Get financial statements + earnings Get earnings report + sentiment Get crowd sentiment + insights Get AI insights + news Get stock news + screen MSN screener + compare Compare fundamentals across stocks + help Print this message or the help of the given subcommand(s) + +Options: + -o, --output Output format [possible values: table, json] + --no-color Disable colored output + -q, --quiet Suppress non-essential output + -v, --verbose... Increase verbosity (-v, -vv) + --offline Run without network requests + --no-cache Bypass local cache + -h, --help Print help +--- stderr diff --git a/tests/golden/ownership-changes.json.golden b/tests/golden/ownership-changes.json.golden new file mode 100644 index 0000000..138b1cb --- /dev/null +++ b/tests/golden/ownership-changes.json.golden @@ -0,0 +1,109 @@ +exit: 0 +--- stdout +[ + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL ID", + "change_type": "decreased", + "old_bps": 2400, + "new_bps": 2334, + "delta_bps": -66 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL IS", + "change_type": "increased", + "old_bps": 75, + "new_bps": 129, + "delta_bps": 54 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL MF", + "change_type": "increased", + "old_bps": 117, + "new_bps": 152, + "delta_bps": 35 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN MF", + "change_type": "increased", + "old_bps": 307, + "new_bps": 337, + "delta_bps": 30 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN OT", + "change_type": "decreased", + "old_bps": 91, + "new_bps": 64, + "delta_bps": -27 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN PF", + "change_type": "decreased", + "old_bps": 76, + "new_bps": 56, + "delta_bps": -20 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN CP", + "change_type": "increased", + "old_bps": 72, + "new_bps": 86, + "delta_bps": 14 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL CP", + "change_type": "decreased", + "old_bps": 6481, + "new_bps": 6467, + "delta_bps": -14 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL SC", + "change_type": "decreased", + "old_bps": 36, + "new_bps": 29, + "delta_bps": -7 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN IB", + "change_type": "decreased", + "old_bps": 267, + "new_bps": 263, + "delta_bps": -4 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN SC", + "change_type": "increased", + "old_bps": 51, + "new_bps": 55, + "delta_bps": 4 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE LOCAL PF", + "change_type": "increased", + "old_bps": 17, + "new_bps": 20, + "delta_bps": 3 + }, + { + "ticker_code": "AADI", + "entity_name": "KSEI AGGREGATE FOREIGN ID", + "change_type": "decreased", + "old_bps": 3, + "new_bps": 2, + "delta_bps": -1 + } +] +--- stderr diff --git a/tests/golden/ownership-changes.table.golden b/tests/golden/ownership-changes.table.golden new file mode 100644 index 0000000..7f08993 --- /dev/null +++ b/tests/golden/ownership-changes.table.golden @@ -0,0 +1,32 @@ +exit: 0 +--- stdout +┌────────┬───────────────────────────┬─────────────────────┬────────┬────────┬────────┐ +│ TICKER ┆ ENTITY ┆ CHANGE ┆ OLD% ┆ NEW% ┆ DELTA% │ +╞════════╪═══════════════════════════╪═════════════════════╪════════╪════════╪════════╡ +│ AADI ┆ KSEI AGGREGATE LOCAL ID ┆ DECREASED ┆ 24.00% ┆ 23.34% ┆ -0.66% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE LOCAL IS ┆ INCREASED ┆ 0.75% ┆ 1.29% ┆ +0.54% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE LOCAL MF ┆ INCREASED ┆ 1.17% ┆ 1.52% ┆ +0.35% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN MF ┆ INCREASED ┆ 3.07% ┆ 3.37% ┆ +0.30% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN OT ┆ DECREASED ┆ 0.91% ┆ 0.64% ┆ -0.27% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN PF ┆ DECREASED ┆ 0.76% ┆ 0.56% ┆ -0.20% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN CP ┆ INCREASED ┆ 0.72% ┆ 0.86% ┆ +0.14% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE LOCAL CP ┆ DECREASED ┆ 64.81% ┆ 64.67% ┆ -0.14% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE LOCAL SC ┆ DECREASED ┆ 0.36% ┆ 0.29% ┆ -0.07% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN IB ┆ DECREASED ┆ 2.67% ┆ 2.63% ┆ -0.04% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN SC ┆ INCREASED ┆ 0.51% ┆ 0.55% ┆ +0.04% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE LOCAL PF ┆ INCREASED ┆ 0.17% ┆ 0.20% ┆ +0.03% │ +├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ AADI ┆ KSEI AGGREGATE FOREIGN ID ┆ DECREASED ┆ 0.03% ┆ 0.02% ┆ -0.01% │ +└────────┴───────────────────────────┴─────────────────────┴────────┴────────┴────────┘ +--- stderr diff --git a/tests/golden/ownership-concentration.json.golden b/tests/golden/ownership-concentration.json.golden new file mode 100644 index 0000000..2c5afc3 --- /dev/null +++ b/tests/golden/ownership-concentration.json.golden @@ -0,0 +1,45 @@ +exit: 0 +--- stdout +[ + [ + "AALI", + { + "top1_bps": 7968, + "top3_bps": 7968, + "hhi": 6348, + "free_float_bps": 2032, + "holder_count": 1 + } + ], + [ + "BBCA", + { + "top1_bps": 5494, + "top3_bps": 5716, + "hhi": 3021, + "free_float_bps": 4182, + "holder_count": 4 + } + ], + [ + "AADI", + { + "top1_bps": 4110, + "top3_bps": 6230, + "hhi": 2009, + "free_float_bps": 2030, + "holder_count": 10 + } + ], + [ + "ABBA", + { + "top1_bps": 283, + "top3_bps": 418, + "hhi": 9, + "free_float_bps": 9582, + "holder_count": 2 + } + ] +] +--- stderr diff --git a/tests/golden/ownership-concentration.table.golden b/tests/golden/ownership-concentration.table.golden new file mode 100644 index 0000000..b7472ac --- /dev/null +++ b/tests/golden/ownership-concentration.table.golden @@ -0,0 +1,14 @@ +exit: 0 +--- stdout +┌──────┬────────┬────────┬────────┬──────┬─────────────┬─────────┐ +│ RANK ┆ TICKER ┆ TOP1% ┆ TOP3% ┆ HHI ┆ FREE_FLOAT% ┆ HOLDERS │ +╞══════╪════════╪════════╪════════╪══════╪═════════════╪═════════╡ +│ 1 ┆ AALI ┆ 79.68% ┆ 79.68% ┆ 6348 ┆ 20.32% ┆ 1 │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ 2 ┆ BBCA ┆ 54.94% ┆ 57.16% ┆ 3021 ┆ 41.82% ┆ 4 │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ 3 ┆ AADI ┆ 41.10% ┆ 62.30% ┆ 2009 ┆ 20.30% ┆ 10 │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ 4 ┆ ABBA ┆ 2.83% ┆ 4.18% ┆ 9 ┆ 95.82% ┆ 2 │ +└──────┴────────┴────────┴────────┴──────┴─────────────┴─────────┘ +--- stderr diff --git a/tests/golden/ownership-cross-holders.json.golden b/tests/golden/ownership-cross-holders.json.golden new file mode 100644 index 0000000..9c4685e --- /dev/null +++ b/tests/golden/ownership-cross-holders.json.golden @@ -0,0 +1,175 @@ +exit: 0 +--- stdout +[ + { + "entity": { + "id": 11, + "canonical_name": "ASTRA INTERNATIONAL", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 7968 + }, + { + "entity": { + "id": 14, + "canonical_name": "DWIMURIA INVESTAMA ANDALAN", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 5494 + }, + { + "entity": { + "id": 4, + "canonical_name": "ADARO STRATEGIC INVESTMENTS", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 4110 + }, + { + "entity": { + "id": 5, + "canonical_name": "ALAMTRI RESOURCES INDONESIA TBK PT", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 1537 + }, + { + "entity": { + "id": 7, + "canonical_name": "GARIBALDI THOHIR", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 583 + }, + { + "entity": { + "id": 2, + "canonical_name": "SARATOGA INVESTAMA SEDAYA", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 438 + }, + { + "entity": { + "id": 9, + "canonical_name": "EDWIN SOERYADJAYA", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 352 + }, + { + "entity": { + "id": 8, + "canonical_name": "IR. T. PERMADI RACHMAT", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 348 + }, + { + "entity": { + "id": 13, + "canonical_name": "YAYASAN ABDI BANGSA", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 283 + }, + { + "entity": { + "id": 10, + "canonical_name": "SANDIAGA SALAHUDDIN UNO", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 204 + }, + { + "entity": { + "id": 1, + "canonical_name": "PERSADA CAPITAL INVESTAMA", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 154 + }, + { + "entity": { + "id": 12, + "canonical_name": "YAYASAN BINA SEJAHTERA WARGA BULOG", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 135 + }, + { + "entity": { + "id": 6, + "canonical_name": "FTAG INVESTMENT BANK", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 129 + }, + { + "entity": { + "id": 17, + "canonical_name": "ANTHONI SALIM", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 115 + }, + { + "entity": { + "id": 3, + "canonical_name": "TRINUGRAHA THOHIR", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 115 + }, + { + "entity": { + "id": 16, + "canonical_name": "TRICIPTA MANDHALA GUMILANG", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 107 + }, + { + "entity": { + "id": 15, + "canonical_name": "CATURGUWIRATNA SUMAPALA", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "total_bps": 102 + } +] +--- stderr diff --git a/tests/golden/ownership-cross-holders.table.golden b/tests/golden/ownership-cross-holders.table.golden new file mode 100644 index 0000000..ddce4ba --- /dev/null +++ b/tests/golden/ownership-cross-holders.table.golden @@ -0,0 +1,40 @@ +exit: 0 +--- stdout +┌──────┬────────────────────────────────────┬─────────┬───────────┐ +│ RANK ┆ ENTITY ┆ TICKERS ┆ TOTAL_BPS │ +╞══════╪════════════════════════════════════╪═════════╪═══════════╡ +│ 1 ┆ ASTRA INTERNATIONAL ┆ 1 ┆ 79.68% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2 ┆ DWIMURIA INVESTAMA ANDALAN ┆ 1 ┆ 54.94% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 3 ┆ ADARO STRATEGIC INVESTMENTS ┆ 1 ┆ 41.10% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 4 ┆ ALAMTRI RESOURCES INDONESIA TBK PT ┆ 1 ┆ 15.37% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 5 ┆ GARIBALDI THOHIR ┆ 1 ┆ 5.83% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 6 ┆ SARATOGA INVESTAMA SEDAYA ┆ 1 ┆ 4.38% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 7 ┆ EDWIN SOERYADJAYA ┆ 1 ┆ 3.52% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 8 ┆ IR. T. PERMADI RACHMAT ┆ 1 ┆ 3.48% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 9 ┆ YAYASAN ABDI BANGSA ┆ 1 ┆ 2.83% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 10 ┆ SANDIAGA SALAHUDDIN UNO ┆ 1 ┆ 2.04% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 11 ┆ PERSADA CAPITAL INVESTAMA ┆ 1 ┆ 1.54% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 12 ┆ YAYASAN BINA SEJAHTERA WARGA BULOG ┆ 1 ┆ 1.35% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 13 ┆ FTAG INVESTMENT BANK ┆ 1 ┆ 1.29% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 14 ┆ ANTHONI SALIM ┆ 1 ┆ 1.15% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 15 ┆ TRINUGRAHA THOHIR ┆ 1 ┆ 1.15% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 16 ┆ TRICIPTA MANDHALA GUMILANG ┆ 1 ┆ 1.07% │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ 17 ┆ CATURGUWIRATNA SUMAPALA ┆ 1 ┆ 1.02% │ +└──────┴────────────────────────────────────┴─────────┴───────────┘ +--- stderr diff --git a/tests/golden/ownership-entity.json.golden b/tests/golden/ownership-entity.json.golden new file mode 100644 index 0000000..ece9e47 --- /dev/null +++ b/tests/golden/ownership-entity.json.golden @@ -0,0 +1,26 @@ +exit: 0 +--- stdout +{ + "entity": { + "id": 17, + "canonical_name": "ANTHONI SALIM", + "entity_type": null, + "country": null + }, + "ticker_count": 1, + "holdings": [ + { + "ticker": { + "id": 4, + "code": "BBCA", + "name": "BANK CENTRAL ASIA Tbk", + "sector": null + }, + "source": "ksei", + "shares": 1416306835, + "percentage_bps": 115, + "report_date": "2026-08-31" + } + ] +} +--- stderr diff --git a/tests/golden/ownership-entity.table.golden b/tests/golden/ownership-entity.table.golden new file mode 100644 index 0000000..3fb0cae --- /dev/null +++ b/tests/golden/ownership-entity.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +Entity: ANTHONI SALIM +┌────────┬────────┬───────────────┬───────┬─────────────┐ +│ TICKER ┆ SOURCE ┆ SHARES ┆ % ┆ REPORT_DATE │ +╞════════╪════════╪═══════════════╪═══════╪═════════════╡ +│ BBCA ┆ KSEI ┆ 1,416,306,835 ┆ 1.15% ┆ 2026-08-31 │ +└────────┴────────┴───────────────┴───────┴─────────────┘ +Total tickers: 1 +--- stderr diff --git a/tests/golden/ownership-flow.json.golden b/tests/golden/ownership-flow.json.golden new file mode 100644 index 0000000..3882ba7 --- /dev/null +++ b/tests/golden/ownership-flow.json.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +null +--- stderr diff --git a/tests/golden/ownership-flow.table.golden b/tests/golden/ownership-flow.table.golden new file mode 100644 index 0000000..cfa0580 --- /dev/null +++ b/tests/golden/ownership-flow.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +No institutional flow data available for BBCA. +--- stderr diff --git a/tests/golden/ownership-graph-dot.json.golden b/tests/golden/ownership-graph-dot.json.golden new file mode 100644 index 0000000..ffebccb --- /dev/null +++ b/tests/golden/ownership-graph-dot.json.golden @@ -0,0 +1,58 @@ +exit: 0 +--- stdout +{ + "nodes": [ + { + "id": "entity:14", + "label": "DWIMURIA INVESTAMA ANDALAN", + "node_type": "entity" + }, + { + "id": "entity:15", + "label": "CATURGUWIRATNA SUMAPALA", + "node_type": "entity" + }, + { + "id": "entity:16", + "label": "TRICIPTA MANDHALA GUMILANG", + "node_type": "entity" + }, + { + "id": "entity:17", + "label": "ANTHONI SALIM", + "node_type": "entity" + }, + { + "id": "ticker:BBCA", + "label": "BBCA", + "node_type": "ticker" + } + ], + "edges": [ + { + "from": "entity:17", + "to": "ticker:BBCA", + "percentage_bps": 115, + "source": "ksei" + }, + { + "from": "entity:15", + "to": "ticker:BBCA", + "percentage_bps": 102, + "source": "ksei" + }, + { + "from": "entity:14", + "to": "ticker:BBCA", + "percentage_bps": 5494, + "source": "ksei" + }, + { + "from": "entity:16", + "to": "ticker:BBCA", + "percentage_bps": 107, + "source": "ksei" + } + ] +} +--- stderr diff --git a/tests/golden/ownership-graph-dot.table.golden b/tests/golden/ownership-graph-dot.table.golden new file mode 100644 index 0000000..0e6fa36 --- /dev/null +++ b/tests/golden/ownership-graph-dot.table.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +digraph ownership { + rankdir=LR; + graph [fontname="Helvetica"]; + node [fontname="Helvetica"]; + edge [fontname="Helvetica"]; + + "entity:14" [label="DWIMURIA INVESTAMA ANDALAN", shape=ellipse]; + "entity:15" [label="CATURGUWIRATNA SUMAPALA", shape=ellipse]; + "entity:16" [label="TRICIPTA MANDHALA GUMILANG", shape=ellipse]; + "entity:17" [label="ANTHONI SALIM", shape=ellipse]; + "ticker:BBCA" [label="BBCA", shape=box]; + + "entity:17" -> "ticker:BBCA" [label="1.15% (ksei)"]; + "entity:15" -> "ticker:BBCA" [label="1.02% (ksei)"]; + "entity:14" -> "ticker:BBCA" [label="54.94% (ksei)"]; + "entity:16" -> "ticker:BBCA" [label="1.07% (ksei)"]; +} +--- stderr diff --git a/tests/golden/ownership-graph.json.golden b/tests/golden/ownership-graph.json.golden new file mode 100644 index 0000000..ffebccb --- /dev/null +++ b/tests/golden/ownership-graph.json.golden @@ -0,0 +1,58 @@ +exit: 0 +--- stdout +{ + "nodes": [ + { + "id": "entity:14", + "label": "DWIMURIA INVESTAMA ANDALAN", + "node_type": "entity" + }, + { + "id": "entity:15", + "label": "CATURGUWIRATNA SUMAPALA", + "node_type": "entity" + }, + { + "id": "entity:16", + "label": "TRICIPTA MANDHALA GUMILANG", + "node_type": "entity" + }, + { + "id": "entity:17", + "label": "ANTHONI SALIM", + "node_type": "entity" + }, + { + "id": "ticker:BBCA", + "label": "BBCA", + "node_type": "ticker" + } + ], + "edges": [ + { + "from": "entity:17", + "to": "ticker:BBCA", + "percentage_bps": 115, + "source": "ksei" + }, + { + "from": "entity:15", + "to": "ticker:BBCA", + "percentage_bps": 102, + "source": "ksei" + }, + { + "from": "entity:14", + "to": "ticker:BBCA", + "percentage_bps": 5494, + "source": "ksei" + }, + { + "from": "entity:16", + "to": "ticker:BBCA", + "percentage_bps": 107, + "source": "ksei" + } + ] +} +--- stderr diff --git a/tests/golden/ownership-graph.table.golden b/tests/golden/ownership-graph.table.golden new file mode 100644 index 0000000..46031aa --- /dev/null +++ b/tests/golden/ownership-graph.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +nodes: 5 edges: 4 + +BBCA [TICKER] +├─ DWIMURIA INVESTAMA ANDALAN (54.94%, ksei) +├─ ANTHONI SALIM (1.15%, ksei) +├─ TRICIPTA MANDHALA GUMILANG (1.07%, ksei) +└─ CATURGUWIRATNA SUMAPALA (1.02%, ksei) +--- stderr diff --git a/tests/golden/ownership-import-archive-txt.json.golden b/tests/golden/ownership-import-archive-txt.json.golden new file mode 100644 index 0000000..0717683 --- /dev/null +++ b/tests/golden/ownership-import-archive-txt.json.golden @@ -0,0 +1,11 @@ +exit: 0 +--- stdout +{ + "action": "imported", + "inserted_rows": 18, + "ticker_count": 1, + "as_of_date": "2026-02-27", + "sha256": "7b8da1d820f67062410c48988bb6c70fec2088c24961d481d1c0fce8b9372547", + "source_url": null +} +--- stderr diff --git a/tests/golden/ownership-import-archive-txt.table.golden b/tests/golden/ownership-import-archive-txt.table.golden new file mode 100644 index 0000000..8b0dfc1 --- /dev/null +++ b/tests/golden/ownership-import-archive-txt.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +Imported 18 rows for 1 tickers (as of 2026-02-27). +--- stderr diff --git a/tests/golden/ownership-import-xlsx.json.golden b/tests/golden/ownership-import-xlsx.json.golden new file mode 100644 index 0000000..30fb0d3 --- /dev/null +++ b/tests/golden/ownership-import-xlsx.json.golden @@ -0,0 +1,11 @@ +exit: 0 +--- stdout +{ + "action": "imported", + "inserted_rows": 17, + "ticker_count": 4, + "as_of_date": "2026-08-31", + "sha256": "7c1a4c209b8ce5bde9112436a0c64d66b042915995b584d2bbf7ca7b15ce81e3", + "source_url": null +} +--- stderr diff --git a/tests/golden/ownership-import-xlsx.table.golden b/tests/golden/ownership-import-xlsx.table.golden new file mode 100644 index 0000000..6eda918 --- /dev/null +++ b/tests/golden/ownership-import-xlsx.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +Imported 17 rows for 4 tickers (as of 2026-08-31). +--- stderr diff --git a/tests/golden/ownership-releases-empty.json.golden b/tests/golden/ownership-releases-empty.json.golden new file mode 100644 index 0000000..d845e3c --- /dev/null +++ b/tests/golden/ownership-releases-empty.json.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +[] +--- stderr diff --git a/tests/golden/ownership-releases-empty.table.golden b/tests/golden/ownership-releases-empty.table.golden new file mode 100644 index 0000000..a069376 --- /dev/null +++ b/tests/golden/ownership-releases-empty.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +No ownership releases imported yet. +--- stderr diff --git a/tests/golden/ownership-releases.json.golden b/tests/golden/ownership-releases.json.golden new file mode 100644 index 0000000..a555050 --- /dev/null +++ b/tests/golden/ownership-releases.json.golden @@ -0,0 +1,29 @@ +exit: 0 +--- stdout +[ + { + "id": 1, + "source_url": null, + "sha256": "7c1a4c209b8ce5bde9112436a0c64d66b042915995b584d2bbf7ca7b15ce81e3", + "as_of_date": "2026-08-31", + "row_count": 17, + "imported_at": 0 + }, + { + "id": 3, + "source_url": null, + "sha256": "7b8da1d820f67062410c48988bb6c70fec2088c24961d481d1c0fce8b9372547", + "as_of_date": "2026-02-27", + "row_count": 18, + "imported_at": 0 + }, + { + "id": 2, + "source_url": null, + "sha256": "efb3d8fb19c9e0d803dc1b1734c8389356605d0f78d73db6729fb76d3d75753d", + "as_of_date": "2026-01-30", + "row_count": 18, + "imported_at": 0 + } +] +--- stderr diff --git a/tests/golden/ownership-releases.table.golden b/tests/golden/ownership-releases.table.golden new file mode 100644 index 0000000..ffebd3c --- /dev/null +++ b/tests/golden/ownership-releases.table.golden @@ -0,0 +1,12 @@ +exit: 0 +--- stdout +┌────────────┬──────┬──────────────────────────────────────────────────────────────────┬────────┬─────────────┐ +│ AS_OF ┆ ROWS ┆ SHA256 ┆ SOURCE ┆ IMPORTED_AT │ +╞════════════╪══════╪══════════════════════════════════════════════════════════════════╪════════╪═════════════╡ +│ 2026-08-31 ┆ 17 ┆ 7c1a4c209b8ce5bde9112436a0c64d66b042915995b584d2bbf7ca7b15ce81e3 ┆ - ┆ 0 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2026-02-27 ┆ 18 ┆ 7b8da1d820f67062410c48988bb6c70fec2088c24961d481d1c0fce8b9372547 ┆ - ┆ 0 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2026-01-30 ┆ 18 ┆ efb3d8fb19c9e0d803dc1b1734c8389356605d0f78d73db6729fb76d3d75753d ┆ - ┆ 0 │ +└────────────┴──────┴──────────────────────────────────────────────────────────────────┴────────┴─────────────┘ +--- stderr diff --git a/tests/golden/ownership-resolve-map.json.golden b/tests/golden/ownership-resolve-map.json.golden new file mode 100644 index 0000000..baf20c6 --- /dev/null +++ b/tests/golden/ownership-resolve-map.json.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +{ + "action": "mapped", + "alias": "SALIM ANTHONI", + "entity": "ANTHONI SALIM" +} +--- stderr diff --git a/tests/golden/ownership-resolve-map.table.golden b/tests/golden/ownership-resolve-map.table.golden new file mode 100644 index 0000000..baa777c --- /dev/null +++ b/tests/golden/ownership-resolve-map.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +Mapped alias 'SALIM ANTHONI' -> 'ANTHONI SALIM'. +--- stderr diff --git a/tests/golden/ownership-resolve-merge.json.golden b/tests/golden/ownership-resolve-merge.json.golden new file mode 100644 index 0000000..6ea677b --- /dev/null +++ b/tests/golden/ownership-resolve-merge.json.golden @@ -0,0 +1,11 @@ +exit: 0 +--- stdout +{ + "action": "merged", + "alias_updates": 1, + "bing_holdings_updates": 0, + "keep": 1, + "ksei_holdings_updates": 1, + "merge": 2 +} +--- stderr diff --git a/tests/golden/ownership-resolve-merge.table.golden b/tests/golden/ownership-resolve-merge.table.golden new file mode 100644 index 0000000..8bf7715 --- /dev/null +++ b/tests/golden/ownership-resolve-merge.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +Merged entity 2 into 1 (aliases: 1, ksei_holdings: 1, bing_holdings: 0). +--- stderr diff --git a/tests/golden/ownership-resolve-unresolved.json.golden b/tests/golden/ownership-resolve-unresolved.json.golden new file mode 100644 index 0000000..d845e3c --- /dev/null +++ b/tests/golden/ownership-resolve-unresolved.json.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +[] +--- stderr diff --git a/tests/golden/ownership-resolve-unresolved.table.golden b/tests/golden/ownership-resolve-unresolved.table.golden new file mode 100644 index 0000000..186a221 --- /dev/null +++ b/tests/golden/ownership-resolve-unresolved.table.golden @@ -0,0 +1,4 @@ +exit: 0 +--- stdout +No unresolved or low-confidence aliases found. +--- stderr diff --git a/tests/golden/ownership-search.json.golden b/tests/golden/ownership-search.json.golden new file mode 100644 index 0000000..81ef014 --- /dev/null +++ b/tests/golden/ownership-search.json.golden @@ -0,0 +1,11 @@ +exit: 0 +--- stdout +[ + { + "id": 17, + "canonical_name": "ANTHONI SALIM", + "entity_type": null, + "country": null + } +] +--- stderr diff --git a/tests/golden/ownership-search.table.golden b/tests/golden/ownership-search.table.golden new file mode 100644 index 0000000..31b0f0e --- /dev/null +++ b/tests/golden/ownership-search.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +┌───────────────┬──────┬─────────┐ +│ ENTITY ┆ TYPE ┆ COUNTRY │ +╞═══════════════╪══════╪═════════╡ +│ ANTHONI SALIM ┆ - ┆ - │ +└───────────────┴──────┴─────────┘ +--- stderr diff --git a/tests/golden/ownership-ticker-empty.json.golden b/tests/golden/ownership-ticker-empty.json.golden new file mode 100644 index 0000000..f165894 --- /dev/null +++ b/tests/golden/ownership-ticker-empty.json.golden @@ -0,0 +1,8 @@ +exit: 1 +--- stdout +--- stderr +{ + "code": "SYMBOLNOTFOUND", + "error": true, + "message": "symbol not found: BBCA" +} diff --git a/tests/golden/ownership-ticker-empty.table.golden b/tests/golden/ownership-ticker-empty.table.golden new file mode 100644 index 0000000..b92ace9 --- /dev/null +++ b/tests/golden/ownership-ticker-empty.table.golden @@ -0,0 +1,4 @@ +exit: 1 +--- stdout +--- stderr +Error: symbol not found: BBCA diff --git a/tests/golden/ownership-ticker-ksei.json.golden b/tests/golden/ownership-ticker-ksei.json.golden new file mode 100644 index 0000000..de19ce7 --- /dev/null +++ b/tests/golden/ownership-ticker-ksei.json.golden @@ -0,0 +1,67 @@ +exit: 0 +--- stdout +{ + "ticker": { + "id": 4, + "code": "BBCA", + "name": "BANK CENTRAL ASIA Tbk", + "sector": null + }, + "ksei_as_of": "2026-08-31", + "bing_as_of": null, + "holders": [ + { + "rank": 1, + "source": "ksei", + "name": "DWIMURIA INVESTAMA ANDALAN", + "entity_id": 14, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 67729950000, + "percentage_bps": 5494, + "signal": null + }, + { + "rank": 2, + "source": "ksei", + "name": "ANTHONI SALIM", + "entity_id": 17, + "investor_type": "INDIVIDUAL", + "locality": "Local", + "shares": 1416306835, + "percentage_bps": 115, + "signal": null + }, + { + "rank": 3, + "source": "ksei", + "name": "TRICIPTA MANDHALA GUMILANG", + "entity_id": 16, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 1313250000, + "percentage_bps": 107, + "signal": null + }, + { + "rank": 4, + "source": "ksei", + "name": "CATURGUWIRATNA SUMAPALA", + "entity_id": 15, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 1261750000, + "percentage_bps": 102, + "signal": null + } + ], + "concentration": { + "top1_bps": 5494, + "top3_bps": 5716, + "hhi": 3021, + "free_float_bps": 4182, + "holder_count": 4 + }, + "flow": null +} +--- stderr diff --git a/tests/golden/ownership-ticker-ksei.table.golden b/tests/golden/ownership-ticker-ksei.table.golden new file mode 100644 index 0000000..e59444a --- /dev/null +++ b/tests/golden/ownership-ticker-ksei.table.golden @@ -0,0 +1,16 @@ +exit: 0 +--- stdout +Ownership: BBCA KSEI=2026-08-31 Bing=- +┌──────┬────────┬────────────────────────────┬────────────┬─────┬────────────────┬────────┬────────┐ +│ RANK ┆ SOURCE ┆ INVESTOR ┆ TYPE ┆ L/F ┆ SHARES ┆ % ┆ SIGNAL │ +╞══════╪════════╪════════════════════════════╪════════════╪═════╪════════════════╪════════╪════════╡ +│ 1 ┆ KSEI ┆ DWIMURIA INVESTAMA ANDALAN ┆ CORPORATE ┆ L ┆ 67,729,950,000 ┆ 54.94% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 2 ┆ KSEI ┆ ANTHONI SALIM ┆ INDIVIDUAL ┆ L ┆ 1,416,306,835 ┆ 1.15% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 3 ┆ KSEI ┆ TRICIPTA MANDHALA GUMILANG ┆ CORPORATE ┆ L ┆ 1,313,250,000 ┆ 1.07% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 4 ┆ KSEI ┆ CATURGUWIRATNA SUMAPALA ┆ CORPORATE ┆ L ┆ 1,261,750,000 ┆ 1.02% ┆ - │ +└──────┴────────┴────────────────────────────┴────────────┴─────┴────────────────┴────────┴────────┘ +Concentration: top1=54.94% top3=57.16% hhi=3021 free_float=41.82% holders=4 +--- stderr diff --git a/tests/golden/ownership-ticker.json.golden b/tests/golden/ownership-ticker.json.golden new file mode 100644 index 0000000..de19ce7 --- /dev/null +++ b/tests/golden/ownership-ticker.json.golden @@ -0,0 +1,67 @@ +exit: 0 +--- stdout +{ + "ticker": { + "id": 4, + "code": "BBCA", + "name": "BANK CENTRAL ASIA Tbk", + "sector": null + }, + "ksei_as_of": "2026-08-31", + "bing_as_of": null, + "holders": [ + { + "rank": 1, + "source": "ksei", + "name": "DWIMURIA INVESTAMA ANDALAN", + "entity_id": 14, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 67729950000, + "percentage_bps": 5494, + "signal": null + }, + { + "rank": 2, + "source": "ksei", + "name": "ANTHONI SALIM", + "entity_id": 17, + "investor_type": "INDIVIDUAL", + "locality": "Local", + "shares": 1416306835, + "percentage_bps": 115, + "signal": null + }, + { + "rank": 3, + "source": "ksei", + "name": "TRICIPTA MANDHALA GUMILANG", + "entity_id": 16, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 1313250000, + "percentage_bps": 107, + "signal": null + }, + { + "rank": 4, + "source": "ksei", + "name": "CATURGUWIRATNA SUMAPALA", + "entity_id": 15, + "investor_type": "CORPORATE", + "locality": "Local", + "shares": 1261750000, + "percentage_bps": 102, + "signal": null + } + ], + "concentration": { + "top1_bps": 5494, + "top3_bps": 5716, + "hhi": 3021, + "free_float_bps": 4182, + "holder_count": 4 + }, + "flow": null +} +--- stderr diff --git a/tests/golden/ownership-ticker.table.golden b/tests/golden/ownership-ticker.table.golden new file mode 100644 index 0000000..e59444a --- /dev/null +++ b/tests/golden/ownership-ticker.table.golden @@ -0,0 +1,16 @@ +exit: 0 +--- stdout +Ownership: BBCA KSEI=2026-08-31 Bing=- +┌──────┬────────┬────────────────────────────┬────────────┬─────┬────────────────┬────────┬────────┐ +│ RANK ┆ SOURCE ┆ INVESTOR ┆ TYPE ┆ L/F ┆ SHARES ┆ % ┆ SIGNAL │ +╞══════╪════════╪════════════════════════════╪════════════╪═════╪════════════════╪════════╪════════╡ +│ 1 ┆ KSEI ┆ DWIMURIA INVESTAMA ANDALAN ┆ CORPORATE ┆ L ┆ 67,729,950,000 ┆ 54.94% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 2 ┆ KSEI ┆ ANTHONI SALIM ┆ INDIVIDUAL ┆ L ┆ 1,416,306,835 ┆ 1.15% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 3 ┆ KSEI ┆ TRICIPTA MANDHALA GUMILANG ┆ CORPORATE ┆ L ┆ 1,313,250,000 ┆ 1.07% ┆ - │ +├╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 4 ┆ KSEI ┆ CATURGUWIRATNA SUMAPALA ┆ CORPORATE ┆ L ┆ 1,261,750,000 ┆ 1.02% ┆ - │ +└──────┴────────┴────────────────────────────┴────────────┴─────┴────────────────┴────────┴────────┘ +Concentration: top1=54.94% top3=57.16% hhi=3021 free_float=41.82% holders=4 +--- stderr diff --git a/tests/golden/stocks-compare.json.golden b/tests/golden/stocks-compare.json.golden new file mode 100644 index 0000000..c5cf145 --- /dev/null +++ b/tests/golden/stocks-compare.json.golden @@ -0,0 +1,81 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "growth": { + "revenue_growth": 0.081, + "earnings_growth": 0.121, + "revenue_growth_pct": 8.1, + "earnings_growth_pct": 12.1, + "revenue_signal": "slow", + "earnings_signal": "moderate", + "overall_signal": "mixed" + }, + "valuation": { + "pe_trailing": 25.4, + "pe_forward": null, + "pb": 4.6, + "roe": 0.198, + "roe_pct": 19.8, + "net_margin": 0.324, + "net_margin_pct": 32.4, + "ev_ebitda": null, + "pe_signal": "premium", + "pb_signal": "expensive", + "roe_signal": "strong", + "margin_signal": "excellent", + "ev_ebitda_signal": "no data", + "overall_signal": "expensive" + }, + "risk": { + "debt_to_equity": 0.42, + "current_ratio": 1.18, + "roa": 0.031, + "roa_pct": 3.1, + "de_signal": "conservative", + "current_ratio_signal": "tight", + "overall_signal": "moderate risk" + }, + "overall_signal": "mixed" + }, + { + "symbol": "BBRI.JK", + "growth": { + "revenue_growth": 0.081, + "earnings_growth": 0.121, + "revenue_growth_pct": 8.1, + "earnings_growth_pct": 12.1, + "revenue_signal": "slow", + "earnings_signal": "moderate", + "overall_signal": "mixed" + }, + "valuation": { + "pe_trailing": 25.4, + "pe_forward": null, + "pb": 4.6, + "roe": 0.198, + "roe_pct": 19.8, + "net_margin": 0.324, + "net_margin_pct": 32.4, + "ev_ebitda": null, + "pe_signal": "premium", + "pb_signal": "expensive", + "roe_signal": "strong", + "margin_signal": "excellent", + "ev_ebitda_signal": "no data", + "overall_signal": "expensive" + }, + "risk": { + "debt_to_equity": 0.42, + "current_ratio": 1.18, + "roa": 0.031, + "roa_pct": 3.1, + "de_signal": "conservative", + "current_ratio_signal": "tight", + "overall_signal": "moderate risk" + }, + "overall_signal": "mixed" + } +] +--- stderr diff --git a/tests/golden/stocks-compare.table.golden b/tests/golden/stocks-compare.table.golden new file mode 100644 index 0000000..c913fbc --- /dev/null +++ b/tests/golden/stocks-compare.table.golden @@ -0,0 +1,23 @@ +exit: 0 +--- stdout +Fundamental Comparison +┌────────────────┬─────────────────────────┬─────────────────────────┐ +│ METRIC ┆ BBCA.JK ┆ BBRI.JK │ +╞════════════════╪═════════════════════════╪═════════════════════════╡ +│ Symbol ┆ BBCA.JK ┆ BBRI.JK │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ mixed ┆ mixed │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Growth ┆ mixed ┆ mixed │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Valuation ┆ expensive ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Risk ┆ moderate risk ┆ moderate risk │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ P/E ┆ 25.40 ┆ 25.40 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROE ┆ +19.80% ┆ +19.80% │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Revenue Growth ┆ +8.10% ┆ +8.10% │ +└────────────────┴─────────────────────────┴─────────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-earnings-forecast-annual.json.golden b/tests/golden/stocks-earnings-forecast-annual.json.golden new file mode 100644 index 0000000..ca4b219 --- /dev/null +++ b/tests/golden/stocks-earnings-forecast-annual.json.golden @@ -0,0 +1,22 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "eps_last_year": 1200.0, + "revenue_last_year": 100000000000.0, + "forecast": [ + { + "eps_actual": null, + "eps_forecast": 1300.0, + "eps_surprise": null, + "eps_surprise_pct": null, + "revenue_actual": null, + "revenue_forecast": 110000000000.0, + "revenue_surprise": null, + "earning_release_date": "2026-03-15", + "period_type": "2026" + } + ], + "history": [] +} +--- stderr diff --git a/tests/golden/stocks-earnings-forecast-annual.table.golden b/tests/golden/stocks-earnings-forecast-annual.table.golden new file mode 100644 index 0000000..7462fbf --- /dev/null +++ b/tests/golden/stocks-earnings-forecast-annual.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout + +── Earnings Forecast ── +┌────────┬─────────┬─────────────────┬────────────┐ +│ PERIOD ┆ EPS FC ┆ REV FC ┆ DATE │ +╞════════╪═════════╪═════════════════╪════════════╡ +│ FY2026 ┆ 1300.00 ┆ 110,000,000,000 ┆ 2026-03-15 │ +└────────┴─────────┴─────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-earnings-history-quarterly.json.golden b/tests/golden/stocks-earnings-history-quarterly.json.golden new file mode 100644 index 0000000..2c8eef8 --- /dev/null +++ b/tests/golden/stocks-earnings-history-quarterly.json.golden @@ -0,0 +1,22 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "eps_last_year": 1200.0, + "revenue_last_year": 100000000000.0, + "forecast": [], + "history": [ + { + "eps_actual": 320.0, + "eps_forecast": null, + "eps_surprise": 5.0, + "eps_surprise_pct": 1.5, + "revenue_actual": 27000000000.0, + "revenue_forecast": null, + "revenue_surprise": 200000000.0, + "earning_release_date": "2025-11-20", + "period_type": "Q42025" + } + ] +} +--- stderr diff --git a/tests/golden/stocks-earnings-history-quarterly.table.golden b/tests/golden/stocks-earnings-history-quarterly.table.golden new file mode 100644 index 0000000..733bfb3 --- /dev/null +++ b/tests/golden/stocks-earnings-history-quarterly.table.golden @@ -0,0 +1,9 @@ +exit: 0 +--- stdout +── Earnings History ── +┌─────────┬─────────┬────────┬──────────┬───────────┬────────────────┬────────────┐ +│ PERIOD ┆ EPS ACT ┆ EPS FC ┆ SURPRISE ┆ SURPRISE% ┆ REV ACT ┆ DATE │ +╞═════════╪═════════╪════════╪══════════╪═══════════╪════════════════╪════════════╡ +│ Q4 2025 ┆ 320.00 ┆ - ┆ 5.00 ┆ 1.50 ┆ 27,000,000,000 ┆ 2025-11-20 │ +└─────────┴─────────┴────────┴──────────┴───────────┴────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-earnings.json.golden b/tests/golden/stocks-earnings.json.golden new file mode 100644 index 0000000..f9fcc31 --- /dev/null +++ b/tests/golden/stocks-earnings.json.golden @@ -0,0 +1,56 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "eps_last_year": 1200.0, + "revenue_last_year": 100000000000.0, + "forecast": [ + { + "eps_actual": null, + "eps_forecast": 1300.0, + "eps_surprise": null, + "eps_surprise_pct": null, + "revenue_actual": null, + "revenue_forecast": 110000000000.0, + "revenue_surprise": null, + "earning_release_date": "2026-03-15", + "period_type": "2026" + }, + { + "eps_actual": null, + "eps_forecast": 330.0, + "eps_surprise": null, + "eps_surprise_pct": null, + "revenue_actual": null, + "revenue_forecast": 28000000000.0, + "revenue_surprise": null, + "earning_release_date": "2026-04-20", + "period_type": "Q12026" + } + ], + "history": [ + { + "eps_actual": 1250.0, + "eps_forecast": null, + "eps_surprise": 20.0, + "eps_surprise_pct": 1.6, + "revenue_actual": 105000000000.0, + "revenue_forecast": null, + "revenue_surprise": 500000000.0, + "earning_release_date": "2025-03-15", + "period_type": "2025" + }, + { + "eps_actual": 320.0, + "eps_forecast": null, + "eps_surprise": 5.0, + "eps_surprise_pct": 1.5, + "revenue_actual": 27000000000.0, + "revenue_forecast": null, + "revenue_surprise": 200000000.0, + "earning_release_date": "2025-11-20", + "period_type": "Q42025" + } + ] +} +--- stderr diff --git a/tests/golden/stocks-earnings.table.golden b/tests/golden/stocks-earnings.table.golden new file mode 100644 index 0000000..ef37a38 --- /dev/null +++ b/tests/golden/stocks-earnings.table.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +── Earnings History ── +┌─────────┬─────────┬────────┬──────────┬───────────┬─────────────────┬────────────┐ +│ PERIOD ┆ EPS ACT ┆ EPS FC ┆ SURPRISE ┆ SURPRISE% ┆ REV ACT ┆ DATE │ +╞═════════╪═════════╪════════╪══════════╪═══════════╪═════════════════╪════════════╡ +│ FY2025 ┆ 1250.00 ┆ - ┆ 20.00 ┆ 1.60 ┆ 105,000,000,000 ┆ 2025-03-15 │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Q4 2025 ┆ 320.00 ┆ - ┆ 5.00 ┆ 1.50 ┆ 27,000,000,000 ┆ 2025-11-20 │ +└─────────┴─────────┴────────┴──────────┴───────────┴─────────────────┴────────────┘ + +── Earnings Forecast ── +┌─────────┬─────────┬─────────────────┬────────────┐ +│ PERIOD ┆ EPS FC ┆ REV FC ┆ DATE │ +╞═════════╪═════════╪═════════════════╪════════════╡ +│ FY2026 ┆ 1300.00 ┆ 110,000,000,000 ┆ 2026-03-15 │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Q1 2026 ┆ 330.00 ┆ 28,000,000,000 ┆ 2026-04-20 │ +└─────────┴─────────┴─────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-financials-income.json.golden b/tests/golden/stocks-financials-income.json.golden new file mode 100644 index 0000000..83cd079 --- /dev/null +++ b/tests/golden/stocks-financials-income.json.golden @@ -0,0 +1,21 @@ +exit: 0 +--- stdout +{ + "instrument": { + "id": "bn91jc", + "symbol": "BBCA.JK", + "name": "Bank Central Asia Tbk PT" + }, + "balance_sheet": null, + "cash_flow": null, + "income_statement": { + "values": { + "netIncome": 400000000.0, + "totalRevenue": 1000000000.0 + }, + "currency": "IDR", + "report_date": "2025-12-31", + "end_date": "2025-12-31" + } +} +--- stderr diff --git a/tests/golden/stocks-financials-income.table.golden b/tests/golden/stocks-financials-income.table.golden new file mode 100644 index 0000000..ca58877 --- /dev/null +++ b/tests/golden/stocks-financials-income.table.golden @@ -0,0 +1,12 @@ +exit: 0 +--- stdout + +── Income Statement (2025-12-31) ── +┌───────────────┬───────────────┐ +│ LINE ITEM ┆ VALUE (IDR) │ +╞═══════════════╪═══════════════╡ +│ Net Income ┆ 400,000,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Total Revenue ┆ 1,000,000,000 │ +└───────────────┴───────────────┘ +--- stderr diff --git a/tests/golden/stocks-financials.json.golden b/tests/golden/stocks-financials.json.golden new file mode 100644 index 0000000..34bab31 --- /dev/null +++ b/tests/golden/stocks-financials.json.golden @@ -0,0 +1,36 @@ +exit: 0 +--- stdout +{ + "instrument": { + "id": "bn91jc", + "symbol": "BBCA.JK", + "name": "Bank Central Asia Tbk PT" + }, + "balance_sheet": { + "values": { + "cashAndShortTermInvestments": 500000000.0, + "totalEquity": 900000000.0 + }, + "currency": "IDR", + "report_date": "2025-12-31", + "end_date": "2025-12-31" + }, + "cash_flow": { + "values": { + "operatingCashFlow": 600000000.0 + }, + "currency": "IDR", + "report_date": "2025-12-31", + "end_date": "2025-12-31" + }, + "income_statement": { + "values": { + "netIncome": 400000000.0, + "totalRevenue": 1000000000.0 + }, + "currency": "IDR", + "report_date": "2025-12-31", + "end_date": "2025-12-31" + } +} +--- stderr diff --git a/tests/golden/stocks-financials.table.golden b/tests/golden/stocks-financials.table.golden new file mode 100644 index 0000000..b938cf2 --- /dev/null +++ b/tests/golden/stocks-financials.table.golden @@ -0,0 +1,28 @@ +exit: 0 +--- stdout + +── Income Statement (2025-12-31) ── +┌───────────────┬───────────────┐ +│ LINE ITEM ┆ VALUE (IDR) │ +╞═══════════════╪═══════════════╡ +│ Net Income ┆ 400,000,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Total Revenue ┆ 1,000,000,000 │ +└───────────────┴───────────────┘ + +── Balance Sheet (2025-12-31) ── +┌─────────────────────────────────┬─────────────┐ +│ LINE ITEM ┆ VALUE (IDR) │ +╞═════════════════════════════════╪═════════════╡ +│ Cash And Short Term Investments ┆ 500,000,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Total Equity ┆ 900,000,000 │ +└─────────────────────────────────┴─────────────┘ + +── Cash Flow (2025-12-31) ── +┌─────────────────────┬─────────────┐ +│ LINE ITEM ┆ VALUE (IDR) │ +╞═════════════════════╪═════════════╡ +│ Operating Cash Flow ┆ 600,000,000 │ +└─────────────────────┴─────────────┘ +--- stderr diff --git a/tests/golden/stocks-fundamental.json.golden b/tests/golden/stocks-fundamental.json.golden new file mode 100644 index 0000000..a24505b --- /dev/null +++ b/tests/golden/stocks-fundamental.json.golden @@ -0,0 +1,41 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "growth": { + "revenue_growth": 0.081, + "earnings_growth": 0.121, + "revenue_growth_pct": 8.1, + "earnings_growth_pct": 12.1, + "revenue_signal": "slow", + "earnings_signal": "moderate", + "overall_signal": "mixed" + }, + "valuation": { + "pe_trailing": 25.4, + "pe_forward": null, + "pb": 4.6, + "roe": 0.198, + "roe_pct": 19.8, + "net_margin": 0.324, + "net_margin_pct": 32.4, + "ev_ebitda": null, + "pe_signal": "premium", + "pb_signal": "expensive", + "roe_signal": "strong", + "margin_signal": "excellent", + "ev_ebitda_signal": "no data", + "overall_signal": "expensive" + }, + "risk": { + "debt_to_equity": 0.42, + "current_ratio": 1.18, + "roa": 0.031, + "roa_pct": 3.1, + "de_signal": "conservative", + "current_ratio_signal": "tight", + "overall_signal": "moderate risk" + }, + "overall_signal": "mixed" +} +--- stderr diff --git a/tests/golden/stocks-fundamental.table.golden b/tests/golden/stocks-fundamental.table.golden new file mode 100644 index 0000000..d697200 --- /dev/null +++ b/tests/golden/stocks-fundamental.table.golden @@ -0,0 +1,49 @@ +exit: 0 +--- stdout +Fundamental Analysis for BBCA.JK + +Growth Analysis for BBCA.JK +┌─────────────────┬─────────┬────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═════════════════╪═════════╪════════════════════╡ +│ Revenue Growth ┆ +8.10% ┆ slow │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Earnings Growth ┆ +12.10% ┆ moderate │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ mixed │ +└─────────────────┴─────────┴────────────────────┘ + +Valuation Analysis for BBCA.JK +┌────────────────┬─────────┬─────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞════════════════╪═════════╪═════════════════════╡ +│ P/E (Trailing) ┆ 25.40 ┆ premium │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ P/E (Forward) ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Price/Book ┆ 4.60 ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROE ┆ +19.80% ┆ strong │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Net Margin ┆ +32.40% ┆ excellent │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ EV/EBITDA ┆ - ┆ no data │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ expensive │ +└────────────────┴─────────┴─────────────────────┘ + +Risk Analysis for BBCA.JK +┌───────────────┬────────┬─────────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═══════════════╪════════╪═════════════════════════╡ +│ Debt/Equity ┆ 0.42 ┆ conservative │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Current Ratio ┆ 1.18 ┆ tight │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROA ┆ +3.10% ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ moderate risk │ +└───────────────┴────────┴─────────────────────────┘ + +Overall Signal: mixed +--- stderr diff --git a/tests/golden/stocks-growth.json.golden b/tests/golden/stocks-growth.json.golden new file mode 100644 index 0000000..d2327bf --- /dev/null +++ b/tests/golden/stocks-growth.json.golden @@ -0,0 +1,12 @@ +exit: 0 +--- stdout +{ + "revenue_growth": 0.081, + "earnings_growth": 0.121, + "revenue_growth_pct": 8.1, + "earnings_growth_pct": 12.1, + "revenue_signal": "slow", + "earnings_signal": "moderate", + "overall_signal": "mixed" +} +--- stderr diff --git a/tests/golden/stocks-growth.table.golden b/tests/golden/stocks-growth.table.golden new file mode 100644 index 0000000..a26e3a1 --- /dev/null +++ b/tests/golden/stocks-growth.table.golden @@ -0,0 +1,13 @@ +exit: 0 +--- stdout +Growth Analysis for BBCA.JK +┌─────────────────┬─────────┬────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═════════════════╪═════════╪════════════════════╡ +│ Revenue Growth ┆ +8.10% ┆ slow │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Earnings Growth ┆ +12.10% ┆ moderate │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ mixed │ +└─────────────────┴─────────┴────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-history-msn.json.golden b/tests/golden/stocks-history-msn.json.golden new file mode 100644 index 0000000..e6eb3e6 --- /dev/null +++ b/tests/golden/stocks-history-msn.json.golden @@ -0,0 +1,29 @@ +exit: 0 +--- stdout +[ + { + "date": "2026-01-14", + "open": 8000, + "high": 8000, + "low": 8000, + "close": 8000, + "volume": 0 + }, + { + "date": "2026-01-15", + "open": 8075, + "high": 8075, + "low": 8075, + "close": 8075, + "volume": 0 + }, + { + "date": "2026-01-16", + "open": 8025, + "high": 8025, + "low": 8025, + "close": 8025, + "volume": 0 + } +] +--- stderr diff --git a/tests/golden/stocks-history-msn.table.golden b/tests/golden/stocks-history-msn.table.golden new file mode 100644 index 0000000..b47c365 --- /dev/null +++ b/tests/golden/stocks-history-msn.table.golden @@ -0,0 +1,13 @@ +exit: 0 +--- stdout +History for BBCA.JK +┌────────────┬───────┬───────┬───────┬───────┬────────┐ +│ DATE ┆ OPEN ┆ HIGH ┆ LOW ┆ CLOSE ┆ VOLUME │ +╞════════════╪═══════╪═══════╪═══════╪═══════╪════════╡ +│ 2026-01-14 ┆ 8,000 ┆ 8,000 ┆ 8,000 ┆ 8,000 ┆ 0 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 2026-01-15 ┆ 8,075 ┆ 8,075 ┆ 8,075 ┆ 8,075 ┆ 0 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ 2026-01-16 ┆ 8,025 ┆ 8,025 ┆ 8,025 ┆ 8,025 ┆ 0 │ +└────────────┴───────┴───────┴───────┴───────┴────────┘ +--- stderr diff --git a/tests/golden/stocks-history.json.golden b/tests/golden/stocks-history.json.golden new file mode 100644 index 0000000..423e9c6 --- /dev/null +++ b/tests/golden/stocks-history.json.golden @@ -0,0 +1,29 @@ +exit: 0 +--- stdout +[ + { + "date": "2024-03-01", + "open": 9800, + "high": 9900, + "low": 9750, + "close": 9875, + "volume": 12300000 + }, + { + "date": "2024-03-02", + "open": 9850, + "high": 9920, + "low": 9800, + "close": 9880, + "volume": 11000000 + }, + { + "date": "2024-03-03", + "open": 9860, + "high": 9950, + "low": 9820, + "close": 9925, + "volume": 14000000 + } +] +--- stderr diff --git a/tests/golden/stocks-history.table.golden b/tests/golden/stocks-history.table.golden new file mode 100644 index 0000000..50e34a6 --- /dev/null +++ b/tests/golden/stocks-history.table.golden @@ -0,0 +1,14 @@ +exit: 0 +--- stdout +History for BBCA.JK +┌────────────┬───────┬───────┬───────┬───────┬────────────┐ +│ DATE ┆ OPEN ┆ HIGH ┆ LOW ┆ CLOSE ┆ VOLUME │ +╞════════════╪═══════╪═══════╪═══════╪═══════╪════════════╡ +│ 2024-03-01 ┆ 9,800 ┆ 9,900 ┆ 9,750 ┆ 9,875 ┆ 12,300,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2024-03-02 ┆ 9,850 ┆ 9,920 ┆ 9,800 ┆ 9,880 ┆ 11,000,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2024-03-03 ┆ 9,860 ┆ 9,950 ┆ 9,820 ┆ 9,925 ┆ 14,000,000 │ +└────────────┴───────┴───────┴───────┴───────┴────────────┘ +--- stderr +info: history provider fallback active (msn -> yahoo) diff --git a/tests/golden/stocks-insights.json.golden b/tests/golden/stocks-insights.json.golden new file mode 100644 index 0000000..0050c83 --- /dev/null +++ b/tests/golden/stocks-insights.json.golden @@ -0,0 +1,16 @@ +exit: 0 +--- stdout +{ + "id": "bn91jc", + "symbol": "BBCA.JK", + "summary": "Mixed analyst signals for Bank Central Asia Tbk PT: 1 positive, 1 negative, 1 neutral.", + "highlights": [ + "P/E ratio: Earnings data unavailable, can't compare P/E ratio with peers", + "Analyst price target: Analysts forecast more than 20% upside for 12-month price target" + ], + "risks": [ + "Quarterly Revenue YoY Growth: Revenue grew worse than peers" + ], + "last_updated": "2026-03-26T04:14:57.9197955Z" +} +--- stderr diff --git a/tests/golden/stocks-insights.table.golden b/tests/golden/stocks-insights.table.golden new file mode 100644 index 0000000..f2ab652 --- /dev/null +++ b/tests/golden/stocks-insights.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +Mixed analyst signals for Bank Central Asia Tbk PT: 1 positive, 1 negative, 1 neutral. +Last updated: 2026-03-26T04:14:57.9197955Z +Highlights: +- P/E ratio: Earnings data unavailable, can't compare P/E ratio with peers +- Analyst price target: Analysts forecast more than 20% upside for 12-month price target +Risks: +- Quarterly Revenue YoY Growth: Revenue grew worse than peers +--- stderr diff --git a/tests/golden/stocks-news.json.golden b/tests/golden/stocks-news.json.golden new file mode 100644 index 0000000..71cf540 --- /dev/null +++ b/tests/golden/stocks-news.json.golden @@ -0,0 +1,15 @@ +exit: 0 +--- stdout +[ + { + "id": "news-1", + "symbol": "BBCA.JK", + "title": "BCA reports steady growth", + "url": "https://example.com/news/bca-growth", + "description": "BCA delivered steady quarterly growth across lending and deposits.", + "provider": "Contoso News", + "published_at": "2026-03-20T10:00:00Z", + "read_time_min": 3 + } +] +--- stderr diff --git a/tests/golden/stocks-news.table.golden b/tests/golden/stocks-news.table.golden new file mode 100644 index 0000000..f0bdf1e --- /dev/null +++ b/tests/golden/stocks-news.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +┌───────────────────────────┬──────────────┬──────────────────────┬─────────────────────────────────────┐ +│ TITLE ┆ PROVIDER ┆ DATE ┆ URL │ +╞═══════════════════════════╪══════════════╪══════════════════════╪═════════════════════════════════════╡ +│ BCA reports steady growth ┆ Contoso News ┆ 2026-03-20T10:00:00Z ┆ https://example.com/news/bca-growth │ +└───────────────────────────┴──────────────┴──────────────────────┴─────────────────────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-profile.json.golden b/tests/golden/stocks-profile.json.golden new file mode 100644 index 0000000..afafd3f --- /dev/null +++ b/tests/golden/stocks-profile.json.golden @@ -0,0 +1,27 @@ +exit: 0 +--- stdout +{ + "id": "feee16af-c94c-465d-a59d-a9727c22bd0e", + "symbol": "BBCA", + "short_name": "Bank Cntrl Asia", + "long_name": "PT Bank Central Asia Tbk", + "description": "PT Bank Central Asia Tbk (the Bank) is an Indonesia-based commercial bank.", + "sector": "Financials", + "industry": "Banking Services", + "website": "https://www.bca.co.id/", + "employees": 27937, + "address": "Menara BCA, Grand, Jl. MH Thamrin No. 1", + "city": "JAKARTA PUSAT", + "country": "Indonesia", + "phone": "+62 21 23588000", + "officers": [ + { + "name": "Jahja Setiaatmadja", + "title": "President Director", + "age": 69, + "year_born": 1955, + "total_pay": 12000000000 + } + ] +} +--- stderr diff --git a/tests/golden/stocks-profile.table.golden b/tests/golden/stocks-profile.table.golden new file mode 100644 index 0000000..3c5c955 --- /dev/null +++ b/tests/golden/stocks-profile.table.golden @@ -0,0 +1,28 @@ +exit: 0 +--- stdout +┌─────────────┬────────────────────────────────────────────────────────────────────────────┐ +│ FIELD ┆ VALUE │ +╞═════════════╪════════════════════════════════════════════════════════════════════════════╡ +│ Symbol ┆ BBCA │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Name ┆ PT Bank Central Asia Tbk │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Sector ┆ Financials │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Industry ┆ Banking Services │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Website ┆ https://www.bca.co.id/ │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Country ┆ Indonesia │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ City ┆ JAKARTA PUSAT │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Phone ┆ +62 21 23588000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Employees ┆ 27937 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Description ┆ PT Bank Central Asia Tbk (the Bank) is an Indonesia-based commercial bank. │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Executives ┆ Jahja Setiaatmadja (President Director) │ +└─────────────┴────────────────────────────────────────────────────────────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-quote-multi.json.golden b/tests/golden/stocks-quote-multi.json.golden new file mode 100644 index 0000000..6095d47 --- /dev/null +++ b/tests/golden/stocks-quote-multi.json.golden @@ -0,0 +1,35 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10250, + "week52_low": 7800, + "week52_position": 0.8469387755102041, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + }, + { + "symbol": "BBRI.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10250, + "week52_low": 7800, + "week52_position": 0.8469387755102041, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-quote-multi.table.golden b/tests/golden/stocks-quote-multi.table.golden new file mode 100644 index 0000000..99eb4e0 --- /dev/null +++ b/tests/golden/stocks-quote-multi.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ BBRI.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-quote-offline-warm.json.golden b/tests/golden/stocks-quote-offline-warm.json.golden new file mode 100644 index 0000000..bfcbbaf --- /dev/null +++ b/tests/golden/stocks-quote-offline-warm.json.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10250, + "week52_low": 7800, + "week52_position": 0.8469387755102041, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-quote-offline-warm.table.golden b/tests/golden/stocks-quote-offline-warm.table.golden new file mode 100644 index 0000000..6102d71 --- /dev/null +++ b/tests/golden/stocks-quote-offline-warm.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-quote.json.golden b/tests/golden/stocks-quote.json.golden new file mode 100644 index 0000000..bfcbbaf --- /dev/null +++ b/tests/golden/stocks-quote.json.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10250, + "week52_low": 7800, + "week52_position": 0.8469387755102041, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-quote.table.golden b/tests/golden/stocks-quote.table.golden new file mode 100644 index 0000000..6102d71 --- /dev/null +++ b/tests/golden/stocks-quote.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-risk.json.golden b/tests/golden/stocks-risk.json.golden new file mode 100644 index 0000000..0cce202 --- /dev/null +++ b/tests/golden/stocks-risk.json.golden @@ -0,0 +1,12 @@ +exit: 0 +--- stdout +{ + "debt_to_equity": 0.42, + "current_ratio": 1.18, + "roa": 0.031, + "roa_pct": 3.1, + "de_signal": "conservative", + "current_ratio_signal": "tight", + "overall_signal": "moderate risk" +} +--- stderr diff --git a/tests/golden/stocks-risk.table.golden b/tests/golden/stocks-risk.table.golden new file mode 100644 index 0000000..9888a27 --- /dev/null +++ b/tests/golden/stocks-risk.table.golden @@ -0,0 +1,15 @@ +exit: 0 +--- stdout +Risk Analysis for BBCA.JK +┌───────────────┬────────┬─────────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═══════════════╪════════╪═════════════════════════╡ +│ Debt/Equity ┆ 0.42 ┆ conservative │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Current Ratio ┆ 1.18 ┆ tight │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROA ┆ +3.10% ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ moderate risk │ +└───────────────┴────────┴─────────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-screen-high-volume.json.golden b/tests/golden/stocks-screen-high-volume.json.golden new file mode 100644 index 0000000..a822087 --- /dev/null +++ b/tests/golden/stocks-screen-high-volume.json.golden @@ -0,0 +1,35 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBRI.JK", + "price": 5100, + "change": 210, + "change_pct": 4.3, + "volume": 22200000, + "market_cap": 780000000000000, + "week52_high": 5400, + "week52_low": 3900, + "week52_position": 0.8, + "range_signal": "upper", + "prev_close": 4890, + "avg_volume": 18000000, + "as_of": null + }, + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10500, + "week52_low": 8000, + "week52_position": 0.75, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-screen-high-volume.table.golden b/tests/golden/stocks-screen-high-volume.table.golden new file mode 100644 index 0000000..f9e4ee5 --- /dev/null +++ b/tests/golden/stocks-screen-high-volume.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBRI.JK ┆ 5,100 ┆ +210 ┆ +4.30% ┆ 22,200,000 ┆ 780,000,000,000,000 ┆ ████████░░ │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-screen-large-cap.json.golden b/tests/golden/stocks-screen-large-cap.json.golden new file mode 100644 index 0000000..94ac385 --- /dev/null +++ b/tests/golden/stocks-screen-large-cap.json.golden @@ -0,0 +1,35 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10500, + "week52_low": 8000, + "week52_position": 0.75, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + }, + { + "symbol": "BBRI.JK", + "price": 5100, + "change": 210, + "change_pct": 4.3, + "volume": 22200000, + "market_cap": 780000000000000, + "week52_high": 5400, + "week52_low": 3900, + "week52_position": 0.8, + "range_signal": "upper", + "prev_close": 4890, + "avg_volume": 18000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-screen-large-cap.table.golden b/tests/golden/stocks-screen-large-cap.table.golden new file mode 100644 index 0000000..5fe90da --- /dev/null +++ b/tests/golden/stocks-screen-large-cap.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ BBRI.JK ┆ 5,100 ┆ +210 ┆ +4.30% ┆ 22,200,000 ┆ 780,000,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-screen-worst.json.golden b/tests/golden/stocks-screen-worst.json.golden new file mode 100644 index 0000000..94ac385 --- /dev/null +++ b/tests/golden/stocks-screen-worst.json.golden @@ -0,0 +1,35 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10500, + "week52_low": 8000, + "week52_position": 0.75, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + }, + { + "symbol": "BBRI.JK", + "price": 5100, + "change": 210, + "change_pct": 4.3, + "volume": 22200000, + "market_cap": 780000000000000, + "week52_high": 5400, + "week52_low": 3900, + "week52_position": 0.8, + "range_signal": "upper", + "prev_close": 4890, + "avg_volume": 18000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-screen-worst.table.golden b/tests/golden/stocks-screen-worst.table.golden new file mode 100644 index 0000000..5fe90da --- /dev/null +++ b/tests/golden/stocks-screen-worst.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ BBRI.JK ┆ 5,100 ┆ +210 ┆ +4.30% ┆ 22,200,000 ┆ 780,000,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-screen.json.golden b/tests/golden/stocks-screen.json.golden new file mode 100644 index 0000000..a822087 --- /dev/null +++ b/tests/golden/stocks-screen.json.golden @@ -0,0 +1,35 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBRI.JK", + "price": 5100, + "change": 210, + "change_pct": 4.3, + "volume": 22200000, + "market_cap": 780000000000000, + "week52_high": 5400, + "week52_low": 3900, + "week52_position": 0.8, + "range_signal": "upper", + "prev_close": 4890, + "avg_volume": 18000000, + "as_of": null + }, + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.2, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10500, + "week52_low": 8000, + "week52_position": 0.75, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/stocks-screen.table.golden b/tests/golden/stocks-screen.table.golden new file mode 100644 index 0000000..f9e4ee5 --- /dev/null +++ b/tests/golden/stocks-screen.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBRI.JK ┆ 5,100 ┆ +210 ┆ +4.30% ┆ 22,200,000 ┆ 780,000,000,000,000 ┆ ████████░░ │ +├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr diff --git a/tests/golden/stocks-sentiment.json.golden b/tests/golden/stocks-sentiment.json.golden new file mode 100644 index 0000000..e4d6d4a --- /dev/null +++ b/tests/golden/stocks-sentiment.json.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "statistics": [ + { + "time_range": "1D", + "bullish": 10, + "bearish": 2, + "neutral": 3 + }, + { + "time_range": "1W", + "bullish": 25, + "bearish": 5, + "neutral": 7 + } + ] +} +--- stderr diff --git a/tests/golden/stocks-sentiment.table.golden b/tests/golden/stocks-sentiment.table.golden new file mode 100644 index 0000000..93c28e9 --- /dev/null +++ b/tests/golden/stocks-sentiment.table.golden @@ -0,0 +1,10 @@ +exit: 0 +--- stdout +┌───────┬─────────┬─────────┬─────────┐ +│ RANGE ┆ BULLISH ┆ BEARISH ┆ NEUTRAL │ +╞═══════╪═════════╪═════════╪═════════╡ +│ 1D ┆ 10 ┆ 2 ┆ 3 │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ 1W ┆ 25 ┆ 5 ┆ 7 │ +└───────┴─────────┴─────────┴─────────┘ +--- stderr diff --git a/tests/golden/stocks-technical.json.golden b/tests/golden/stocks-technical.json.golden new file mode 100644 index 0000000..3e587b3 --- /dev/null +++ b/tests/golden/stocks-technical.json.golden @@ -0,0 +1,28 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "as_of": "2024-03-03", + "current_price": 9925, + "sma20": null, + "sma50": null, + "sma200": null, + "rsi14": null, + "macd": { + "line": null, + "signal": null, + "histogram": null + }, + "volume": { + "current": 14000000, + "average20": null, + "ratio20": null + }, + "signals": { + "rsi": "Neutral", + "macd": "Neutral", + "trend": "Neutral", + "overall": "Neutral" + } +} +--- stderr diff --git a/tests/golden/stocks-technical.table.golden b/tests/golden/stocks-technical.table.golden new file mode 100644 index 0000000..d1099d0 --- /dev/null +++ b/tests/golden/stocks-technical.table.golden @@ -0,0 +1,26 @@ +exit: 0 +--- stdout +Technical Analysis for BBCA.JK (2024-03-03) +┌───────────────────┬─────────────────────────────────────────────────────┬───────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═══════════════════╪═════════════════════════════════════════════════════╪═══════════════════╡ +│ Current Price ┆ 9,925 ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ SMA 20 ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ SMA 50 ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ SMA 200 ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ RSI (14) ┆ - ┆ Neutral │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ MACD (12,26,9) ┆ -/-/- ┆ Neutral │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Trend ┆ Trend unavailable (need at least 200 daily candles) ┆ Neutral │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Volume Ratio (20) ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall Signal ┆ - ┆ NEUTRAL │ +└───────────────────┴─────────────────────────────────────────────────────┴───────────────────┘ +--- stderr +info: history provider fallback active (msn -> yahoo) diff --git a/tests/golden/stocks-valuation-nonfinite.json.golden b/tests/golden/stocks-valuation-nonfinite.json.golden new file mode 100644 index 0000000..ef8159f --- /dev/null +++ b/tests/golden/stocks-valuation-nonfinite.json.golden @@ -0,0 +1,19 @@ +exit: 0 +--- stdout +{ + "pe_trailing": null, + "pe_forward": null, + "pb": 4.6, + "roe": 0.198, + "roe_pct": 19.8, + "net_margin": 0.442, + "net_margin_pct": 44.2, + "ev_ebitda": null, + "pe_signal": "no data", + "pb_signal": "expensive", + "roe_signal": "strong", + "margin_signal": "excellent", + "ev_ebitda_signal": "no data", + "overall_signal": "expensive" +} +--- stderr diff --git a/tests/golden/stocks-valuation-nonfinite.table.golden b/tests/golden/stocks-valuation-nonfinite.table.golden new file mode 100644 index 0000000..6ccd5d2 --- /dev/null +++ b/tests/golden/stocks-valuation-nonfinite.table.golden @@ -0,0 +1,21 @@ +exit: 0 +--- stdout +Valuation Analysis for BBCA.JK +┌────────────────┬─────────┬─────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞════════════════╪═════════╪═════════════════════╡ +│ P/E (Trailing) ┆ - ┆ no data │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ P/E (Forward) ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Price/Book ┆ 4.60 ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROE ┆ +19.80% ┆ strong │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Net Margin ┆ +44.20% ┆ excellent │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ EV/EBITDA ┆ - ┆ no data │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ expensive │ +└────────────────┴─────────┴─────────────────────┘ +--- stderr diff --git a/tests/golden/stocks-valuation.json.golden b/tests/golden/stocks-valuation.json.golden new file mode 100644 index 0000000..f795bfb --- /dev/null +++ b/tests/golden/stocks-valuation.json.golden @@ -0,0 +1,19 @@ +exit: 0 +--- stdout +{ + "pe_trailing": 25.4, + "pe_forward": null, + "pb": 4.6, + "roe": 0.198, + "roe_pct": 19.8, + "net_margin": 0.324, + "net_margin_pct": 32.4, + "ev_ebitda": null, + "pe_signal": "premium", + "pb_signal": "expensive", + "roe_signal": "strong", + "margin_signal": "excellent", + "ev_ebitda_signal": "no data", + "overall_signal": "expensive" +} +--- stderr diff --git a/tests/golden/stocks-valuation.table.golden b/tests/golden/stocks-valuation.table.golden new file mode 100644 index 0000000..2a0666c --- /dev/null +++ b/tests/golden/stocks-valuation.table.golden @@ -0,0 +1,21 @@ +exit: 0 +--- stdout +Valuation Analysis for BBCA.JK +┌────────────────┬─────────┬─────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞════════════════╪═════════╪═════════════════════╡ +│ P/E (Trailing) ┆ 25.40 ┆ premium │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ P/E (Forward) ┆ - ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Price/Book ┆ 4.60 ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROE ┆ +19.80% ┆ strong │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Net Margin ┆ +32.40% ┆ excellent │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ EV/EBITDA ┆ - ┆ no data │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ expensive │ +└────────────────┴─────────┴─────────────────────┘ +--- stderr diff --git a/tests/golden/yahoo-fundamental.json.golden b/tests/golden/yahoo-fundamental.json.golden new file mode 100644 index 0000000..8857e28 --- /dev/null +++ b/tests/golden/yahoo-fundamental.json.golden @@ -0,0 +1,41 @@ +exit: 0 +--- stdout +{ + "symbol": "BBCA.JK", + "growth": { + "revenue_growth": 0.118, + "earnings_growth": 0.121, + "revenue_growth_pct": 11.8, + "earnings_growth_pct": 12.1, + "revenue_signal": "moderate", + "earnings_signal": "moderate", + "overall_signal": "growing" + }, + "valuation": { + "pe_trailing": 25.4, + "pe_forward": 23.1, + "pb": 4.6, + "roe": 0.202, + "roe_pct": 20.2, + "net_margin": 0.385, + "net_margin_pct": 38.5, + "ev_ebitda": 21.28, + "pe_signal": "premium", + "pb_signal": "expensive", + "roe_signal": "excellent", + "margin_signal": "excellent", + "ev_ebitda_signal": "expensive", + "overall_signal": "expensive" + }, + "risk": { + "debt_to_equity": 18.5, + "current_ratio": 1.21, + "roa": 0.038, + "roa_pct": 3.8, + "de_signal": "conservative", + "current_ratio_signal": "tight", + "overall_signal": "moderate risk" + }, + "overall_signal": "mixed" +} +--- stderr diff --git a/tests/golden/yahoo-fundamental.table.golden b/tests/golden/yahoo-fundamental.table.golden new file mode 100644 index 0000000..651349a --- /dev/null +++ b/tests/golden/yahoo-fundamental.table.golden @@ -0,0 +1,49 @@ +exit: 0 +--- stdout +Fundamental Analysis for BBCA.JK + +Growth Analysis for BBCA.JK +┌─────────────────┬─────────┬────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═════════════════╪═════════╪════════════════════╡ +│ Revenue Growth ┆ +11.80% ┆ moderate │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Earnings Growth ┆ +12.10% ┆ moderate │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ growing │ +└─────────────────┴─────────┴────────────────────┘ + +Valuation Analysis for BBCA.JK +┌────────────────┬─────────┬─────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞════════════════╪═════════╪═════════════════════╡ +│ P/E (Trailing) ┆ 25.40 ┆ premium │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ P/E (Forward) ┆ 23.10 ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Price/Book ┆ 4.60 ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROE ┆ +20.20% ┆ excellent │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Net Margin ┆ +38.50% ┆ excellent │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ EV/EBITDA ┆ 21.28 ┆ expensive │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ expensive │ +└────────────────┴─────────┴─────────────────────┘ + +Risk Analysis for BBCA.JK +┌───────────────┬────────┬─────────────────────────┐ +│ METRIC ┆ VALUE ┆ SIGNAL │ +╞═══════════════╪════════╪═════════════════════════╡ +│ Debt/Equity ┆ 18.50 ┆ conservative │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Current Ratio ┆ 1.21 ┆ tight │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ ROA ┆ +3.80% ┆ - │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ Overall ┆ - ┆ moderate risk │ +└───────────────┴────────┴─────────────────────────┘ + +Overall Signal: mixed +--- stderr diff --git a/tests/golden/yahoo-history.json.golden b/tests/golden/yahoo-history.json.golden new file mode 100644 index 0000000..423e9c6 --- /dev/null +++ b/tests/golden/yahoo-history.json.golden @@ -0,0 +1,29 @@ +exit: 0 +--- stdout +[ + { + "date": "2024-03-01", + "open": 9800, + "high": 9900, + "low": 9750, + "close": 9875, + "volume": 12300000 + }, + { + "date": "2024-03-02", + "open": 9850, + "high": 9920, + "low": 9800, + "close": 9880, + "volume": 11000000 + }, + { + "date": "2024-03-03", + "open": 9860, + "high": 9950, + "low": 9820, + "close": 9925, + "volume": 14000000 + } +] +--- stderr diff --git a/tests/golden/yahoo-history.table.golden b/tests/golden/yahoo-history.table.golden new file mode 100644 index 0000000..59954ee --- /dev/null +++ b/tests/golden/yahoo-history.table.golden @@ -0,0 +1,13 @@ +exit: 0 +--- stdout +History for BBCA.JK +┌────────────┬───────┬───────┬───────┬───────┬────────────┐ +│ DATE ┆ OPEN ┆ HIGH ┆ LOW ┆ CLOSE ┆ VOLUME │ +╞════════════╪═══════╪═══════╪═══════╪═══════╪════════════╡ +│ 2024-03-01 ┆ 9,800 ┆ 9,900 ┆ 9,750 ┆ 9,875 ┆ 12,300,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2024-03-02 ┆ 9,850 ┆ 9,920 ┆ 9,800 ┆ 9,880 ┆ 11,000,000 │ +├╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2024-03-03 ┆ 9,860 ┆ 9,950 ┆ 9,820 ┆ 9,925 ┆ 14,000,000 │ +└────────────┴───────┴───────┴───────┴───────┴────────────┘ +--- stderr diff --git a/tests/golden/yahoo-quote.json.golden b/tests/golden/yahoo-quote.json.golden new file mode 100644 index 0000000..ead2991 --- /dev/null +++ b/tests/golden/yahoo-quote.json.golden @@ -0,0 +1,20 @@ +exit: 0 +--- stdout +[ + { + "symbol": "BBCA.JK", + "price": 9875, + "change": 117, + "change_pct": 1.1990161918425908, + "volume": 12300000, + "market_cap": 1215200000000000, + "week52_high": 10250, + "week52_low": 7800, + "week52_position": 0.8469387755102041, + "range_signal": "upper", + "prev_close": 9758, + "avg_volume": 10000000, + "as_of": null + } +] +--- stderr diff --git a/tests/golden/yahoo-quote.table.golden b/tests/golden/yahoo-quote.table.golden new file mode 100644 index 0000000..6102d71 --- /dev/null +++ b/tests/golden/yahoo-quote.table.golden @@ -0,0 +1,8 @@ +exit: 0 +--- stdout +┌─────────┬───────┬──────┬────────┬────────────┬───────────────────────┬────────────┐ +│ SYMBOL ┆ PRICE ┆ CHG ┆ CHG% ┆ VOLUME ┆ MKT CAP ┆ 52W RANGE │ +╞═════════╪═══════╪══════╪════════╪════════════╪═══════════════════════╪════════════╡ +│ BBCA.JK ┆ 9,875 ┆ +117 ┆ +1.20% ┆ 12,300,000 ┆ 1,215,200,000,000,000 ┆ ████████░░ │ +└─────────┴───────┴──────┴────────┴────────────┴───────────────────────┴────────────┘ +--- stderr