Skip to main content

ExchangeRate

Struct ExchangeRate 

Source
pub struct ExchangeRate { /* private fields */ }
Expand description

The rate at which one currency buys another.

A rate works in one direction only: the rate from dollars to euros is not the rate back. It multiplies, so it is always positive. The currency being priced is the base, and the currency it is priced in is the quote. Two rates that share a currency combine with cross_with, which is how a pair nobody prices directly gets priced.

§Example

use lucre::{Currency, ExchangeRate, Money};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(
    rate.convert(Money::from_major(100, Currency::USD))?,
    Money::from_major(90, Currency::EUR)
);

Implementations§

Source§

impl ExchangeRate

Source

pub fn new<P: Into<Pair>, R: Into<Decimal>>( pair: P, rate: R, ) -> Result<Self, ExchangeRateError>

Quote a rate over a currency pair.

The pair may be given as a Pair or as the two currencies alone, base first. The rate multiplies an amount in the base currency to give an amount in the quote currency. A currency may be priced against itself, though identity says that more plainly.

§Example
use lucre::{Currency, ExchangeRate, Pair};
use rust_decimal::dec;

let quoted = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(quoted, ExchangeRate::new("USD/EUR".parse::<Pair>()?, dec!(0.9))?);
assert!(ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0)).is_err());
§Errors

Returns ExchangeRateError::InvalidRate if the rate is zero or negative.

Source

pub fn identity(currency: Currency) -> Self

The rate of a currency against itself. Converting leaves an amount unchanged.

§Example
use lucre::{Currency, ExchangeRate, Money};

let fare = Money::from_minor(275, Currency::USD);

assert_eq!(ExchangeRate::identity(Currency::USD).convert(fare)?, fare);
Source

pub fn base(self) -> Currency

The currency being priced.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(rate.base(), Currency::USD);
Source

pub fn quote(self) -> Currency

The currency it is priced in.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(rate.quote(), Currency::EUR);
Source

pub fn pair(self) -> Pair

Both currencies together, base first.

§Example
use lucre::{Currency, ExchangeRate, Pair};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(rate.pair(), Pair::new(Currency::USD, Currency::EUR));
Source

pub fn rate(self) -> Decimal

The multiplier.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(rate.rate(), dec!(0.9));
Source

pub fn convert(self, money: Money) -> Result<Money, ConvertError>

Restate an amount in the quote currency.

The result keeps every digit the multiplication produced. Rounding to the currency’s minor units is Money::round’s job.

§Example
use lucre::{ConvertError, Currency, ExchangeRate, Money};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(
    rate.convert(Money::from_minor(2550, Currency::USD))?,
    Money::from_decimal(dec!(22.950), Currency::EUR)
);
assert!(matches!(
    rate.convert(Money::from_major(10, Currency::GBP)),
    Err(ConvertError::CurrencyMismatch {
        base: Currency::USD,
        found: Currency::GBP,
        ..
    })
));
§Errors

Returns ConvertError::CurrencyMismatch if the amount is not in the base currency. Returns ConvertError::Overflow if the result is too large for a Decimal.

Source

pub fn cross_with(self, other: Self) -> Result<Self, CrossRateError>

Combine two rates that share a currency.

This rate’s quote currency must be other’s base currency. The result is one rate from this rate’s base to other’s quote, which is the usual way to price a pair quoted only against some third currency.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let usd_eur = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;
let eur_jpy = ExchangeRate::new((Currency::EUR, Currency::JPY), dec!(160))?;
let usd_jpy = usd_eur.cross_with(eur_jpy)?;

assert_eq!(usd_jpy.base(), Currency::USD);
assert_eq!(usd_jpy.quote(), Currency::JPY);
assert_eq!(usd_jpy.rate(), dec!(144.0));
§Errors

Returns CrossRateError::CurrencyMismatch if this rate’s quote currency is not other’s base currency. Returns CrossRateError::Overflow if the combined rate is too large for a Decimal, and CrossRateError::Rate if the combined rate cannot be quoted.

Source

pub fn invert(self) -> Result<Self, InvertError>

The rate back the other way, over the inverted pair.

The multiplier becomes one divided by this one, so the result prices the quote currency in the base. This treats both directions as priced alike. A desk that charges a spread quotes each direction separately instead.

Dividing keeps 28 significant digits, so inverting twice need not give back the rate it started from.

