storing data for multiple exchanges for a single isin

This commit is contained in:
2025-11-24 18:33:13 +01:00
parent 9cfcae84ea
commit bbc19f2110
6 changed files with 473 additions and 106 deletions

View File

@@ -1,7 +1,6 @@
// src/corporate/update.rs
use super::{scraper::*, storage::*, helpers::*, types::*, aggregation::aggregate_best_price_data};
use crate::config::Config;
use yfinance_rs::{Range, Interval};
use chrono::Local;
use std::collections::HashMap;
@@ -20,26 +19,67 @@ pub async fn run_full_update(client: &fantoccini::Client, config: &Config) -> an
ensure_company_dirs(&company.isin).await?;
save_company_metadata(&company).await?;
for ticker_info in &company.tickers {
// === STEP 1: Discover all available exchanges ===
let mut all_tickers = company.tickers.clone();
// Try to discover additional exchanges using the primary ticker
if let Some(primary_ticker) = company.tickers.iter().find(|t| t.primary) {
println!(" 🔍 Discovering additional exchanges...");
match discover_available_exchanges(&company.isin, &primary_ticker.ticker).await {
Ok(discovered) => {
// Merge discovered tickers with existing ones
for disc in discovered {
if !all_tickers.iter().any(|t| t.ticker == disc.ticker) {
println!(" ✓ Found new exchange: {} ({})", disc.ticker, disc.exchange_mic);
all_tickers.push(disc);
}
}
}
Err(e) => println!(" ⚠ Discovery failed: {}", e),
}
}
// Update metadata with newly discovered tickers
if all_tickers.len() > company.tickers.len() {
let updated_company = CompanyMetadata {
isin: company.isin.clone(),
name: company.name.clone(),
tickers: all_tickers.clone(),
};
save_company_metadata(&updated_company).await?;
println!(" 📝 Updated metadata with {} total tickers", all_tickers.len());
}
// === STEP 2: Fetch data from all available exchanges ===
for ticker_info in &all_tickers {
let ticker = &ticker_info.ticker;
println!(" → Trying ticker: {ticker} ({})", ticker_info.exchange_mic);
println!(" → Trying ticker: {} ({})", ticker, ticker_info.exchange_mic);
let mut daily_success = false;
let mut intraday_success = false;
// Earnings
if let Ok(new_events) = fetch_earnings_history(client, ticker).await {
let result = process_batch(&new_events, &mut existing_events, &today);
save_changes(&result.changes).await?;
println!(" {} earnings events", new_events.len());
// Earnings (only from primary ticker to avoid duplicates)
if ticker_info.primary {
if let Ok(new_events) = fetch_earnings_history(client, ticker).await {
let result = process_batch(&new_events, &mut existing_events, &today);
save_changes(&result.changes).await?;
println!("{} earnings events", new_events.len());
}
}
// Daily prices
if let Ok(prices) = fetch_daily_price_history(ticker, &config.corporate_start_date, &today).await {
if !prices.is_empty() {
save_prices_by_source(&company.isin, ticker, "daily", prices).await?;
daily_success = true;
match fetch_daily_price_history(ticker, &config.corporate_start_date, &today).await {
Ok(prices) => {
if !prices.is_empty() {
save_prices_by_source(&company.isin, ticker, "daily", prices.clone()).await?;
daily_success = true;
println!(" ✓ Saved {} daily bars ({} currency)",
prices.len(),
prices.first().map(|p| p.currency.as_str()).unwrap_or("?")
);
}
}
Err(e) => println!(" ✗ Daily fetch failed: {}", e),
}
// 5-minute prices (last 60 days)
@@ -47,34 +87,41 @@ pub async fn run_full_update(client: &fantoccini::Client, config: &Config) -> an
.format("%Y-%m-%d")
.to_string();
if let Ok(prices) = fetch_price_history_5min(ticker, &sixty_days_ago, &today).await {
if !prices.is_empty() {
save_prices_by_source(&company.isin, ticker, "5min", prices.clone()).await?;
intraday_success = true;
println!(" Saved {} 5min bars from {ticker}", prices.len());
match fetch_price_history_5min(ticker, &sixty_days_ago, &today).await {
Ok(prices) => {
if !prices.is_empty() {
save_prices_by_source(&company.isin, ticker, "5min", prices.clone()).await?;
intraday_success = true;
println!(" ✓ Saved {} 5min bars", prices.len());
}
}
Err(e) => println!(" ✗ 5min fetch failed: {}", e),
}
// Record success
update_available_exchange(
&company.isin,
ticker,
&ticker_info.exchange_mic,
daily_success,
intraday_success,
).await?;
aggregate_best_price_data(&company.isin).await?;
// Record success in available_exchanges.json
if daily_success || intraday_success {
update_available_exchange(
&company.isin,
ticker,
&ticker_info.exchange_mic,
daily_success,
intraday_success,
).await?;
}
tokio::time::sleep(tokio::time::Duration::from_millis(800)).await;
}
// Optional: run aggregation after all sources
// aggregate_best_price_data(&company.isin).await?;
// === STEP 3: Aggregate prices from all sources ===
println!(" 📊 Aggregating multi-exchange data with FX conversion...");
match aggregate_best_price_data(&company.isin).await {
Ok(_) => println!(" ✓ Aggregation complete"),
Err(e) => println!(" ⚠ Aggregation warning: {}", e),
}
}
save_optimized_events(existing_events).await?;
println!("Corporate update complete (ISIN-based)");
println!("\nCorporate update complete (ISIN-based)");
Ok(())
}