You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
3.1 KiB
104 lines
3.1 KiB
use std::pin::Pin; |
|
|
|
use chrono::NaiveDateTime; |
|
use futures_util::{SinkExt, Stream, TryStreamExt as _}; |
|
use reqwest::Url; |
|
use reqwest_websocket::Message; |
|
|
|
use crate::{ |
|
error::{AppError, AppResult}, |
|
models::{ |
|
Candle, CandleInterval, SubscriptionRequest, SubscriptionResponse, |
|
SubscriptionResponseData, Trade, |
|
}, |
|
}; |
|
|
|
use super::Market; |
|
|
|
pub struct PoloniexClient { |
|
rest_base_url: Url, |
|
ws_base_url: Url, |
|
} |
|
|
|
impl PoloniexClient { |
|
pub fn new(rest_base_url: &Url, ws_base_url: &Url) -> Self { |
|
Self { |
|
rest_base_url: rest_base_url.to_owned(), |
|
ws_base_url: ws_base_url.to_owned(), |
|
} |
|
} |
|
} |
|
|
|
#[async_trait] |
|
impl Market for PoloniexClient { |
|
async fn get_historical_candles( |
|
&self, |
|
pair: &str, |
|
interval: CandleInterval, |
|
start_date: NaiveDateTime, |
|
end_date: NaiveDateTime, |
|
limit: usize, |
|
) -> AppResult<Vec<Candle>> { |
|
let mut req = self |
|
.rest_base_url |
|
.join(&format!("/markets/{}/candles", pair))?; |
|
|
|
req.query_pairs_mut() |
|
.append_pair( |
|
"startTime", |
|
&start_date.and_utc().timestamp_millis().to_string(), |
|
) |
|
.append_pair( |
|
"endTime", |
|
&end_date.and_utc().timestamp_millis().to_string(), |
|
) |
|
.append_pair("interval", interval.as_ref()) |
|
.append_pair("limit", &limit.to_string()); |
|
|
|
let result = reqwest::get(req).await?; |
|
|
|
if let Err(e) = result.error_for_status_ref() { |
|
println!("{:?}", result.text().await); |
|
return Err(e.into()); |
|
}; |
|
result.json().await.map_err(AppError::from) |
|
} |
|
|
|
async fn recent_trades_stream( |
|
&self, |
|
pairs: &[String], |
|
) -> AppResult<Pin<Box<dyn Stream<Item = AppResult<Trade>> + Send>>> { |
|
let mut ws = reqwest_websocket::websocket(self.ws_base_url.clone()).await?; |
|
|
|
let req = SubscriptionRequest { |
|
event: "subscribe".to_string(), |
|
channel: vec!["trades".to_string()], |
|
symbols: pairs.to_vec(), |
|
}; |
|
|
|
ws.send(Message::Text(serde_json::to_string(&req)?)).await?; |
|
|
|
Ok(Box::pin(async_stream::stream! { |
|
while let Some(message) = ws.try_next().await? { |
|
match message { |
|
Message::Text(text) => { |
|
if let Ok(sub) = serde_json::from_str::<SubscriptionResponse>(&text) { |
|
println!("{sub:?}"); |
|
continue |
|
} |
|
|
|
let trades = serde_json::from_str::<SubscriptionResponseData<Trade>>(&text)?; |
|
for trade in trades.data { |
|
yield Ok(trade) |
|
} |
|
}, |
|
Message::Close {..} => { |
|
eprintln!("trades stream closed: {message:?}"); |
|
break |
|
}, |
|
m => eprintln!("unknown message {m:?}") |
|
} |
|
} |
|
})) |
|
} |
|
}
|
|
|