§Example
use lucre::{Currency, ExchangeRate, Money};
use rust_decimal::dec;

let usd_eur = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.8))?;
let eur_usd = usd_eur.invert()?;

assert_eq!(eur_usd.base(), Currency::EUR);
assert_eq!(eur_usd.quote(), Currency::USD);
assert_eq!(eur_usd.rate(), dec!(1.25));
assert_eq!(
    eur_usd.convert(Money::from_major(80, Currency::EUR))?,
    Money::from_major(100, Currency::USD)
);
§Errors

Returns InvertError::Underflow if the multiplier is 2e28 or above, since one divided by it rounds away to zero.

Trait Implementations§

Source§

impl Clone for ExchangeRate

Source§

fn clone(&self) -> ExchangeRate

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for ExchangeRate

Source§

impl Debug for ExchangeRate

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ExchangeRate

Available on crate feature serde only.

Reads all three fields, in any order. All three are required, and any other field is skipped, such as the time of the quote. The rate must pass the same check as ExchangeRate::new: a rate of zero or less is rejected.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let quoted = r#"{"as_of": "2026-08-14", "base": "USD", "quote": "EUR", "rate": 0.9}"#;

assert_eq!(
    serde_json::from_str::<ExchangeRate>(quoted)?,
    ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?
);
assert!(
    serde_json::from_str::<ExchangeRate>(r#"{"base": "USD", "quote": "EUR", "rate": 0}"#)
        .is_err()
);
Source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for ExchangeRate

Writes the pair, a space, and the multiplier. The multiplier keeps the digits it was quoted with, since a rate has no minor units to pad out to.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(rate.to_string(), "USD/EUR 0.9");

// Width, fill, and alignment flags are honored. The default is right
// alignment, as it is for an amount.
assert_eq!(format!("{rate:>13}"), "  USD/EUR 0.9");
Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for ExchangeRate

Source§

impl<'a> Extend<&'a ExchangeRate> for Exchange

Source§

fn extend<I: IntoIterator<Item = &'a ExchangeRate>>(&mut self, iter: I)

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: A)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl Extend<ExchangeRate> for Exchange

Adds each rate as set_rate does, so a pair quoted more than once keeps the rate given last. This holds whether the rate it displaces came from the same batch or was already in the table.

§Example
use lucre::{Currency, Exchange, ExchangeRate};
use rust_decimal::dec;

let mut desk = Exchange::new();
desk.set_rate(ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?);
desk.extend([
    ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.92))?,
    ExchangeRate::new((Currency::EUR, Currency::JPY), dec!(160))?,
]);

assert_eq!(
    desk.rate((Currency::USD, Currency::EUR)).map(|rate| rate.rate()),
    Some(dec!(0.92))
);
assert_eq!(desk.iter().len(), 2);
Source§

fn extend<I: IntoIterator<Item = ExchangeRate>>(&mut self, iter: I)

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: A)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl<'a> FromIterator<&'a ExchangeRate> for Exchange

Source§

fn from_iter<I: IntoIterator<Item = &'a ExchangeRate>>(iter: I) -> Self

Creates a value from an iterator. Read more
Source§

impl FromIterator<ExchangeRate> for Exchange

Collects rates into a table, adding each one as Extend does.

§Example
use lucre::{Currency, Exchange, ExchangeRate, Money};
use rust_decimal::dec;

let feed = [
    ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?,
    ExchangeRate::new((Currency::EUR, Currency::JPY), dec!(160))?,
];

let desk: Exchange = feed.into_iter().collect();

assert_eq!(
    desk.convert(Money::from_major(100, Currency::USD), Currency::EUR)?,
    Money::from_major(90, Currency::EUR)
);
Source§

fn from_iter<I: IntoIterator<Item = ExchangeRate>>(iter: I) -> Self

Creates a value from an iterator. Read more
Source§

impl Hash for ExchangeRate

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for ExchangeRate

Source§

fn eq(&self, other: &ExchangeRate) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for ExchangeRate

Available on crate feature serde only.

Writes three named fields: the two currencies of the pair and the rate between them.

§Example
use lucre::{Currency, ExchangeRate};
use rust_decimal::dec;

let rate = ExchangeRate::new((Currency::USD, Currency::EUR), dec!(0.9))?;

assert_eq!(
    serde_json::to_string(&rate)?,
    r#"{"base":"USD","quote":"EUR","rate":"0.9"}"#
);
Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for ExchangeRate

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.