mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
feat(core): broker summaries from IPOT
Adds a BrokerSummaryProvider capability served by Indo Premier's public broker summary page: the top 10 buyers and sellers of a stock by lots over a date range, with totals. No login or key. - api/ipot: pure parser (decimal scaling of `1.6 M`-style figures), a client that retries the server's spurious 404s, and a fixture mock. - broker: Broker::summary/latest over the cache (7 days for closed ranges, 5 minutes for today), range checks, and IDX member names bundled from GetBrokerCodeList. - foreign_net_value is null for filtered summaries: the source does not apply the filters to it. The endpoint's behavior was first mapped by fzn0x/idx (MIT). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
parent
ebdba13a41
commit
2c93c73b6a
16 changed files with 1767 additions and 1 deletions
|
|
@ -2,7 +2,10 @@
|
|||
//! `IDX_USE_MOCK_PROVIDER`. Release builds do not contain it.
|
||||
|
||||
use super::types::{Bar, Fundamentals, Interval, Period, Quote};
|
||||
use super::{FundamentalsProvider, HistoryProvider, QuoteProvider, msn, yahoo};
|
||||
use super::{
|
||||
BrokerSummaryProvider, FundamentalsProvider, HistoryProvider, QuoteProvider, ipot, msn, yahoo,
|
||||
};
|
||||
use crate::broker::types::{BrokerBoard, BrokerInvestor, BrokerQuery};
|
||||
use crate::config::ProviderKind;
|
||||
use crate::error::IdxError;
|
||||
|
||||
|
|
@ -10,6 +13,15 @@ pub(crate) fn enabled() -> bool {
|
|||
std::env::var("IDX_USE_MOCK_PROVIDER").is_ok()
|
||||
}
|
||||
|
||||
/// `IDX_MOCK_TODAY` (YYYY-MM-DD) pins today's date under the mock provider.
|
||||
pub(crate) fn today() -> Option<chrono::NaiveDate> {
|
||||
if !enabled() {
|
||||
return None;
|
||||
}
|
||||
let raw = std::env::var("IDX_MOCK_TODAY").ok()?;
|
||||
chrono::NaiveDate::parse_from_str(&raw, "%Y-%m-%d").ok()
|
||||
}
|
||||
|
||||
pub struct MockProvider {
|
||||
quote: Result<Quote, IdxError>,
|
||||
fundamentals: Result<Fundamentals, IdxError>,
|
||||
|
|
@ -117,3 +129,38 @@ impl HistoryProvider for MockProvider {
|
|||
self.history.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Broker summaries from `tests/fixtures/ipot_brokersummary_<code>_<from>
|
||||
/// [_<to>][_<investor>_<board>].html`; a query without a fixture reads as an
|
||||
/// unknown code.
|
||||
pub struct MockBrokerProvider;
|
||||
|
||||
impl BrokerSummaryProvider for MockBrokerProvider {
|
||||
fn broker_summary(
|
||||
&self,
|
||||
code: &str,
|
||||
query: &BrokerQuery,
|
||||
) -> Result<ipot::BrokerSummaryRaw, IdxError> {
|
||||
if std::env::var("IDX_MOCK_ERROR").is_ok() {
|
||||
return Err(IdxError::ProviderUnavailable);
|
||||
}
|
||||
let mut name = format!(
|
||||
"ipot_brokersummary_{}_{}",
|
||||
code.to_ascii_lowercase(),
|
||||
query.from.format("%Y%m%d")
|
||||
);
|
||||
if query.to != query.from {
|
||||
name.push_str(&query.to.format("_%Y%m%d").to_string());
|
||||
}
|
||||
if (query.investor, query.board) != (BrokerInvestor::All, BrokerBoard::All) {
|
||||
name.push_str(&format!(
|
||||
"_{}_{}",
|
||||
query.investor.as_str(),
|
||||
query.board.as_str()
|
||||
));
|
||||
}
|
||||
let html = std::fs::read_to_string(format!("tests/fixtures/{name}.html"))
|
||||
.unwrap_or_else(|_| "Invalid stock code!".to_string());
|
||||
ipot::summary_from_html(code, &html)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue