mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-08-07 01:33:52 +00:00
feat(msn): implement full MSN Finance endpoint coverage
- Finance/Equities: company profile + executives (idx stock profile) - Finance/Equities/financialstatements: balance sheet, cash flow, income (idx stock financials) - Finance/Events/Earnings: EPS history + forecast (idx stock earnings) - Finance/Charts: OHLCV chart history, unblocks MSN history command - Finance/SentimentBrowser: crowd sentiment (idx stock sentiment) - api.msn.com/insights: AI insights (idx stock insights) - MSN/Feed/me: stock news feed (idx stock news) - Finance/Screener: IDX universe screener with 8 presets (idx screen) - Wire ProfileProvider, EarningsProvider, FinancialsProvider, SentimentProvider, InsightsProvider, NewsProvider traits on MsnProvider - Remove HISTORY_UNSUPPORTED_REASON — MSN history now works via charts API
This commit is contained in:
parent
3998e38ffc
commit
a0da0a3adb
8 changed files with 1049 additions and 49 deletions
|
|
@ -4,17 +4,25 @@ mod parse;
|
|||
mod raw_types;
|
||||
mod symbols;
|
||||
|
||||
use crate::api::types::{Bar, Fundamentals, Interval, Period, Quote};
|
||||
use crate::api::{FundamentalsProvider, HistoryProvider, QuoteProvider};
|
||||
use crate::api::types::{
|
||||
Bar, CompanyProfile, EarningsReport, FinancialStatements, Fundamentals, InsightData, Interval,
|
||||
NewsItem, Period, Quote, SentimentData,
|
||||
};
|
||||
use crate::api::{
|
||||
EarningsProvider, FinancialsProvider, FundamentalsProvider, HistoryProvider, InsightsProvider,
|
||||
NewsProvider, ProfileProvider, QuoteProvider, SentimentProvider,
|
||||
};
|
||||
use crate::error::IdxError;
|
||||
|
||||
use client::MsnClient;
|
||||
use map::{parse_fundamentals, parse_quote};
|
||||
use map::{
|
||||
parse_chart_history, parse_earnings, parse_financial_statements, parse_fundamentals,
|
||||
parse_insights, parse_news, parse_profile, parse_quote, parse_screener_results,
|
||||
parse_sentiment,
|
||||
};
|
||||
|
||||
pub(crate) use parse::{parse_fundamentals_from_str, parse_history_from_str, parse_quote_from_str};
|
||||
|
||||
const HISTORY_UNSUPPORTED_REASON: &str = "MSN provider does not currently support history or technical analysis because MSN charts do not consistently expose real OHLCV data";
|
||||
|
||||
pub struct MsnProvider {
|
||||
client: MsnClient,
|
||||
}
|
||||
|
|
@ -25,6 +33,16 @@ impl MsnProvider {
|
|||
client: MsnClient::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screener(
|
||||
&self,
|
||||
filter: &str,
|
||||
region: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<Quote>, IdxError> {
|
||||
let raw = self.client.fetch_screener(filter, region, limit)?;
|
||||
parse_screener_results(&raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl QuoteProvider for MsnProvider {
|
||||
|
|
@ -45,33 +63,67 @@ impl FundamentalsProvider for MsnProvider {
|
|||
impl HistoryProvider for MsnProvider {
|
||||
fn history(
|
||||
&self,
|
||||
_symbol: &str,
|
||||
_period: &Period,
|
||||
symbol: &str,
|
||||
period: &Period,
|
||||
_interval: &Interval,
|
||||
) -> Result<Vec<Bar>, IdxError> {
|
||||
Err(IdxError::Unsupported(
|
||||
HISTORY_UNSUPPORTED_REASON.to_string(),
|
||||
))
|
||||
let chart_type = period_to_chart_type(period);
|
||||
let raw = self.client.fetch_charts(symbol, chart_type)?;
|
||||
parse_chart_history(symbol, period, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::MsnProvider;
|
||||
use crate::api::HistoryProvider;
|
||||
use crate::api::types::{Interval, Period};
|
||||
use crate::error::IdxError;
|
||||
impl ProfileProvider for MsnProvider {
|
||||
fn profile(&self, symbol: &str) -> Result<CompanyProfile, IdxError> {
|
||||
let raw = self.client.fetch_equities(symbol)?;
|
||||
parse_profile(symbol, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn history_is_explicitly_unsupported() {
|
||||
let provider = MsnProvider::new(false);
|
||||
let err = provider
|
||||
.history("BBCA.JK", &Period::OneMonth, &Interval::Day)
|
||||
.expect_err("history should be unsupported");
|
||||
assert!(matches!(err, IdxError::Unsupported(_)));
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("MSN provider does not currently support history or technical analysis")
|
||||
);
|
||||
impl EarningsProvider for MsnProvider {
|
||||
fn earnings(&self, symbol: &str) -> Result<EarningsReport, IdxError> {
|
||||
let raw = self.client.fetch_earnings(symbol)?;
|
||||
parse_earnings(symbol, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl FinancialsProvider for MsnProvider {
|
||||
fn financials(&self, symbol: &str) -> Result<FinancialStatements, IdxError> {
|
||||
let raw = self.client.fetch_financial_statements(symbol)?;
|
||||
parse_financial_statements(symbol, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl SentimentProvider for MsnProvider {
|
||||
fn sentiment(&self, symbol: &str) -> Result<SentimentData, IdxError> {
|
||||
let raw = self.client.fetch_sentiment(symbol)?;
|
||||
parse_sentiment(symbol, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl InsightsProvider for MsnProvider {
|
||||
fn insights(&self, symbol: &str) -> Result<InsightData, IdxError> {
|
||||
let raw = self.client.fetch_insights(symbol)?;
|
||||
parse_insights(symbol, &raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl NewsProvider for MsnProvider {
|
||||
fn news(&self, symbol: &str, limit: usize) -> Result<Vec<NewsItem>, IdxError> {
|
||||
let raw = self.client.fetch_news(symbol, limit)?;
|
||||
parse_news(&raw)
|
||||
}
|
||||
}
|
||||
|
||||
fn period_to_chart_type(period: &Period) -> &'static str {
|
||||
match period {
|
||||
Period::OneDay => "1D",
|
||||
Period::FiveDays => "1W",
|
||||
Period::OneMonth => "1M",
|
||||
Period::ThreeMonths => "3M",
|
||||
Period::SixMonths => "6M",
|
||||
Period::OneYear => "1Y",
|
||||
Period::TwoYears => "3Y",
|
||||
Period::FiveYears => "5Y",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue