use crate::{
currency::Currency,
error::{Error, ErrorKind},
prelude::*,
sources::Sources,
trading_pair::TradingPair,
};
use rust_decimal::Decimal;
use serde::{de, ser, Deserialize, Serialize};
use std::{
convert::TryInto,
fmt::{self, Display},
str::FromStr,
};
use tokio::try_join;
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Denom {
Ukrw,
Umnt,
Usdr,
Uusd,
Ueur,
Ucny,
Ujpy,
Ugbp,
Uinr,
Ucad,
Uchf,
Uhkd,
Uaud,
Usgd,
Uthb,
Usek,
Udkk,
Uidr,
Uphp,
}
impl Denom {
pub fn kinds() -> &'static [Denom] {
&[
Denom::Ukrw,
Denom::Umnt,
Denom::Usdr,
Denom::Uusd,
Denom::Ueur,
Denom::Ucny,
Denom::Ujpy,
Denom::Ugbp,
Denom::Uinr,
Denom::Ucad,
Denom::Uchf,
Denom::Uhkd,
Denom::Uaud,
Denom::Usgd,
Denom::Uthb,
Denom::Usek,
Denom::Udkk,
Denom::Uidr,
Denom::Uphp,
]
}
pub fn as_str(self) -> &'static str {
match self {
Denom::Ukrw => "ukrw",
Denom::Umnt => "umnt",
Denom::Usdr => "usdr",
Denom::Uusd => "uusd",
Denom::Ueur => "ueur",
Denom::Ucny => "ucny",
Denom::Ujpy => "ujpy",
Denom::Ugbp => "ugbp",
Denom::Uinr => "uinr",
Denom::Ucad => "ucad",
Denom::Uchf => "uchf",
Denom::Uhkd => "uhkd",
Denom::Uaud => "uaud",
Denom::Usgd => "usgd",
Denom::Uthb => "uthb",
Denom::Usek => "usek",
Denom::Udkk => "udkk",
Denom::Uidr => "uidr",
Denom::Uphp => "uphp",
}
}
pub async fn get_exchange_rate(self, sources: &Sources) -> Result<stdtx::Decimal, Error> {
match self {
Denom::Ukrw => {
let bithumb_response = sources
.bithumb
.trading_pairs(&TradingPair(Currency::Luna, Currency::Krw))
.await?;
let mut luna_krw: Decimal = bithumb_response.into();
luna_krw.rescale(18);
Ok(luna_krw.try_into().map_err(|_| ErrorKind::Parse)?)
}
Denom::Umnt => {
let (
currencylayer_response_usd,
currencylayer_response_krw,
binance_response,
coinone_midpoint,
) = try_join!(
sources
.alphavantage
.trading_pairs(&TradingPair(Currency::Usd, Currency::Mnt)),
sources
.alphavantage
.trading_pairs(&TradingPair(Currency::Krw, Currency::Mnt)),
sources
.binance
.approx_price_for_pair(&TradingPair(Currency::Luna, Currency::Usd)),
sources
.coinone
.trading_pairs(&TradingPair(Currency::Luna, Currency::Krw))
)?;
let mut luna_mnt = Decimal::from(
(binance_response * currencylayer_response_usd
+ coinone_midpoint * currencylayer_response_krw)
/ 2,
);
luna_mnt.rescale(18);
Ok(luna_mnt.try_into().map_err(|_| ErrorKind::Parse)?)
}
Denom::Uusd => {
let binance_response = sources
.binance
.approx_price_for_pair(&TradingPair(Currency::Luna, Currency::Usd))
.await?;
let mut luna_usd: Decimal = binance_response.into();
luna_usd.rescale(18);
Ok(luna_usd.try_into().map_err(|_| ErrorKind::Parse)?)
}
Denom::Usdr => {
let (imf_sdr_response, coinone_midpoint) = try_join!(
sources
.alphavantage
.trading_pairs(&TradingPair(Currency::Krw, Currency::Sdr)),
sources
.coinone
.trading_pairs(&TradingPair(Currency::Luna, Currency::Krw))
)?;
let mut luna_sdr = Decimal::from(coinone_midpoint * imf_sdr_response);
luna_sdr.rescale(18);
Ok(luna_sdr.try_into().map_err(|_| ErrorKind::Parse)?)
}
_ => luna_rate_via_usd(sources, self.into()).await,
}
}
}
async fn luna_rate_via_usd(sources: &Sources, cur: Currency) -> Result<stdtx::Decimal, Error> {
let pair_1 = TradingPair(Currency::Usd, cur);
let (currencylayer_response_usd, binance_response) = try_join!(
sources.alphavantage.trading_pairs(&pair_1),
sources
.binance
.approx_price_for_pair(&TradingPair(Currency::Luna, Currency::Usd)),
)?;
let mut luna_cur = Decimal::from(binance_response * currencylayer_response_usd);
luna_cur.rescale(18);
Ok(luna_cur.try_into().map_err(|_| ErrorKind::Parse)?)
}
impl Display for Denom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Denom {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
match s.to_ascii_lowercase().as_ref() {
"ukrw" => Ok(Denom::Ukrw),
"umnt" => Ok(Denom::Umnt),
"usdr" => Ok(Denom::Usdr),
"uusd" => Ok(Denom::Uusd),
"ueur" => Ok(Denom::Ueur),
"ucny" => Ok(Denom::Ucny),
"ujpy" => Ok(Denom::Ujpy),
"ugbp" => Ok(Denom::Ugbp),
"uinr" => Ok(Denom::Uinr),
"ucad" => Ok(Denom::Ucad),
"uchf" => Ok(Denom::Uchf),
"uthb" => Ok(Denom::Uthb),
"usek" => Ok(Denom::Usek),
"udkk" => Ok(Denom::Udkk),
"uidr" => Ok(Denom::Uidr),
"uphp" => Ok(Denom::Uphp),
_ => fail!(ErrorKind::Currency, "unknown Terra denom: {}", s),
}
}
}
impl<'de> Deserialize<'de> for Denom {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use de::Error;
let s = String::deserialize(deserializer)?;
s.parse().map_err(D::Error::custom)
}
}
impl Serialize for Denom {
fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}