2022-03-07 06:47:06 -08:00
|
|
|
use crate::DbAdapter;
|
2021-08-09 07:13:10 -07:00
|
|
|
use chrono::NaiveDateTime;
|
2022-03-07 06:47:06 -08:00
|
|
|
use zcash_params::coin::get_coin_chain;
|
2021-08-09 07:13:10 -07:00
|
|
|
|
2021-08-16 06:07:04 -07:00
|
|
|
const DAY_SEC: i64 = 24 * 3600;
|
2021-08-09 07:13:10 -07:00
|
|
|
|
2021-08-16 06:07:04 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Quote {
|
|
|
|
pub timestamp: i64,
|
|
|
|
pub price: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn fetch_historical_prices(
|
|
|
|
now: i64,
|
|
|
|
days: u32,
|
|
|
|
currency: &str,
|
|
|
|
db: &DbAdapter,
|
|
|
|
) -> anyhow::Result<Vec<Quote>> {
|
2022-03-07 06:47:06 -08:00
|
|
|
let chain = get_coin_chain(db.coin_type);
|
2021-09-06 17:53:54 -07:00
|
|
|
let json_error = || anyhow::anyhow!("Invalid JSON");
|
2021-08-16 06:07:04 -07:00
|
|
|
let today = now / DAY_SEC;
|
|
|
|
let from_day = today - days as i64;
|
|
|
|
let latest_quote = db.get_latest_quote(currency)?;
|
|
|
|
let latest_day = if let Some(latest_quote) = latest_quote {
|
|
|
|
latest_quote.timestamp / DAY_SEC
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
let latest_day = latest_day.max(from_day);
|
|
|
|
|
|
|
|
let mut quotes: Vec<Quote> = vec![];
|
|
|
|
if latest_day < today {
|
|
|
|
let from = (latest_day + 1) * DAY_SEC;
|
|
|
|
let to = today * DAY_SEC;
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let url = format!(
|
|
|
|
"https://api.coingecko.com/api/v3/coins/{}/market_chart/range",
|
2022-03-07 06:47:06 -08:00
|
|
|
chain.ticker()
|
2021-08-16 06:07:04 -07:00
|
|
|
);
|
|
|
|
let params = [
|
|
|
|
("from", from.to_string()),
|
|
|
|
("to", to.to_string()),
|
|
|
|
("vs_currency", currency.to_string()),
|
|
|
|
];
|
|
|
|
let req = client.get(url).query(¶ms);
|
|
|
|
let res = req.send().await?;
|
|
|
|
let r: serde_json::Value = res.json().await?;
|
2021-09-06 17:53:54 -07:00
|
|
|
let prices = r["prices"].as_array().ok_or_else(json_error)?;
|
2021-08-16 06:07:04 -07:00
|
|
|
let mut prev_timestamp = 0i64;
|
|
|
|
for p in prices.iter() {
|
2021-09-06 17:53:54 -07:00
|
|
|
let p = p.as_array().ok_or_else(json_error)?;
|
|
|
|
let ts = p[0].as_i64().ok_or_else(json_error)? / 1000;
|
|
|
|
let price = p[1].as_f64().ok_or_else(json_error)?;
|
2021-08-16 06:07:04 -07:00
|
|
|
// rounded to daily
|
|
|
|
let date = NaiveDateTime::from_timestamp(ts, 0).date().and_hms(0, 0, 0);
|
|
|
|
let timestamp = date.timestamp();
|
|
|
|
if timestamp != prev_timestamp {
|
|
|
|
let quote = Quote { timestamp, price };
|
|
|
|
quotes.push(quote);
|
|
|
|
}
|
|
|
|
prev_timestamp = timestamp;
|
2021-08-09 07:13:10 -07:00
|
|
|
}
|
|
|
|
}
|
2021-08-16 06:07:04 -07:00
|
|
|
|
|
|
|
Ok(quotes)
|
2021-08-09 07:13:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::db::DEFAULT_DB_PATH;
|
2021-08-16 06:07:04 -07:00
|
|
|
use crate::prices::fetch_historical_prices;
|
|
|
|
use crate::DbAdapter;
|
|
|
|
use std::time::SystemTime;
|
2022-03-07 06:47:06 -08:00
|
|
|
use zcash_params::coin::CoinType;
|
2021-08-09 07:13:10 -07:00
|
|
|
|
|
|
|
#[tokio::test]
|
2021-08-16 06:07:04 -07:00
|
|
|
async fn test_fetch_quotes() {
|
2021-08-09 07:13:10 -07:00
|
|
|
let currency = "EUR";
|
2022-04-19 09:47:08 -07:00
|
|
|
let mut db = DbAdapter::new(CoinType::Zcash, DEFAULT_DB_PATH).unwrap();
|
2021-08-16 06:07:04 -07:00
|
|
|
let now = SystemTime::now()
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs() as i64;
|
|
|
|
let quotes = fetch_historical_prices(now, 365, currency, &db)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
for q in quotes.iter() {
|
|
|
|
println!("{:?}", q);
|
|
|
|
}
|
|
|
|
db.store_historical_prices("es, currency).unwrap();
|
2021-08-09 07:13:10 -07:00
|
|
|
}
|
2021-08-16 06:07:04 -07:00
|
|
|
}
|