pub use crate::hexnum::HexNum;
pub use crate::strnum::FloatOrString;
use serde::Deserialize;
use std::collections::HashMap;
type JsonObject = HashMap<String, serde_json::Value>;
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Stock {
pub company: String,
pub currency: String,
pub exchange: String,
pub industry: String,
pub sector: String,
pub subindustry: String,
pub symbol: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Quote {
#[serde(rename = "Currency")]
pub currency: String,
#[serde(rename = "Day\'s Change %")]
pub todays_change: FloatOrString,
#[serde(rename = "Day\'s Volume")]
pub todays_volume: FloatOrString,
#[serde(rename = "Exchange")]
pub exchange: String,
#[serde(rename = "Current Price")]
pub current_price: FloatOrString,
#[serde(rename = "Price")]
pub price: FloatOrString,
#[serde(rename = "Price Change")]
pub price_change: FloatOrString,
#[serde(rename = "Price Updated Time")]
pub update_time: String,
#[serde(rename = "Symbol")]
pub symbol: String,
pub high: FloatOrString,
pub low: FloatOrString,
pub open: FloatOrString,
pub timestamp: i64,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct StockSummary {
pub summary: StockSummaryByCat,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct StockSummaryByCat {
pub general: GeneralData,
pub chart: Chart,
pub ratio: JsonObject,
pub guru: HashMap<String, GuruTransaction>,
pub insider: HashMap<String, InsiderTransaction>,
pub company_data: JsonObject,
pub estimate: Estimate,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct GuruTransaction {
pub buy: i64,
pub hold: i64,
pub new_buy: i64,
pub sell: i64,
pub sold_out: i64,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct InsiderTransaction {
pub buy: FloatOrString,
pub sell: Option<FloatOrString>,
pub price: Option<FloatOrString>,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Chart {
#[serde(rename = "Graham Number")]
pub graham_number: FloatOrString,
#[serde(rename = "Median P/S Value")]
pub median_ps_value: FloatOrString,
#[serde(rename = "Peter Lynch Value")]
pub peter_lynch_value: FloatOrString,
#[serde(rename = "Net Current Asset Value")]
pub net_current_asset_value: FloatOrString,
#[serde(rename = "Net-Net Working Capital")]
pub net_net_working_capital: FloatOrString,
#[serde(rename = "Projected FCF")]
pub projected_fcf: FloatOrString,
#[serde(rename = "Tangible Book")]
pub tangible_book: FloatOrString,
#[serde(rename = "DCF (Earnings Based)")]
pub dcf_earnings_based: FloatOrString,
#[serde(rename = "DCF (FCF Based)")]
pub dcf_fcf_based: FloatOrString,
#[serde(rename = "GF Value")]
pub gf_balue: FloatOrString,
#[serde(rename = "Earnings Power Value")]
pub earnings_power_value: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Estimate {
#[serde(rename = "LongTermGrowthRateMean")]
pub longtermgrowthratemean: FloatOrString,
pub eps_nri: Vec<FloatOrString>,
pub count: FloatOrString,
#[serde(rename = "per share eps")]
pub per_share_eps: Vec<FloatOrString>,
pub percentage: FloatOrString,
#[serde(rename = "Dividends Per Share")]
pub dividends_per_share: Vec<FloatOrString>,
#[serde(rename = "Revenue")]
pub revenue: Vec<FloatOrString>,
pub quarter: Vec<String>,
#[serde(rename = "LongTermRevenueGrowthRateMean")]
pub longtermrevenuegrowthratemean: FloatOrString,
#[serde(rename = "Book Value per Share")]
pub book_value_per_share: Option<Vec<FloatOrString>>,
#[serde(rename = "EBIT")]
pub ebit: Option<Vec<FloatOrString>>,
#[serde(rename = "EBITDA")]
pub ebitda: Option<Vec<FloatOrString>>,
#[serde(rename = "Net Income")]
pub net_income: Option<Vec<FloatOrString>>,
#[serde(rename = "Pretax Income")]
pub pretax_income: Option<Vec<FloatOrString>>,
#[serde(rename = "Operating Cash Flow per Share")]
pub operating_cash_flow_per_share: Option<Vec<FloatOrString>>,
#[serde(rename = "ROA %")]
pub roa_percent: Option<Vec<FloatOrString>>,
#[serde(rename = "ROE %")]
pub roe_percent: Option<Vec<FloatOrString>>,
#[serde(rename = "Gross Margin %")]
pub gross_margin_percent: Option<Vec<FloatOrString>>,
pub future_revenue_estimate_growth: Option<FloatOrString>,
pub future_per_share_eps_estimate_growth: Option<FloatOrString>,
pub future_eps_nri_estimate_growth: Option<FloatOrString>,
pub future_dividend_estimate_growth: Option<FloatOrString>,
pub future_operating_cash_flow_per_share_estimate_growth: Option<FloatOrString>,
pub future_book_value_per_share_estimate_growth: Option<FloatOrString>,
pub future_net_income_estimate_growth: Option<FloatOrString>,
pub future_ebit_estimate_growth: Option<FloatOrString>,
pub future_ebitda_estimate_growth: Option<FloatOrString>,
pub future_pretax_income_estimate_growth: Option<FloatOrString>,
pub future_gross_margin_estimate_growth: Option<FloatOrString>,
pub future_roa_estimate_growth: Option<FloatOrString>,
pub future_roe_estimate_growth: Option<FloatOrString>,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct GeneralData {
pub company: String,
pub desc: String,
pub rank_financial_strength: FloatOrString,
pub supersector: String,
pub sector: String,
pub currency: String,
pub price: FloatOrString,
pub short_desc: String,
pub rank_profitability: FloatOrString,
pub rating: FloatOrString,
pub country: String,
pub group: String,
pub subindustry: String,
pub timestamp: String,
pub gf_score: FloatOrString,
pub rank_gf_value: FloatOrString,
pub rank_growth: FloatOrString,
pub rank_momentum: FloatOrString,
pub risk_assessment: String,
pub gf_valuation: String,
pub valuation_box_status: String,
pub percentage_of_13f_buys: FloatOrString,
pub percentage_of_13f_sells: FloatOrString,
pub percentage_of_13f_holds: FloatOrString,
pub percentage_of_mutual_fund_buys: FloatOrString,
pub percentage_of_mutual_fund_sells: FloatOrString,
pub percentage_of_mutual_fund_holds: FloatOrString,
pub percentage_of_etf_buys: FloatOrString,
pub percentage_of_etf_sells: FloatOrString,
pub percentage_of_etf_holds: FloatOrString,
pub percentage_of_premium_guru_buys: FloatOrString,
pub percentage_of_premium_guru_sells: FloatOrString,
pub percentage_of_premium_guru_holds: FloatOrString,
pub percentage_of_premiumplus_guru_buys: FloatOrString,
pub percentage_of_premiumplus_guru_sells: FloatOrString,
pub percentage_of_premiumplus_guru_holds: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct RatioCmp {
pub his: HistoryCmp,
pub indu: IndustryCmp,
pub status: FloatOrString,
pub value: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct HistoryCmp {
pub high: FloatOrString,
pub low: FloatOrString,
#[serde(default)]
pub med: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct IndustryCmp {
pub global_rank: FloatOrString,
pub indu_med: FloatOrString,
pub indu_tot: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct RatioRange {
pub color: HexNum,
pub current: FloatOrString,
pub high: FloatOrString,
pub low: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct WarningDetails {
pub category: Option<String>,
pub degree: String,
pub details: String,
pub display: String,
pub name: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct GoodDetails {
pub category: Option<String>,
pub details: String,
pub display: String,
pub name: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct CompanyDescription {
pub address: String,
pub descrpt: String,
pub morn_comp_id: Option<String>,
pub short_descript: String,
pub symbol: Option<String>,
pub website: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Country {
pub country: String,
pub exchange: String,
pub symbol: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct StockDynamics {
pub high: FloatOrString,
pub low: FloatOrString,
pub open: FloatOrString,
pub p_change: FloatOrString,
pub p_pct_change: FloatOrString,
pub price: FloatOrString,
pub stockid: Option<String>,
pub volumn_day: FloatOrString,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct IndustryDetails {
pub group: String,
pub groupcode: i64,
pub industry: String,
pub industrycode: i64,
pub sector: String,
pub sectorcode: i64,
pub date: String,
}
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Dividend {
pub ex_date: String,
pub record_date: String,
pub amount: FloatOrString,
pub pay_date: String,
pub currency: String,
#[serde(rename = "type")]
pub div_type: String,
}
#[cfg(test)]
mod tests {
use super::super::*;
use super::*;
use crate::gurus::GuruTrades;
use std::{convert::TryFrom, env};
use time::{Date, UtcDateTime};
fn get_months_before(date: Date, months: u8) -> Date {
let mut day = date.day();
let mut month = date.month() as u8;
let mut year = date.year();
while month + 1 < months {
month += 12;
year -= 1;
}
month -= months;
let month: time::Month = time::Month::try_from(month).unwrap();
day = day.min(month.length(year));
Date::from_calendar_date(year, month, day).unwrap()
}
#[tokio::test]
async fn test_guru_trades() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let stock = "WMT";
let trades = gf_connect.get_guru_trades(stock).await;
assert!(trades.is_ok());
let trades = serde_json::from_value::<HashMap<String, GuruTrades>>(trades.unwrap());
assert!(trades.is_ok());
}
}
}
#[tokio::test]
async fn test_quotes() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let ticker = ["NAS:AAPL", "FRA:APC", "LTS:0JQ4"];
let prices = gf_connect.get_quotes(&ticker).await;
assert!(prices.is_ok());
let prices = serde_json::from_value::<Vec<Quote>>(prices.unwrap());
assert!(prices.is_ok());
assert!(prices.unwrap().len() > 0);
}
}
}
#[tokio::test]
async fn test_quote_history() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let stock = "NYSE:DIS";
let prices = gf_connect.get_price_hist(stock).await;
assert!(prices.is_ok());
let prices = serde_json::from_value::<Vec<(String, f64)>>(prices.unwrap());
assert!(prices.is_ok());
assert!(prices.unwrap().len() > 0);
}
}
}
#[tokio::test]
async fn test_quote_history_unadj() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let stock = "NYSE:DIS";
let prices = gf_connect.get_unadj_price_hist(stock).await;
assert!(prices.is_ok());
let prices = serde_json::from_value::<Vec<(String, f64)>>(prices.unwrap());
assert!(prices.is_ok());
assert!(prices.unwrap().len() > 0);
}
}
}
#[tokio::test]
async fn test_stock_summary() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let stock = "NYSE:BAC";
let stock_summary_json = gf_connect.get_stock_summary(stock).await;
assert!(stock_summary_json.is_ok());
let stock_summary =
serde_json::from_value::<StockSummary>(stock_summary_json.unwrap());
assert!(stock_summary.is_ok());
}
}
}
#[tokio::test]
async fn test_stock_list() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let exchange = "OSL";
let stocks = gf_connect.get_listed_stocks(exchange).await;
assert!(stocks.is_ok());
let stocks = serde_json::from_value::<Vec<Stock>>(stocks.unwrap());
assert!(stocks.is_ok());
}
}
}
#[tokio::test]
async fn test_dividend_history() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let stock = "NAS:MSFT";
let dividends = gf_connect.get_dividend_history(stock).await;
assert!(dividends.is_ok());
let dividends = serde_json::from_value::<Vec<Dividend>>(dividends.unwrap());
assert!(dividends.is_ok());
}
}
}
#[tokio::test]
async fn test_fundamental_updates() {
if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
if !token.is_empty() {
let gf_connect = GuruFocusConnector::new(token);
let now = UtcDateTime::now().date();
let one_months_ago = get_months_before(now, 6);
let stocks_json = gf_connect.get_updated_stocks(one_months_ago).await;
assert!(stocks_json.is_ok());
let stocks = serde_json::from_value::<Vec<String>>(stocks_json.unwrap());
assert!(stocks.is_ok());
}
}
}
}