feat: bandarmology (idx stocks bandar, analyze_bandar)

`idx stocks bandar SYMBOL...` reads the broker summary: for the top 1, 3,
5 and 10 brokers per side, buy value minus sell value in basis points of
value traded, with a signal from big accumulation to big distribution on
the headline tier (--top, default 5). Several symbols give a screen ranked
by that figure (at most 100, one request each), which pipes from
`stocks screen`. The MCP server exposes it as analyze_bandar (20 symbols).

- analysis/bandar.rs is pure and versioned (`top_concentration_v1`):
  integer basis points, no figure missing counted as zero, buy/sell VWAP
  from exact averages, and the brokers on both sides listed because their
  gross figures overstate their direction.
- Thresholds (±5%/±15%) follow fzn0x/idx (MIT); docs call them heuristics.
- Invalid input fails a screen at once; other errors fail one symbol and
  are warned about on stderr.

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:55:25 +00:00
commit 970da26a0e
25 changed files with 1588 additions and 37 deletions

View file

@ -115,10 +115,9 @@ pub struct AnalyzeParams {
pub kinds: Option<Vec<AnalysisKind>>,
}
/// Which sessions and trades a broker tool covers.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BrokerParams {
/// IDX ticker, e.g. "BBCA".
pub symbol: String,
pub struct BrokerScope {
/// One session, YYYY-MM-DD. Omit date, from and to for the latest session with trades.
#[serde(default)]
pub date: Option<String>,
@ -139,6 +138,25 @@ pub struct BrokerParams {
pub board: Option<String>,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BrokerParams {
/// IDX ticker, e.g. "BBCA".
pub symbol: String,
#[serde(flatten)]
pub scope: BrokerScope,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BandarParams {
/// IDX tickers, e.g. ["BBCA", "BBRI"]; the `.JK` suffix is optional. At most 20.
pub symbols: Vec<String>,
#[serde(flatten)]
pub scope: BrokerScope,
/// Tier behind the headline signal: 1, 3, 5 or 10 brokers per side (default 5).
#[serde(default)]
pub top: Option<u8>,
}
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ShareholdersParams {
/// IDX ticker, e.g. "BBCA".