openlimits_nash/
lib.rs

1//! This module provides functionality for communicating with the nash API.
2
3
4mod nash_credentials;
5mod nash_parameters;
6mod nash_stream;
7mod utils;
8
9pub use nash_credentials::NashCredentials;
10pub use nash_parameters::NashParameters;
11pub use nash_stream::NashWebsocket;
12pub use utils::client_from_params_failable;
13pub use openlimits_exchange::shared;
14
15use std::convert::TryInto;
16use async_trait::async_trait;
17use nash_native_client::Client;
18use rust_decimal::prelude::*;
19use openlimits_exchange::{
20    errors::OpenLimitsError,
21    model::{
22        Balance, CancelAllOrdersRequest, CancelOrderRequest, Candle, GetHistoricRatesRequest,
23        GetHistoricTradesRequest, GetOrderHistoryRequest, GetOrderRequest, GetPriceTickerRequest,
24        OpenLimitOrderRequest, OpenMarketOrderRequest, Order,
25        OrderBookRequest, OrderBookResponse, OrderCanceled, Paginator,
26        Ticker, Trade, TradeHistoryRequest,
27    },
28};
29use openlimits_exchange::shared::Result;
30use openlimits_exchange::traits::info::ExchangeInfo;
31use openlimits_exchange::traits::info::ExchangeInfoRetrieval;
32use openlimits_exchange::traits::Exchange;
33use openlimits_exchange::traits::ExchangeMarketData;
34use openlimits_exchange::traits::ExchangeAccount;
35use openlimits_exchange::traits::info::MarketPairInfo;
36use openlimits_exchange::traits::info::MarketPairHandle;
37use openlimits_exchange::model::market_pair::MarketPair;
38use openlimits_exchange::MissingImplementationContent;
39
40/// This struct is the main struct of this module and it is used for communications with the nash openlimits-exchange
41pub struct Nash {
42    pub transport: Client,
43    pub exchange_info: ExchangeInfo,
44}
45
46#[async_trait]
47impl Exchange for Nash {
48    type InitParams = NashParameters;
49    type InnerClient = Client;
50
51    async fn new(params: Self::InitParams) -> Result<Self> {
52        let nash = Self {
53            exchange_info: ExchangeInfo::new(),
54            transport: client_from_params_failable(params).await?,
55        };
56        nash.refresh_market_info().await.ok();
57        Ok(nash)
58    }
59
60    fn inner_client(&self) -> Option<&Self::InnerClient> {
61        Some(&self.transport)
62    }
63}
64
65#[async_trait]
66impl ExchangeMarketData for Nash {
67    async fn get_historic_rates(&self, req: &GetHistoricRatesRequest) -> Result<Vec<Candle>> {
68        let req: nash_protocol::protocol::list_candles::ListCandlesRequest = req.try_into()?;
69
70        let resp = self.transport.run(req).await;
71
72        let resp: nash_protocol::protocol::list_candles::ListCandlesResponse =
73            Nash::unwrap_response::<nash_protocol::protocol::list_candles::ListCandlesResponse>(
74                resp,
75            )?;
76
77        Ok(resp.candles.into_iter().map(Into::into).collect())
78    }
79
80    async fn get_historic_trades(&self, req: &GetHistoricTradesRequest) -> Result<Vec<Trade>> {
81        let req: nash_protocol::protocol::list_trades::ListTradesRequest = req.try_into()?;
82        let resp = self.transport.run(req).await;
83
84        let resp: nash_protocol::protocol::list_trades::ListTradesResponse = Nash::unwrap_response::<
85            nash_protocol::protocol::list_trades::ListTradesResponse,
86        >(resp)?;
87
88        Ok(resp.trades.into_iter().map(Into::into).collect())
89    }
90
91    async fn get_price_ticker(&self, req: &GetPriceTickerRequest) -> Result<Ticker> {
92        let req: nash_protocol::protocol::get_ticker::TickerRequest = req.into();
93        let resp = self.transport.run(req).await;
94        Ok(
95            Nash::unwrap_response::<nash_protocol::protocol::get_ticker::TickerResponse>(resp)?
96                .into(),
97        )
98    }
99
100    async fn order_book(&self, req: &OrderBookRequest) -> Result<OrderBookResponse> {
101        let req: nash_protocol::protocol::orderbook::OrderbookRequest = req.into();
102        let resp = self.transport.run(req).await;
103        Ok(
104            Nash::unwrap_response::<nash_protocol::protocol::orderbook::OrderbookResponse>(resp)?
105                .into(),
106        )
107    }
108}
109
110#[async_trait]
111impl ExchangeAccount for Nash {
112    async fn cancel_all_orders(&self, req: &CancelAllOrdersRequest) -> Result<Vec<OrderCanceled>> {
113        let req: nash_protocol::protocol::cancel_all_orders::CancelAllOrders = req.into();
114        self.transport.run_http(req).await?;
115        Ok(vec![])
116    }
117
118    async fn cancel_order(&self, req: &CancelOrderRequest) -> Result<OrderCanceled> {
119        let req: nash_protocol::protocol::cancel_order::CancelOrderRequest = req.into();
120        let resp = self.transport.run_http(req).await;
121        Ok(
122            Nash::unwrap_response::<nash_protocol::protocol::cancel_order::CancelOrderResponse>(
123                resp,
124            )?
125            .into(),
126        )
127    }
128
129    async fn get_account_balances(&self, _paginator: Option<Paginator>) -> Result<Vec<Balance>> {
130        let req = nash_protocol::protocol::list_account_balances::ListAccountBalancesRequest {
131            filter: None,
132        };
133        let resp = self.transport.run(req).await;
134
135        let resp: nash_protocol::protocol::list_account_balances::ListAccountBalancesResponse =
136            Nash::unwrap_response::<
137                nash_protocol::protocol::list_account_balances::ListAccountBalancesResponse,
138            >(resp)?;
139
140        let mut balances = Vec::new();
141        for asset in resp.state_channel.keys() {
142            let free = Decimal::from_str(
143                &resp
144                    .state_channel
145                    .get(asset)
146                    .expect("Couldn't get asset.")
147                    .to_string(),
148            )
149            .expect("Couldn't parse Decimal from string.");
150            let in_orders = Decimal::from_str(
151                &resp
152                    .in_orders
153                    .get(asset)
154                    .expect("Couldn't get asset")
155                    .to_string(),
156            )
157            .expect("Couldn't parse Decimal from string.");
158            let total = free + in_orders;
159            balances.push(Balance {
160                asset: asset.name().to_string(),
161                total,
162                free,
163            });
164        }
165
166        Ok(balances)
167    }
168
169    async fn get_all_open_orders(&self) -> Result<Vec<Order>> {
170        let req = nash_protocol::protocol::list_account_orders::ListAccountOrdersRequest {
171            market: Default::default(),
172            before: None,
173            buy_or_sell: None,
174            limit: Some(100),
175            status: Some(vec![nash_protocol::types::OrderStatus::Open]),
176            order_type: None,
177            range: None,
178        };
179
180        let resp = self.transport.run(req).await;
181
182        let resp: nash_protocol::protocol::list_account_orders::ListAccountOrdersResponse =
183            Nash::unwrap_response::<
184                nash_protocol::protocol::list_account_orders::ListAccountOrdersResponse,
185            >(resp)?;
186
187        Ok(resp.orders.into_iter().map(Into::into).collect())
188    }
189
190    async fn get_order_history(&self, req: &GetOrderHistoryRequest) -> Result<Vec<Order>> {
191        let req: nash_protocol::protocol::list_account_orders::ListAccountOrdersRequest =
192            req.try_into()?;
193
194        let resp = self.transport.run(req).await;
195
196        let resp: nash_protocol::protocol::list_account_orders::ListAccountOrdersResponse =
197            Nash::unwrap_response::<
198                nash_protocol::protocol::list_account_orders::ListAccountOrdersResponse,
199            >(resp)?;
200
201        Ok(resp.orders.into_iter().map(Into::into).collect())
202    }
203
204    async fn get_trade_history(&self, req: &TradeHistoryRequest) -> Result<Vec<Trade>> {
205        let req: nash_protocol::protocol::list_account_trades::ListAccountTradesRequest =
206            req.try_into()?;
207
208        let resp = self.transport.run(req).await;
209
210        let resp: nash_protocol::protocol::list_account_trades::ListAccountTradesResponse =
211            Nash::unwrap_response::<
212                nash_protocol::protocol::list_account_trades::ListAccountTradesResponse,
213            >(resp)?;
214
215        Ok(resp.trades.into_iter().map(Into::into).collect())
216    }
217
218    async fn limit_buy(&self, req: &OpenLimitOrderRequest) -> Result<Order> {
219        let req: nash_protocol::protocol::place_order::LimitOrderRequest =
220            Nash::convert_limit_order(req, nash_protocol::types::BuyOrSell::Buy);
221
222        let resp = self.transport.run_http(req).await;
223
224        Ok(
225            Nash::unwrap_response::<nash_protocol::protocol::place_order::PlaceOrderResponse>(
226                resp,
227            )?
228            .into(),
229        )
230    }
231
232    async fn limit_sell(&self, req: &OpenLimitOrderRequest) -> Result<Order> {
233        let req: nash_protocol::protocol::place_order::LimitOrderRequest =
234            Nash::convert_limit_order(req, nash_protocol::types::BuyOrSell::Sell);
235        let resp = self.transport.run_http(req).await;
236
237        Ok(
238            Nash::unwrap_response::<nash_protocol::protocol::place_order::PlaceOrderResponse>(
239                resp,
240            )?
241            .into(),
242        )
243    }
244
245    async fn market_sell(&self, req: &OpenMarketOrderRequest) -> Result<Order> {
246        let req: nash_protocol::protocol::place_order::MarketOrderRequest =
247            Nash::convert_market_request(req);
248        println!("{:#?}", req);
249        let resp = self.transport.run_http(req).await;
250        Ok(
251            Nash::unwrap_response::<nash_protocol::protocol::place_order::PlaceOrderResponse>(
252                resp,
253            )?
254            .into(),
255        )
256    }
257
258    async fn market_buy(&self, _req: &OpenMarketOrderRequest) -> Result<Order> {
259        let message = "Nash client doesn't implement market_buy".into();
260        Err(OpenLimitsError::MissingImplementation(MissingImplementationContent { message }))
261    }
262
263    async fn get_order(&self, req: &GetOrderRequest) -> Result<Order> {
264        let req: nash_protocol::protocol::get_account_order::GetAccountOrderRequest = req.into();
265        let resp = self.transport.run(req).await;
266        let resp = Nash::unwrap_response::<
267            nash_protocol::protocol::get_account_order::GetAccountOrderResponse,
268        >(resp)?;
269        Ok(resp.order.into())
270    }
271}
272
273impl Nash {
274    pub fn unwrap_response<T>(
275        resp: std::result::Result<
276            nash_protocol::protocol::ResponseOrError<T>,
277            nash_protocol::errors::ProtocolError,
278        >,
279    ) -> Result<T> {
280        match resp {
281            Ok(resp) => resp
282                .response_or_error()
283                .map_err(|error| OpenLimitsError::Generic(Box::new(error))),
284            Err(error) => Err(OpenLimitsError::Generic(Box::new(error))),
285        }
286    }
287
288    pub fn convert_limit_order(
289        req: &OpenLimitOrderRequest,
290        buy_or_sell: nash_protocol::types::BuyOrSell,
291    ) -> nash_protocol::protocol::place_order::LimitOrderRequest {
292        let market = req.market_pair.clone();
293        let market = nash_protocol::types::market_pair::MarketPair::from(market).0;
294        nash_protocol::protocol::place_order::LimitOrderRequest {
295            client_order_id: req.client_order_id.clone(),
296            cancellation_policy: req.time_in_force.into(),
297            allow_taker: !req.post_only,
298            market,
299            buy_or_sell,
300            amount: format!("{}", req.size),
301            price: format!("{}", req.price),
302        }
303    }
304
305    pub fn convert_market_request(
306        req: &OpenMarketOrderRequest,
307    ) -> nash_protocol::protocol::place_order::MarketOrderRequest {
308        let market = req.market_pair.clone();
309        let market = nash_protocol::types::market_pair::MarketPair::from(market).0;
310        nash_protocol::protocol::place_order::MarketOrderRequest {
311            client_order_id: req.client_order_id.clone(),
312            market,
313            amount: format!("{}", req.size),
314        }
315    }
316
317    async fn list_markets(
318        &self,
319    ) -> Result<nash_protocol::protocol::list_markets::ListMarketsResponse> {
320        let response = self
321            .transport
322            .run(nash_protocol::protocol::list_markets::ListMarketsRequest)
323            .await?;
324        if let Some(err) = response.error() {
325            Err(OpenLimitsError::Generic(Box::new(err.clone())))
326        } else {
327            Ok(response
328                .consume_response()
329                .expect("Couldn't consume response.")) // safe unwrap
330        }
331    }
332}
333
334#[async_trait]
335impl ExchangeInfoRetrieval for Nash {
336    async fn retrieve_pairs(&self) -> Result<Vec<MarketPairInfo>> {
337        Ok(self
338            .list_markets()
339            .await?
340            .markets
341            .iter()
342            .map(|(symbol, v)| MarketPairInfo {
343                symbol: symbol.to_string(),
344                base: v.asset_a.asset.name().to_string(),
345                quote: v.asset_b.asset.name().to_string(),
346                base_increment: Decimal::new(1, v.asset_a.precision),
347                quote_increment: Decimal::new(1, v.asset_b.precision),
348                min_base_trade_size: Some(
349                    Decimal::from_str(&format!("{}", &v.min_trade_size_a.amount.value))
350                        .expect("Couldn't create Decimal from string."),
351                ),
352                min_quote_trade_size: Some(
353                    Decimal::from_str(&format!("{}", &v.min_trade_size_b.amount.value))
354                        .expect("Couldn't create Decimal from string."),
355                ),
356            })
357            .collect())
358    }
359
360    async fn refresh_market_info(&self) -> Result<Vec<MarketPairHandle>> {
361        self.exchange_info
362            .refresh(self as &dyn ExchangeInfoRetrieval)
363            .await
364    }
365
366    async fn get_pair(&self, name: &MarketPair) -> Result<MarketPairHandle> {
367        let name = nash_protocol::types::market_pair::MarketPair::from(name.clone()).0;
368        self.exchange_info.get_pair(&name)
369    }
370}