use super::CurrencyPair;
use crate::fx::currency::Currency;
use crate::fx::money::Money;
use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct Exchange {
pub rates: HashMap<CurrencyPair, ExchangeRate>,
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Copy)]
pub struct ExchangeRate {
pub from_currency: Currency,
pub to_currency: Currency,
pub rate: f64,
}
impl Exchange {
#[must_use]
pub fn new() -> Self {
Self {
rates: HashMap::new(),
}
}
pub fn add_rate(&mut self, rate: ExchangeRate) {
let key = CurrencyPair::new(rate.from_currency, rate.to_currency);
self.rates.insert(key, rate);
}
#[must_use]
pub fn get_rate(
&self,
from_currency: &Currency,
to_currency: &Currency,
) -> Option<&ExchangeRate> {
let key = CurrencyPair::new(*from_currency, *to_currency);
self.rates.get(&key)
}
#[must_use]
pub fn convert(&self, money: Money, to_currency: Currency) -> Money {
let rate = self
.get_rate(&money.currency, &to_currency)
.unwrap_or_else(|| {
panic!(
"Exchange rate for converting {} to {} not found.",
money.currency.code.alphabetic, to_currency.code.alphabetic
)
});
rate.convert(money)
}
}
impl ExchangeRate {
#[must_use]
pub fn new(from_currency: Currency, to_currency: Currency, rate: f64) -> Self {
Self {
from_currency,
to_currency,
rate,
}
}
#[must_use]
pub fn convert(&self, money: Money) -> Money {
if money.currency == self.from_currency {
let new_amount = money.amount * self.rate;
Money::new(self.to_currency, new_amount)
} else {
panic!(
"The currency of the money doesn't match with from_currency of the exchange rate."
)
}
}
}
#[cfg(test)]
mod test_exchange_rate {
use super::*;
use crate::fx::*;
use crate::fx::{EUR, USD};
use std::f64::EPSILON as EPS;
use RustQuant_utils::assert_approx_equal;
#[test]
fn test_conversion() {
let usd_100 = Money::new(USD, 100.0);
let usd_to_eur = ExchangeRate::new(USD, EUR, 0.85);
let eur_85 = usd_to_eur.convert(usd_100);
assert_eq!(eur_85.currency, EUR);
assert_approx_equal!(eur_85.amount, 85.0, EPS);
}
#[test]
fn test_add_and_get_rate() {
let mut exchange = Exchange::new();
let usd_to_eur = ExchangeRate::new(USD, EUR, 0.85); let eur_to_usd = ExchangeRate::new(EUR, USD, 1.18);
exchange.add_rate(usd_to_eur);
exchange.add_rate(eur_to_usd);
let retrieved_usd_to_eur = exchange.get_rate(&USD, &EUR).expect("Rate not found");
assert_approx_equal!(retrieved_usd_to_eur.rate, 0.85, EPS);
let retrieved_eur_to_usd = exchange.get_rate(&EUR, &USD).expect("Rate not found");
assert_approx_equal!(retrieved_eur_to_usd.rate, 1.18, EPS);
}
#[test]
fn test_conversion_with_exchange() {
let mut exchange = Exchange::new();
let usd_to_eur = ExchangeRate::new(USD, EUR, 0.85); let eur_to_usd = ExchangeRate::new(EUR, USD, 1.18);
exchange.add_rate(usd_to_eur);
exchange.add_rate(eur_to_usd);
let usd_100 = Money::new(USD, 100.0); let eur_85 = exchange.convert(usd_100, EUR);
assert_eq!(eur_85.currency, EUR);
assert_approx_equal!(eur_85.amount, 85.0, EPS);
}
}