1use std::collections::HashSet;
2use std::sync::Arc;
3
4use maxt::{
5 AccountStream, Adapter, AssetNetwork, Balance, BoxFuture, CancelOrdersRequest,
6 CancelOrdersResult, Candle, CandleRequest, Deposit, DepositAddress, DepositAddressEntry,
7 DepositAddressRequest, Exchange, Feature, FundingPayment, FundingRate, HistoryRequest,
8 MarginRequest, MarginSummary, Market, MarketInfo, MarketKind, MarketStream, Order, OrderBook,
9 OrderHistoryRequest, OrderLookupRequest, OrderRequest, OrderRules, Page, Position, Result,
10 StreamConfig, Subscription, Ticker, Trade, TransferHistoryRequest, TransferLookupRequest,
11 WithdrawRequest, Withdrawal, WithdrawalQuote,
12};
13
14use crate::{AdapterCall, AdapterReply, ForeignDispatcher};
15
16pub struct ForeignAdapter {
18 exchange: Exchange,
19 features: HashSet<Feature>,
20 dispatcher: Arc<dyn ForeignDispatcher>,
21}
22
23impl ForeignAdapter {
24 pub fn new(
26 exchange: Exchange,
27 features: impl IntoIterator<Item = Feature>,
28 dispatcher: Arc<dyn ForeignDispatcher>,
29 ) -> Self {
30 Self {
31 exchange,
32 features: features.into_iter().collect(),
33 dispatcher,
34 }
35 }
36
37 pub fn features(&self) -> &HashSet<Feature> {
39 &self.features
40 }
41
42 pub fn dispatcher(&self) -> &dyn ForeignDispatcher {
44 self.dispatcher.as_ref()
45 }
46}
47
48macro_rules! dispatch {
49 ($self:expr, $call:expr, $variant:path, $expected:literal) => {{
50 let future = $self.dispatcher.dispatch($call);
51 Box::pin(async move {
52 match future.await? {
53 $variant(value) => Ok(value),
54 reply => Err(unexpected_reply($expected, &reply)),
55 }
56 })
57 }};
58}
59
60impl Adapter for ForeignAdapter {
61 fn exchange(&self) -> Exchange {
62 self.exchange
63 }
64
65 fn supports(&self, feature: Feature) -> bool {
66 self.features.contains(&feature)
67 }
68
69 fn markets(&self, kind: MarketKind) -> BoxFuture<'_, Result<Vec<MarketInfo>>> {
70 dispatch!(
71 self,
72 AdapterCall::Markets { kind },
73 AdapterReply::Markets,
74 "Markets"
75 )
76 }
77
78 fn trades(&self, market: &Market, limit: Option<u32>) -> BoxFuture<'_, Result<Vec<Trade>>> {
79 dispatch!(
80 self,
81 AdapterCall::Trades {
82 market: market.clone(),
83 limit,
84 },
85 AdapterReply::Trades,
86 "Trades"
87 )
88 }
89
90 fn order_book(&self, market: &Market, depth: Option<u32>) -> BoxFuture<'_, Result<OrderBook>> {
91 dispatch!(
92 self,
93 AdapterCall::OrderBook {
94 market: market.clone(),
95 depth,
96 },
97 AdapterReply::OrderBook,
98 "OrderBook"
99 )
100 }
101
102 fn ticker(&self, market: &Market) -> BoxFuture<'_, Result<Ticker>> {
103 dispatch!(
104 self,
105 AdapterCall::Ticker {
106 market: market.clone(),
107 },
108 AdapterReply::Ticker,
109 "Ticker"
110 )
111 }
112
113 fn candles(&self, request: &CandleRequest) -> BoxFuture<'_, Result<Vec<Candle>>> {
114 dispatch!(
115 self,
116 AdapterCall::Candles {
117 request: request.clone(),
118 },
119 AdapterReply::Candles,
120 "Candles"
121 )
122 }
123
124 fn subscribe(
125 &self,
126 subscription: &Subscription,
127 config: &StreamConfig,
128 ) -> BoxFuture<'_, Result<MarketStream>> {
129 dispatch!(
130 self,
131 AdapterCall::Subscribe {
132 subscription: subscription.clone(),
133 config: config.clone(),
134 },
135 AdapterReply::MarketStream,
136 "MarketStream"
137 )
138 }
139
140 fn balances(&self) -> BoxFuture<'_, Result<Vec<Balance>>> {
141 dispatch!(
142 self,
143 AdapterCall::Balances,
144 AdapterReply::Balances,
145 "Balances"
146 )
147 }
148
149 fn order_rules(&self, market: &Market) -> BoxFuture<'_, Result<OrderRules>> {
150 let future = self.dispatcher.dispatch(AdapterCall::OrderRules {
151 market: market.clone(),
152 });
153 Box::pin(async move {
154 match future.await? {
155 AdapterReply::OrderRules(value) => Ok(*value),
156 reply => Err(unexpected_reply("OrderRules", &reply)),
157 }
158 })
159 }
160
161 fn asset_networks(&self, asset: &str) -> BoxFuture<'_, Result<Vec<AssetNetwork>>> {
162 dispatch!(
163 self,
164 AdapterCall::AssetNetworks {
165 asset: asset.to_owned(),
166 },
167 AdapterReply::AssetNetworks,
168 "AssetNetworks"
169 )
170 }
171
172 fn deposit_addresses(&self) -> BoxFuture<'_, Result<Vec<DepositAddressEntry>>> {
173 dispatch!(
174 self,
175 AdapterCall::DepositAddresses,
176 AdapterReply::DepositAddresses,
177 "DepositAddresses"
178 )
179 }
180
181 fn deposit_address(
182 &self,
183 request: &DepositAddressRequest,
184 ) -> BoxFuture<'_, Result<DepositAddress>> {
185 dispatch!(
186 self,
187 AdapterCall::DepositAddress {
188 request: request.clone(),
189 },
190 AdapterReply::DepositAddress,
191 "DepositAddress"
192 )
193 }
194
195 fn create_deposit_address(
196 &self,
197 request: &DepositAddressRequest,
198 ) -> BoxFuture<'_, Result<DepositAddress>> {
199 dispatch!(
200 self,
201 AdapterCall::CreateDepositAddress {
202 request: request.clone(),
203 },
204 AdapterReply::CreateDepositAddress,
205 "CreateDepositAddress"
206 )
207 }
208
209 fn prepare_withdrawal(
210 &self,
211 request: &WithdrawRequest,
212 ) -> BoxFuture<'_, Result<WithdrawalQuote>> {
213 dispatch!(
214 self,
215 AdapterCall::PrepareWithdrawal {
216 request: request.clone(),
217 },
218 AdapterReply::WithdrawalQuote,
219 "WithdrawalQuote"
220 )
221 }
222
223 fn withdraw(&self, request: &WithdrawRequest) -> BoxFuture<'_, Result<Withdrawal>> {
224 dispatch!(
225 self,
226 AdapterCall::Withdraw {
227 request: request.clone(),
228 },
229 AdapterReply::Withdrawal,
230 "Withdrawal"
231 )
232 }
233
234 fn deposit(&self, request: &TransferLookupRequest) -> BoxFuture<'_, Result<Deposit>> {
235 dispatch!(
236 self,
237 AdapterCall::Deposit {
238 request: request.clone(),
239 },
240 AdapterReply::Deposit,
241 "Deposit"
242 )
243 }
244
245 fn withdrawal(&self, request: &TransferLookupRequest) -> BoxFuture<'_, Result<Withdrawal>> {
246 dispatch!(
247 self,
248 AdapterCall::Withdrawal {
249 request: request.clone(),
250 },
251 AdapterReply::LookupWithdrawal,
252 "LookupWithdrawal"
253 )
254 }
255
256 fn cancel_withdrawal(&self, withdrawal_id: &str) -> BoxFuture<'_, Result<()>> {
257 let future = self.dispatcher.dispatch(AdapterCall::CancelWithdrawal {
258 withdrawal_id: withdrawal_id.to_owned(),
259 });
260 Box::pin(async move {
261 match future.await? {
262 AdapterReply::Unit => Ok(()),
263 reply => Err(unexpected_reply("Unit", &reply)),
264 }
265 })
266 }
267
268 fn deposits(&self, request: &TransferHistoryRequest) -> BoxFuture<'_, Result<Page<Deposit>>> {
269 dispatch!(
270 self,
271 AdapterCall::Deposits {
272 request: request.clone(),
273 },
274 AdapterReply::Deposits,
275 "Deposits"
276 )
277 }
278
279 fn withdrawals(
280 &self,
281 request: &TransferHistoryRequest,
282 ) -> BoxFuture<'_, Result<Page<Withdrawal>>> {
283 dispatch!(
284 self,
285 AdapterCall::Withdrawals {
286 request: request.clone(),
287 },
288 AdapterReply::Withdrawals,
289 "Withdrawals"
290 )
291 }
292
293 fn open_orders(&self, market: Option<&Market>) -> BoxFuture<'_, Result<Vec<Order>>> {
294 dispatch!(
295 self,
296 AdapterCall::OpenOrders {
297 market: market.cloned(),
298 },
299 AdapterReply::OpenOrders,
300 "OpenOrders"
301 )
302 }
303
304 fn order(&self, market: &Market, order_id: &str) -> BoxFuture<'_, Result<Order>> {
305 dispatch!(
306 self,
307 AdapterCall::Order {
308 market: market.clone(),
309 order_id: order_id.to_owned(),
310 },
311 AdapterReply::Order,
312 "Order"
313 )
314 }
315
316 fn order_by_client_id(&self, market: &Market, client_id: &str) -> BoxFuture<'_, Result<Order>> {
317 dispatch!(
318 self,
319 AdapterCall::OrderByClientId {
320 market: market.clone(),
321 client_id: client_id.to_owned(),
322 },
323 AdapterReply::Order,
324 "Order"
325 )
326 }
327
328 fn orders_by_ids(&self, request: &OrderLookupRequest) -> BoxFuture<'_, Result<Vec<Order>>> {
329 dispatch!(
330 self,
331 AdapterCall::OrdersByIds {
332 request: request.clone(),
333 },
334 AdapterReply::OrdersByIds,
335 "OrdersByIds"
336 )
337 }
338
339 fn order_history(&self, request: &OrderHistoryRequest) -> BoxFuture<'_, Result<Page<Order>>> {
340 dispatch!(
341 self,
342 AdapterCall::OrderHistory {
343 request: request.clone(),
344 },
345 AdapterReply::OrderHistory,
346 "OrderHistory"
347 )
348 }
349
350 fn subscribe_account(&self, config: &StreamConfig) -> BoxFuture<'_, Result<AccountStream>> {
351 dispatch!(
352 self,
353 AdapterCall::SubscribeAccount {
354 config: config.clone(),
355 },
356 AdapterReply::AccountStream,
357 "AccountStream"
358 )
359 }
360
361 fn place_order(&self, request: &OrderRequest) -> BoxFuture<'_, Result<Order>> {
362 dispatch!(
363 self,
364 AdapterCall::PlaceOrder {
365 request: request.clone(),
366 },
367 AdapterReply::PlaceOrder,
368 "PlaceOrder"
369 )
370 }
371
372 fn cancel_order(&self, market: &Market, order_id: &str) -> BoxFuture<'_, Result<()>> {
373 let future = self.dispatcher.dispatch(AdapterCall::CancelOrder {
374 market: market.clone(),
375 order_id: order_id.to_owned(),
376 });
377 Box::pin(async move {
378 match future.await? {
379 AdapterReply::Unit => Ok(()),
380 reply => Err(unexpected_reply("Unit", &reply)),
381 }
382 })
383 }
384
385 fn cancel_order_by_client_id(
386 &self,
387 market: &Market,
388 client_id: &str,
389 ) -> BoxFuture<'_, Result<()>> {
390 let future = self
391 .dispatcher
392 .dispatch(AdapterCall::CancelOrderByClientId {
393 market: market.clone(),
394 client_id: client_id.to_owned(),
395 });
396 Box::pin(async move {
397 match future.await? {
398 AdapterReply::Unit => Ok(()),
399 reply => Err(unexpected_reply("Unit", &reply)),
400 }
401 })
402 }
403
404 fn cancel_orders(
405 &self,
406 request: &CancelOrdersRequest,
407 ) -> BoxFuture<'_, Result<CancelOrdersResult>> {
408 dispatch!(
409 self,
410 AdapterCall::CancelOrders {
411 request: request.clone(),
412 },
413 AdapterReply::CancelOrdersResult,
414 "CancelOrdersResult"
415 )
416 }
417
418 fn positions(&self, market: Option<&Market>) -> BoxFuture<'_, Result<Vec<Position>>> {
419 dispatch!(
420 self,
421 AdapterCall::Positions {
422 market: market.cloned(),
423 },
424 AdapterReply::Positions,
425 "Positions"
426 )
427 }
428
429 fn margin_summary(&self) -> BoxFuture<'_, Result<MarginSummary>> {
430 dispatch!(
431 self,
432 AdapterCall::MarginSummary,
433 AdapterReply::MarginSummary,
434 "MarginSummary"
435 )
436 }
437
438 fn funding_rates(&self, request: &HistoryRequest) -> BoxFuture<'_, Result<Page<FundingRate>>> {
439 dispatch!(
440 self,
441 AdapterCall::FundingRates {
442 request: request.clone(),
443 },
444 AdapterReply::FundingRates,
445 "FundingRates"
446 )
447 }
448
449 fn funding_payments(
450 &self,
451 request: &HistoryRequest,
452 ) -> BoxFuture<'_, Result<Page<FundingPayment>>> {
453 dispatch!(
454 self,
455 AdapterCall::FundingPayments {
456 request: request.clone(),
457 },
458 AdapterReply::FundingPayments,
459 "FundingPayments"
460 )
461 }
462
463 fn set_margin(&self, request: &MarginRequest) -> BoxFuture<'_, Result<()>> {
464 let future = self.dispatcher.dispatch(AdapterCall::SetMargin {
465 request: request.clone(),
466 });
467 Box::pin(async move {
468 match future.await? {
469 AdapterReply::Unit => Ok(()),
470 reply => Err(unexpected_reply("Unit", &reply)),
471 }
472 })
473 }
474}
475
476fn unexpected_reply(expected: &str, reply: &AdapterReply) -> maxt::Error {
477 maxt::Error::adapter(format!(
478 "foreign dispatcher returned {} where {expected} was required",
479 reply.kind()
480 ))
481}