Skip to main content

ExchangeClient

Trait ExchangeClient 

Source
pub trait ExchangeClient:
    Send
    + Sync
    + 'static {
    // Required methods
    fn name(&self) -> &str;
    fn place_order<'life0, 'life1, 'async_trait>(
        &'life0 self,
        order: &'life1 Order,
    ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn cancel_all<'life0, 'life1, 'async_trait>(
        &'life0 self,
        symbol: &'life1 Symbol,
    ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn close_position<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        symbol: &'life1 Symbol,
        position: &'life2 Position,
    ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn get_position<'life0, 'life1, 'async_trait>(
        &'life0 self,
        symbol: &'life1 Symbol,
    ) -> Pin<Box<dyn Future<Output = Result<Position>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn get_balance<'life0, 'life1, 'async_trait>(
        &'life0 self,
        currency: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = Result<f64>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;

    // Provided methods
    fn supports(&self, _capability: Capability) -> bool { ... }
    fn contract_value(&self, _symbol: &Symbol) -> f64 { ... }
    fn instrument_spec(&self, symbol: &Symbol) -> InstrumentSpec { ... }
    fn get_open_orders<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _symbol: &'life1 Symbol,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<OpenOrder>>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn cancel_order<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        _symbol: &'life1 Symbol,
        _order_id: &'life2 str,
    ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait { ... }
}
Expand description

What the bot framework needs from an exchange to trade.

This trait is intentionally narrow — the full surface of a real exchange client (ws token management, stop orders, funding history, account tiers) belongs in the concrete adapter crate, not here. The framework only needs to: place orders, close positions, read balance and position state.

§Async + object-safe

async_trait is used so Arc<dyn ExchangeClient> works — downstream code can swap concrete exchanges at runtime without generics propagating through the whole system.

§Example

A stub adapter useful for examples and tests. Real adapters connect to a network and report actual state.

use async_trait::async_trait;
use rustrade_core::{Capability, ExchangeClient, Order, Position, Result, Symbol};

struct StubExchange;

#[async_trait]
impl ExchangeClient for StubExchange {
    fn name(&self) -> &str { "stub" }
    async fn place_order(&self, _order: &Order) -> Result<String> {
        Ok("order-1".into())
    }
    async fn cancel_all(&self, _symbol: &Symbol) -> Result<usize> { Ok(0) }
    async fn close_position(&self, _symbol: &Symbol, _p: &Position) -> Result<String> {
        Ok("close-1".into())
    }
    async fn get_position(&self, _symbol: &Symbol) -> Result<Position> {
        Ok(Position::FLAT)
    }
    async fn get_balance(&self, _currency: &str) -> Result<f64> { Ok(0.0) }
    fn supports(&self, c: Capability) -> bool {
        matches!(c, Capability::ReduceOnly)
    }
}

Required Methods§

Source

fn name(&self) -> &str

Short, lowercase exchange identifier — e.g. "kucoin".

Source

fn place_order<'life0, 'life1, 'async_trait>( &'life0 self, order: &'life1 Order, ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Place an order. Returns the exchange-assigned order id.

Source

fn cancel_all<'life0, 'life1, 'async_trait>( &'life0 self, symbol: &'life1 Symbol, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Cancel all open orders for a symbol. Returns the count cancelled.

Source

fn close_position<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, symbol: &'life1 Symbol, position: &'life2 Position, ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Close the given position with a market order. Returns the exchange order id of the close.

Source

fn get_position<'life0, 'life1, 'async_trait>( &'life0 self, symbol: &'life1 Symbol, ) -> Pin<Box<dyn Future<Output = Result<Position>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Fetch the current position for a symbol (or Position::FLAT if flat).

Source

fn get_balance<'life0, 'life1, 'async_trait>( &'life0 self, currency: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<f64>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Fetch the current balance in the given currency.

Provided Methods§

Source

fn supports(&self, _capability: Capability) -> bool

Does this adapter support the given optional capability?

The default returns false for every variant — adapters opt in by overriding. This is intentionally conservative: a new adapter that forgets to override won’t quietly accept orders it can’t execute.

Source

fn contract_value(&self, _symbol: &Symbol) -> f64

Base-asset units per one contract for the given symbol.

For spot exchanges this is 1.0 for every symbol — one unit traded equals one unit of the base asset. For futures, it’s the contract multiplier (e.g. 0.001 for KuCoin XBTUSDTM where one contract is 0.001 BTC). The risk layer’s PositionSizer uses this to convert margin × leverage into a contract count.

The default returns 1.0 — appropriate for spot adapters. Futures adapters override.

Source

fn instrument_spec(&self, symbol: &Symbol) -> InstrumentSpec

Full instrument metadata for symbol: contract size, price tick, quantity lot, minimum notional, and AssetClass.

The framework uses this to round orders to the venue’s increments, enforce a minimum order notional, and apply class-aware risk rules. The default derives an InstrumentSpec from Self::contract_value with no other constraints, so adapters that only override contract_value keep working unchanged; adapters with real venue metadata (tick/lot/min-notional) override this and should make contract_value agree with instrument_spec(symbol).contract_value.

Source

fn get_open_orders<'life0, 'life1, 'async_trait>( &'life0 self, _symbol: &'life1 Symbol, ) -> Pin<Box<dyn Future<Output = Result<Vec<OpenOrder>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

List the currently-resting (non-terminal) orders for a symbol.

Used by the framework’s order-tracking layer to age out stale limit orders and to reconcile after a reconnect. The default returns an empty list — an adapter that doesn’t advertise Capability::OrderTracking is assumed to have no resting orders the framework should manage (e.g. a market-only or fire-and-forget adapter). Adapters that support resting orders must override this and advertise the capability.

Source

fn cancel_order<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _symbol: &'life1 Symbol, _order_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Cancel a single resting order by its exchange-assigned id.

Returns Ok(true) if the order was cancelled, Ok(false) if it was already gone (filled or cancelled) — a benign no-op the caller can ignore. The default errors, so the tracking layer never believes it cancelled an order against an adapter that can’t actually do so; adapters advertising Capability::OrderTracking override it.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§