1mod 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
40pub 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.")) }
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}