feat(cli,mcp): idx stocks broker and get_broker_summary

`idx stocks broker SYMBOL` shows the top 10 buyers and sellers of a stock
by lots for the latest session with trades, one `--date`, or a
`--from`/`--to` range, filtered by `--investor` and `--board`. The MCP
server exposes the same as get_broker_summary.

- Range and filter parsing lives in idx-core (BrokerRange::from_args,
  FromStr for the filters), so both frontends accept the same input.
- Without dates, the latest session is found by walking back over
  weekdays; offline, uncached days are skipped.
- Table output lists foreign and state-owned brokers, and says when the
  source rounded figures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
hermes 2026-10-18 04:38:58 +00:00
commit 860d4daf77
25 changed files with 939 additions and 13 deletions

View file

@ -2,6 +2,7 @@
//! accepted values and error messages match the CLI; the schemas list them.
use idx_core::api::types::{Interval, Period};
use idx_core::broker::types::{BrokerBoard, BrokerInvestor};
use idx_core::market::{ScreenFilter, ScreenRegion};
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
use serde::Deserialize;
@ -114,6 +115,30 @@ pub struct AnalyzeParams {
pub kinds: Option<Vec<AnalysisKind>>,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BrokerParams {
/// IDX ticker, e.g. "BBCA".
pub symbol: String,
/// One session, YYYY-MM-DD. Omit date, from and to for the latest session with trades.
#[serde(default)]
pub date: Option<String>,
/// First session of a range, YYYY-MM-DD (at most 366 days).
#[serde(default)]
pub from: Option<String>,
/// Last session of the range, YYYY-MM-DD (default today).
#[serde(default)]
pub to: Option<String>,
/// Whose trades to count, by investor domicile (default "all").
#[serde(default)]
#[schemars(schema_with = "investor_schema")]
pub investor: Option<String>,
/// Which board's trades to count (default "all"); "regular" leaves out
/// negotiated block trades.
#[serde(default)]
#[schemars(schema_with = "board_schema")]
pub board: Option<String>,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ShareholdersParams {
/// IDX ticker, e.g. "BBCA".
@ -178,3 +203,11 @@ fn filter_schema(_: &mut SchemaGenerator) -> Schema {
fn region_schema(_: &mut SchemaGenerator) -> Schema {
string_enum(ScreenRegion::ALL.map(ScreenRegion::as_str).to_vec())
}
fn investor_schema(_: &mut SchemaGenerator) -> Schema {
string_enum(BrokerInvestor::ALL.map(BrokerInvestor::as_str).to_vec())
}
fn board_schema(_: &mut SchemaGenerator) -> Schema {
string_enum(BrokerBoard::ALL.map(BrokerBoard::as_str).to_vec())
}