use chrono::Datelike;
use finnhub::{FinnhubClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
let api_key =
std::env::var("FINNHUB_API_KEY").expect("FINNHUB_API_KEY environment variable not set");
let client = FinnhubClient::new(api_key);
let symbol = "AAPL";
println!("π Alternative Data Analysis for {}", symbol);
println!("{}", "=".repeat(55));
esg_analysis(&client, symbol).await?;
patent_analysis(&client, symbol).await?;
misc_alternative_data(&client).await?;
Ok(())
}
async fn esg_analysis(client: &FinnhubClient, symbol: &str) -> Result<()> {
println!("\nπ± ESG (Environmental, Social, Governance) Analysis");
println!("{}", "-".repeat(50));
match client.stock().esg(symbol).await {
Ok(esg) => {
println!(
"π’ Company: {}",
esg.company_name.as_deref().unwrap_or(symbol)
);
if let Some(overall_rating) = esg.esg_risk_rating {
println!("π Overall ESG Risk Rating: {:.1}", overall_rating);
if let Some(level) = &esg.esg_risk_level {
let risk_emoji = match level.as_str() {
"Low" => "π’",
"Medium" => "π‘",
"High" => "π΄",
_ => "βͺ",
};
println!("π― Risk Level: {} {}", risk_emoji, level);
}
}
println!("\nπ Component Scores:");
if let Some(env_score) = esg.environment_risk_score {
let env_rating = score_to_rating(env_score);
println!(" π Environmental: {:.1} ({})", env_score, env_rating);
}
if let Some(social_score) = esg.social_risk_score {
let social_rating = score_to_rating(social_score);
println!(" π₯ Social: {:.1} ({})", social_score, social_rating);
}
if let Some(gov_score) = esg.governance_risk_score {
let gov_rating = score_to_rating(gov_score);
println!(" ποΈ Governance: {:.1} ({})", gov_score, gov_rating);
}
if let Some(overall) = esg.esg_risk_rating {
println!("\nπ‘ ESG Insights:");
if overall < 10.0 {
println!(" β
Negligible ESG risk - Strong sustainability practices");
} else if overall < 20.0 {
println!(" π’ Low ESG risk - Good sustainability management");
} else if overall < 30.0 {
println!(" π‘ Medium ESG risk - Some areas for improvement");
} else if overall < 40.0 {
println!(" πΆ High ESG risk - Significant sustainability concerns");
} else {
println!(" π΄ Severe ESG risk - Major sustainability issues");
}
}
}
Err(e) => println!("ESG data not available: {}", e),
}
Ok(())
}
fn score_to_rating(score: f64) -> &'static str {
if score < 10.0 {
"Excellent"
} else if score < 20.0 {
"Good"
} else if score < 30.0 {
"Average"
} else if score < 40.0 {
"Poor"
} else {
"Critical"
}
}
async fn patent_analysis(client: &FinnhubClient, symbol: &str) -> Result<()> {
println!("\nπ‘ Innovation & Patent Analysis");
println!("{}", "-".repeat(50));
let current_year = chrono::Utc::now().year();
let last_year = current_year - 1;
let from_date = format!("{}-01-01", last_year);
let to_date = format!("{}-12-31", current_year);
match client
.stock()
.uspto_patents(symbol, &from_date, &to_date)
.await
{
Ok(patents) => {
if patents.data.is_empty() {
println!("No patent applications found for the specified period");
} else {
println!("π Patent Applications ({} - {}):", last_year, current_year);
println!("Total Applications: {}", patents.data.len());
println!("\nπ Innovation Metrics:");
println!(" {} total applications in period", patents.data.len());
let avg_per_year = patents.data.len() as f64 / 2.0; println!(" ~{:.1} applications per year (average)", avg_per_year);
}
}
Err(e) => println!("Patent data not available: {}", e),
}
Ok(())
}
async fn misc_alternative_data(client: &FinnhubClient) -> Result<()> {
println!("\nπ Miscellaneous Alternative Data");
println!("{}", "-".repeat(50));
match client.misc().covid19().await {
Ok(covid_data) => {
println!("π¦ COVID-19 Data (US States):");
let mut sorted_indices: Vec<usize> = (0..covid_data.len()).collect();
sorted_indices.sort_by(|&a, &b| {
covid_data[b]
.cases
.partial_cmp(&covid_data[a].cases)
.unwrap()
});
for (i, &idx) in sorted_indices.iter().take(5).enumerate() {
let state_data = &covid_data[idx];
println!(
" {}. {}: {:.0} cases, {:.0} deaths",
i + 1,
state_data.state,
state_data.cases,
state_data.death
);
}
let total_cases: f64 = covid_data.iter().map(|s| s.cases).sum();
let total_deaths: f64 = covid_data.iter().map(|s| s.death).sum();
println!(
" Total US: {:.0} cases, {:.0} deaths",
total_cases, total_deaths
);
}
Err(e) => println!("COVID-19 data not available: {}", e),
}
match client.misc().country().await {
Ok(countries) => {
println!("\nπΊοΈ Country Risk Analysis (Sample):");
let mut major_economies: Vec<_> = countries
.iter()
.filter(|c| {
["US", "CN", "JP", "DE", "GB", "FR", "IN", "IT", "BR", "CA"]
.contains(&c.code2.as_str())
})
.collect();
major_economies.sort_by(|a, b| {
a.country_risk_premium
.unwrap_or(0.0)
.partial_cmp(&b.country_risk_premium.unwrap_or(0.0))
.unwrap()
});
for country in major_economies.iter().take(5) {
let risk_premium = country.country_risk_premium.unwrap_or(0.0);
let equity_premium = country.equity_risk_premium.unwrap_or(0.0);
println!(
" {} ({}): Risk {:.2}%, Equity Premium {:.2}%",
country.code2,
country.rating.as_ref().unwrap_or(&"N/A".to_string()),
risk_premium,
equity_premium
);
}
}
Err(e) => println!("Country data not available: {}", e),
}
match client.misc().fda_calendar().await {
Ok(fda_events) => {
println!("\nπ Upcoming FDA Events:");
if fda_events.is_empty() {
println!("No upcoming FDA events scheduled");
} else {
for (i, event) in fda_events.iter().take(3).enumerate() {
println!(" {}. {} to {}", i + 1, event.from_date, event.to_date);
let description = if event.event_description.len() > 100 {
format!("{}...", &event.event_description[..97])
} else {
event.event_description.clone()
};
println!(" {}", description);
}
}
}
Err(e) => println!("FDA calendar not available: {}", e),
}
match client.misc().symbol_search("tesla", Some("US")).await {
Ok(results) => {
println!("\nπ Symbol Search Example (Tesla):");
println!("Found {} results:", results.count);
for (i, result) in results.result.iter().take(3).enumerate() {
println!(
" {}. {} - {} ({})",
i + 1,
result.symbol,
result.description,
result.security_type
);
}
}
Err(e) => println!("Symbol search not available: {}", e),
}
match client.misc().sector_metrics("NA").await {
Ok(metrics) => {
println!("\nπ North American Sector Metrics:");
for (i, sector) in metrics.data.iter().take(3).enumerate() {
println!(" {}. {}", i + 1, sector.sector);
println!(
" Metrics available: {} indicators",
sector.metrics.len()
);
}
}
Err(e) => println!("Sector metrics not available: {}", e),
}
Ok(())
}