finance-query v3.0.0

FRED & Treasury Yields#

abstract · Cargo Docs

docs.rs/finance-query — fred

info · Feature flag required

Add fred = ["dep:csv"] to your Cargo.toml features to enable this module.

toml
finance-query = { version = "...", features = ["fred"] }

The fred module provides two macro-economic data sources:

FRED Setup#

Get a free API key at fred.stlouisfed.org, then call fred::init once at application startup:

rust · no_run feature=fred
use finance_query::fred;
use std::time::Duration;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize with API key
    fred::init("your-fred-api-key")?;

    // Or, instead: initialize with a custom timeout (pick exactly one)
    fred::init_with_timeout("your-fred-api-key", Duration::from_secs(60))?;
    Ok(())
}

warning

Calling init more than once returns an error. Call it exactly once per process, typically at startup.

The client is a process-wide singleton, so the second call always fails. This example runs as a real test:

rust · runnable
use finance_query::fred;

let _ = fred::init("api-key");
let second_init = fred::init("another-api-key");
assert!(second_init.is_err());
println!("second init is_err = {}", second_init.is_err());
recorded outputcargo soothfast docs capture
second init is_err = true

Fetching FRED Series#

rust · no_run feature=fred
use finance_query::fred;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    fred::init("your-fred-api-key")?;

    // Fetch all observations for a series
    let cpi = fred::series("CPIAUCSL").await?;

    println!("Series: {}", cpi.id);
    println!("Observations: {}", cpi.observations.len());

    // Print the last 5 observations
    for obs in cpi.observations.iter().rev().take(5) {
        match obs.value {
            Some(v) => println!("{}: {:.2}", obs.date, v),
            None    => println!("{}: N/A", obs.date),
        }
    }
    Ok(())
}
checked claims
MacroSeriesverified current
de_fred_seriesmedian time 109.6 µs(limit < 300.0 µs)0limit 300.0 µs

Common FRED Series IDs:

Series IDDescription
"FEDFUNDS"Federal Funds Effective Rate
"CPIAUCSL"Consumer Price Index (all urban, seasonally adjusted)
"CPILFESL"Core CPI (less food and energy)
"UNRATE"Unemployment Rate
"GDP"Gross Domestic Product
"M2SL"M2 Money Supply
"DGS10"10-Year Treasury Constant Maturity Rate
"DGS2"2-Year Treasury Constant Maturity Rate
"T10Y2Y"10-Year minus 2-Year Treasury spread
"INDPRO"Industrial Production Index
"HOUST"Housing Starts
"PAYEMS"Total Nonfarm Payrolls
"PCE"Personal Consumption Expenditures

MacroSeries fields:

MacroObservation·verified current

MacroObservation fields:

Rate limit: 2 requests/second (enforced automatically).

Finding Series (EconomicCatalog)#

fred::series(id) and providers.economic(id) both require an id you already know. providers.economic_catalog() is how you find one:

rust,ignore
use finance_query::Providers;

let providers = Providers::builder().build().await?;
let catalog = providers.economic_catalog();

// Free-text search, most popular first.
for hit in catalog.search("real gross domestic product", 10).await? {
    println!("{} — {:?} ({:?})", hit.id, hit.title, hit.frequency);
}

// Browse the category tree; 0 is the root.
for cat in catalog.categories(0).await? {
    println!("{} {:?}", cat.id, cat.name);
}

// Every scheduled release FRED publishes.
let releases = catalog.releases().await?;

Point-in-Time Data (ALFRED vintages)#

FRED revises macro data after publication, so backtesting a rule against today's GDPC1 is look-ahead bias — the values it trades on were not knowable at the time. .as_of(date) asks for the vintage that was actually published:

rust,ignore
let gdp = providers.economic("GDPC1");

let revised = gdp.series().await?;              // as currently revised
let vintage = gdp.as_of("2020-06-30").await?;   // as published on that date

Both realtime bounds are pinned to date, so the response contains exactly the values in force that day rather than a range of revisions. Results are cached per date.

US Treasury Yields#

No initialization required. Fetches directly from the US Treasury Department:

rust · no_run feature=fred
use finance_query::fred;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Fetch the full yield curve for a given year
    let yields = fred::treasury_yields(2025).await?;

    // Print the most recent day
    if let Some(latest) = yields.last() {
        println!("Date: {}", latest.date);
        println!("2Y:  {:?}%", latest.y2);
        println!("5Y:  {:?}%", latest.y5);
        println!("10Y: {:?}%", latest.y10);
        println!("30Y: {:?}%", latest.y30);
    }
    Ok(())
}
checked claims
TreasuryYieldverified current

TreasuryYield fields (all yields are Option<f64> in %):

FieldMaturity
y1m1 month
y2m2 months
y3m3 months
y4m4 months
y6m6 months
y11 year
y22 years
y33 years
y55 years
y77 years
y1010 years
y2020 years
y3030 years

Dates are formatted as MM/DD/YYYY (the Treasury's native format). Fields are None on days when that maturity is not published.

Example: Yield Curve Inversion Check#

rust · no_run feature=fred
use finance_query::fred;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let yields = fred::treasury_yields(2025).await?;

    for y in yields.iter().rev().take(5) {
        if let (Some(y2), Some(y10)) = (y.y2, y.y10) {
            let spread = y10 - y2;
            let label = if spread < 0.0 { "INVERTED" } else { "normal" };
            println!("{}: 10Y-2Y spread = {:.2}bps ({})", y.date, spread * 100.0, label);
        }
    }
    Ok(())
}
checked claims
de_treasury_yieldsmedian time82.4 µs<200.0 µs

Next Steps#

built with cargo soothfast docs build source