1use std::collections::HashSet;
2use std::sync::Arc;
3
4use maxt::{
5 AccountStream, Adapter, Balance, BoxFuture, Candle, CandleRequest, Exchange, Feature,
6 FundingPayment, FundingRate, HistoryRequest, MarginRequest, MarginSummary, Market, MarketInfo,
7 MarketKind, MarketStream, Order, OrderBook, OrderRequest, Page, Position, Result, StreamConfig,
8 Subscription, Ticker, Trade,
9};
10
11use crate::{AdapterCall, AdapterReply, ForeignDispatcher};
12
13pub struct ForeignAdapter {
15 exchange: Exchange,
16 features: HashSet<Feature>,
17 dispatcher: Arc<dyn ForeignDispatcher>,
18}
19
20impl ForeignAdapter {
21 pub fn new(
23 exchange: Exchange,
24 features: impl IntoIterator<Item = Feature>,
25 dispatcher: Arc<dyn ForeignDispatcher>,
26 ) -> Self {
27 Self {
28 exchange,
29 features: features.into_iter().collect(),
30 dispatcher,
31 }
32 }
33
34 pub fn features(&self) -> &HashSet<Feature> {
36 &self.features
37 }
38
39 pub fn dispatcher(&self) -> &dyn ForeignDispatcher {
41 self.dispatcher.as_ref()
42 }
43}
44
45macro_rules! dispatch {
46 ($self:expr, $call:expr, $variant:path, $expected:literal) => {{
47 let future = $self.dispatcher.dispatch($call);
48 Box::pin(async move {
49 match future.await? {
50 $variant(value) => Ok(value),
51 reply => Err(unexpected_reply($expected, &reply)),
52 }
53 })
54 }};
55}
56
57impl Adapter for ForeignAdapter {
58 fn exchange(&self) -> Exchange {
59 self.exchange
60 }
61
62 fn supports(&self, feature: Feature) -> bool {
63 self.features.contains(&feature)
64 }
65
66 fn markets(&self, kind: MarketKind) -> BoxFuture<'_, Result<Vec<MarketInfo>>> {
67 dispatch!(
68 self,
69 AdapterCall::Markets { kind },
70 AdapterReply::Markets,
71 "Markets"
72 )
73 }
74
75 fn trades(&self, market: &Market, limit: Option<u32>) -> BoxFuture<'_, Result<Vec<Trade>>> {
76 dispatch!(
77 self,
78 AdapterCall::Trades {
79 market: market.clone(),
80 limit,
81 },
82 AdapterReply::Trades,
83 "Trades"
84 )
85 }
86
87 fn order_book(&self, market: &Market, depth: Option<u32>) -> BoxFuture<'_, Result<OrderBook>> {
88 dispatch!(
89 self,
90 AdapterCall::OrderBook {
91 market: market.clone(),
92 depth,
93 },
94 AdapterReply::OrderBook,
95 "OrderBook"
96 )
97 }
98
99 fn ticker(&self, market: &Market) -> BoxFuture<'_, Result<Ticker>> {
100 dispatch!(
101 self,
102 AdapterCall::Ticker {
103 market: market.clone(),
104 },
105 AdapterReply::Ticker,
106 "Ticker"
107 )
108 }
109
110 fn candles(&self, request: &CandleRequest) -> BoxFuture<'_, Result<Vec<Candle>>> {
111 dispatch!(
112 self,
113 AdapterCall::Candles {
114 request: request.clone(),
115 },
116 AdapterReply::Candles,
117 "Candles"
118 )
119 }
120
121 fn subscribe(
122 &self,
123 subscription: &Subscription,
124 config: &StreamConfig,
125 ) -> BoxFuture<'_, Result<MarketStream>> {
126 dispatch!(
127 self,
128 AdapterCall::Subscribe {
129 subscription: subscription.clone(),
130 config: config.clone(),
131 },
132 AdapterReply::MarketStream,
133 "MarketStream"
134 )
135 }
136
137 fn balances(&self) -> BoxFuture<'_, Result<Vec<Balance>>> {
138 dispatch!(
139 self,
140 AdapterCall::Balances,
141 AdapterReply::Balances,
142 "Balances"
143 )
144 }
145
146 fn open_orders(&self, market: Option<&Market>) -> BoxFuture<'_, Result<Vec<Order>>> {
147 dispatch!(
148 self,
149 AdapterCall::OpenOrders {
150 market: market.cloned(),
151 },
152 AdapterReply::OpenOrders,
153 "OpenOrders"
154 )
155 }
156
157 fn subscribe_account(&self, config: &StreamConfig) -> BoxFuture<'_, Result<AccountStream>> {
158 dispatch!(
159 self,
160 AdapterCall::SubscribeAccount {
161 config: config.clone(),
162 },
163 AdapterReply::AccountStream,
164 "AccountStream"
165 )
166 }
167
168 fn place_order(&self, request: &OrderRequest) -> BoxFuture<'_, Result<Order>> {
169 dispatch!(
170 self,
171 AdapterCall::PlaceOrder {
172 request: request.clone(),
173 },
174 AdapterReply::PlaceOrder,
175 "PlaceOrder"
176 )
177 }
178
179 fn cancel_order(&self, market: &Market, order_id: &str) -> BoxFuture<'_, Result<Order>> {
180 dispatch!(
181 self,
182 AdapterCall::CancelOrder {
183 market: market.clone(),
184 order_id: order_id.to_owned(),
185 },
186 AdapterReply::CancelOrder,
187 "CancelOrder"
188 )
189 }
190
191 fn positions(&self, market: Option<&Market>) -> BoxFuture<'_, Result<Vec<Position>>> {
192 dispatch!(
193 self,
194 AdapterCall::Positions {
195 market: market.cloned(),
196 },
197 AdapterReply::Positions,
198 "Positions"
199 )
200 }
201
202 fn margin_summary(&self) -> BoxFuture<'_, Result<MarginSummary>> {
203 dispatch!(
204 self,
205 AdapterCall::MarginSummary,
206 AdapterReply::MarginSummary,
207 "MarginSummary"
208 )
209 }
210
211 fn funding_rates(&self, request: &HistoryRequest) -> BoxFuture<'_, Result<Page<FundingRate>>> {
212 dispatch!(
213 self,
214 AdapterCall::FundingRates {
215 request: request.clone(),
216 },
217 AdapterReply::FundingRates,
218 "FundingRates"
219 )
220 }
221
222 fn funding_payments(
223 &self,
224 request: &HistoryRequest,
225 ) -> BoxFuture<'_, Result<Page<FundingPayment>>> {
226 dispatch!(
227 self,
228 AdapterCall::FundingPayments {
229 request: request.clone(),
230 },
231 AdapterReply::FundingPayments,
232 "FundingPayments"
233 )
234 }
235
236 fn set_margin(&self, request: &MarginRequest) -> BoxFuture<'_, Result<()>> {
237 let future = self.dispatcher.dispatch(AdapterCall::SetMargin {
238 request: request.clone(),
239 });
240 Box::pin(async move {
241 match future.await? {
242 AdapterReply::Unit => Ok(()),
243 reply => Err(unexpected_reply("Unit", &reply)),
244 }
245 })
246 }
247}
248
249fn unexpected_reply(expected: &str, reply: &AdapterReply) -> maxt::Error {
250 maxt::Error::adapter(format!(
251 "foreign dispatcher returned {} where {expected} was required",
252 reply.kind()
253 ))
254}