pub struct BinanceSpot { /* private fields */ }Expand description
BinanceSpot execution client using the official binance-sdk.
- REST API: account snapshot, balance/order/trade queries (startup/cold paths)
- WebSocket API: order placement, order cancellation, user data stream (hot paths)
§Rate limits
REST and the WebSocket API share one per-IP request-weight limit per minute. Its value comes
from each WebSocket API response, and is Binance’s documented 6000 until the first one. Once
a response reports at least 90% of it used, REST queries wait for the next minute, so the
rest is left for orders and cancels, which never wait for it. A query, including a
fetch_open_orders after a reconnect, can therefore take up to
about a minute longer. A reconnect’s fill recovery does not wait for it either, since a pause
could outlast the recovery’s 30 s and delay its fills to a retry. After Binance answers with a
rate-limit error, REST calls wait out a cooldown, and an order or cancel that would have to
open a new WebSocket API session fails with ApiError::RateLimit instead, unsent.
Trait Implementations§
Source§impl Clone for BinanceSpot
impl Clone for BinanceSpot
Source§impl Debug for BinanceSpot
impl Debug for BinanceSpot
Source§impl ExecutionClient for BinanceSpot
impl ExecutionClient for BinanceSpot
Source§fn new(config: Self::Config) -> Self
fn new(config: Self::Config) -> Self
§Panics
Panics if the binance-sdk REST or WebSocket configuration builder fails (e.g. empty or malformed API key/secret).
Source§async fn account_stream(
&self,
_assets: &[AssetNameExchange],
instruments: &[InstrumentNameExchange],
) -> Result<Self::AccountStream, UnindexedClientError>
async fn account_stream( &self, _assets: &[AssetNameExchange], instruments: &[InstrumentNameExchange], ) -> Result<Self::AccountStream, UnindexedClientError>
Returns a live stream of account events (fills, order updates, balance changes).
§Startup Race Window
There is a brief window between the initial WS subscribe response and the
internal event listener being registered inside the spawned connection manager.
TRADE fills arriving in this gap are silently dropped. Callers that require fill
completeness at startup MUST call ExecutionClient::fetch_trades with a
1–2 second lookback unconditionally after account_stream returns, before
processing any events. Do not use the first event’s arrival as a readiness
signal — the first event may be a recovered fill sent before live WS events
start. The dedup cache absorbs any duplicates from the overlapping time window.
Callers MUST also call ExecutionClient::fetch_open_orders after each
reconnect to reconcile open-order state — order lifecycle events (NEW, CANCELED)
are not recovered after a WS disconnect, only TRADE fills are.
§A recovery that does not finish
Recovery after a reconnect is bounded by a 30 s timeout, and an instrument’s query can
fail. Each instrument’s gap, from the disconnect to just after the recovery began, is kept
until its fills are forwarded. A gap not read is retried after 1, 2, 4, 8 and 16 minutes,
whether the stream stays connected or reconnects in between. A retry reads only the gap,
not fills the stream delivered live after it; the few seconds at its end that overlap live
delivery are deduplicated. Each failed read is logged at warn with the instrument and the
gap. After five failed retries the gap is given up, logged at error, and its fills are not
delivered: read them with ExecutionClient::fetch_trades.
A recovered gap counts as read even when a fill in it could not be converted, which is logged, or its order’s cumulative could not be looked up (see below).
§A recovered fill advances the order too
A fill that arrives live over the WebSocket carries the order’s cumulative filled
quantity in Trade::order_filled_quantity (executionReport’s z), so it advances
the order by itself. A fill recovered after a disconnect comes from REST myTrades,
which reports executions only, so recovery rebuilds the same figure by reading each
recovered order’s executions from its first: one extra request per order (another per
further 1,000 executions), up to four orders at a time per instrument.
Those lookups have their own time budget inside the recovery timeout, so they can never
cost a fill. A fill whose order was not looked up in time, or whose lookup failed, goes
out with order_filled_quantity: None, logged at warn. It then advances the position
but not the order, which keeps whatever filled_quantity it held before the gap until
the order’s state is learned some other way.
Source§async fn fetch_open_orders(
&self,
instruments: &[InstrumentNameExchange],
) -> Result<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>, UnindexedClientError>
async fn fetch_open_orders( &self, instruments: &[InstrumentNameExchange], ) -> Result<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>, UnindexedClientError>
An empty instruments slice is the ExecutionClient “return all” sentinel: a single
no-symbol GET /api/v3/openOrders call returns open orders across every instrument (each
order’s instrument is recovered from its own symbol field). A non-empty slice fetches the
listed instruments concurrently, per-symbol.
Source§async fn fetch_trades(
&self,
time_since: DateTime<Utc>,
instruments: &[InstrumentNameExchange],
) -> Result<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>, UnindexedClientError>
async fn fetch_trades( &self, time_since: DateTime<Utc>, instruments: &[InstrumentNameExchange], ) -> Result<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>, UnindexedClientError>
Documented deviation from the ExecutionClient::fetch_trades “return all” contract:
Binance’s myTrades endpoint requires a symbol — there is no no-symbol “all trades” query
(unlike open orders). An empty instruments slice therefore has nothing to query and
returns an empty Vec; callers wanting all trades must enumerate instruments explicitly.
const EXCHANGE: ExchangeId = ExchangeId::BinanceSpot
Source§const SUPPORTED_KINDS: &'static [InstrumentKindDiscriminant]
const SUPPORTED_KINDS: &'static [InstrumentKindDiscriminant]
InstrumentKindDiscriminants this client can trade. Read moretype Config = BinanceSpotConfig
type AccountStream = Pin<Box<dyn Stream<Item = AccountEvent<ExchangeId, AssetNameExchange, InstrumentNameExchange>> + Send>>
Source§async fn account_snapshot(
&self,
assets: &[AssetNameExchange],
instruments: &[InstrumentNameExchange],
) -> Result<UnindexedAccountSnapshot, UnindexedClientError>
async fn account_snapshot( &self, assets: &[AssetNameExchange], instruments: &[InstrumentNameExchange], ) -> Result<UnindexedAccountSnapshot, UnindexedClientError>
instruments, from the
venue. Read moreSource§async fn cancel_order(
&self,
request: OrderRequestCancel<ExchangeId, &InstrumentNameExchange>,
) -> Option<UnindexedOrderResponseCancel>
async fn cancel_order( &self, request: OrderRequestCancel<ExchangeId, &InstrumentNameExchange>, ) -> Option<UnindexedOrderResponseCancel>
Source§async fn open_order(
&self,
request: OrderRequestOpen<ExchangeId, &InstrumentNameExchange>,
) -> Option<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>
async fn open_order( &self, request: OrderRequestOpen<ExchangeId, &InstrumentNameExchange>, ) -> Option<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>
Source§async fn fetch_balances(
&self,
assets: &[AssetNameExchange],
) -> Result<Vec<AssetBalance<AssetNameExchange>>, UnindexedClientError>
async fn fetch_balances( &self, assets: &[AssetNameExchange], ) -> Result<Vec<AssetBalance<AssetNameExchange>>, UnindexedClientError>
fn cancel_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestCancel<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = Option<UnindexedOrderResponseCancel>> + Send
fn open_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestOpen<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = Option<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>> + Send
Auto Trait Implementations§
impl !Freeze for BinanceSpot
impl !RefUnwindSafe for BinanceSpot
impl !UnwindSafe for BinanceSpot
impl Send for BinanceSpot
impl Sync for BinanceSpot
impl Unpin for BinanceSpot
impl UnsafeUnpin for BinanceSpot
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> JsonSchemaMaybe for T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.