mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
fix(core): reject broker and trading pages that would misreport a session
From the 0.4.0 release review (Codex): - IPOT pages with a short row, a rank out of order or a broker after a blank row are parse errors; skipping a row shifted later brokers up a rank and could flip a bandar reading (-2425 bps to -66 bps). - A tbody end before its start is a parse error, not a panic. - An IPOT page that echoes other dates than requested is rejected before it is cached, so one session's figures never pass as another's. - An IDX trading summary with rows for other days only is an error, not an empty session cached for 30 days. - Empty sessions count toward a flow's fetched_at and stale. - The bandar imbalance uses checked i128 arithmetic: past i128 there is no reading instead of a wrapped one. - The bandar table prints "No trades." only when no broker traded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
parent
8b507d78e2
commit
d82f7c6be9
6 changed files with 137 additions and 19 deletions
|
|
@ -38,11 +38,13 @@ fn whole(value: Option<f64>) -> Option<i64> {
|
|||
|
||||
/// The rows of one session. A day without trading (weekend, holiday) is
|
||||
/// empty; rows dated other than `date` are dropped, so another session's
|
||||
/// figures never pass as this one's.
|
||||
/// figures never pass as this one's. Rows for other days only are an error,
|
||||
/// not an empty session, so they are never cached as one.
|
||||
pub fn parse_stock_summary(raw: &str, date: NaiveDate) -> Result<Vec<StockDay>, IdxError> {
|
||||
let response: StockSummaryResponse = serde_json::from_str(raw)
|
||||
.map_err(|e| IdxError::ParseError(format!("idx stock summary: {e}")))?;
|
||||
Ok(response
|
||||
let answered = response.data.len();
|
||||
let rows: Vec<StockDay> = response
|
||||
.data
|
||||
.into_iter()
|
||||
.filter(|row| {
|
||||
|
|
@ -66,7 +68,13 @@ pub fn parse_stock_summary(raw: &str, date: NaiveDate) -> Result<Vec<StockDay>,
|
|||
non_regular_volume: whole(row.non_regular_volume),
|
||||
non_regular_value: whole(row.non_regular_value),
|
||||
})
|
||||
.collect())
|
||||
.collect();
|
||||
if rows.is_empty() && answered > 0 {
|
||||
return Err(IdxError::ParseError(format!(
|
||||
"idx stock summary: asked for {date}, got {answered} rows for other days"
|
||||
)));
|
||||
}
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -101,11 +109,10 @@ mod tests {
|
|||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
assert!(
|
||||
parse_stock_summary(DAY, date(2026, 10, 15))
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
assert!(matches!(
|
||||
parse_stock_summary(DAY, date(2026, 10, 15)),
|
||||
Err(IdxError::ParseError(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
parse_stock_summary("<html>Just a moment...</html>", date(2026, 10, 16)),
|
||||
Err(IdxError::ParseError(_))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue