rustrade-execution 0.20.0

Stream private account data from financial venues, and execute (live or mock) orders.
Documentation
use crate::{
    UnindexedAccountSnapshot,
    balance::AssetBalance,
    borrow_capacity::BorrowCapacity,
    error::UnindexedApiError,
    order::{
        Order, UnindexedInactiveOrder, UnindexedOrderKey,
        request::{OrderRequestCancel, OrderRequestOpen, UnindexedOrderResponseCancel},
        state::{Open, UnindexedOrderState},
    },
    trade::Trade,
};
use chrono::{DateTime, Utc};
use rustrade_instrument::{
    Side, asset::name::AssetNameExchange, exchange::ExchangeId,
    instrument::name::InstrumentNameExchange,
};
use tokio::sync::oneshot;

#[derive(Debug)]
pub struct MockExchangeRequest {
    pub time_request: DateTime<Utc>,
    pub kind: MockExchangeRequestKind,
}

impl MockExchangeRequest {
    pub fn new(time_request: DateTime<Utc>, kind: MockExchangeRequestKind) -> Self {
        Self { time_request, kind }
    }

    pub fn fetch_account_snapshot(
        time_request: DateTime<Utc>,
        response_tx: oneshot::Sender<UnindexedAccountSnapshot>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchAccountSnapshot { response_tx },
        )
    }

    pub fn fetch_balances(
        time_request: DateTime<Utc>,
        assets: Vec<AssetNameExchange>,
        response_tx: oneshot::Sender<Vec<AssetBalance<AssetNameExchange>>>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchBalances {
                response_tx,
                assets,
            },
        )
    }

    pub fn fetch_orders_open(
        time_request: DateTime<Utc>,
        instruments: Vec<InstrumentNameExchange>,
        response_tx: oneshot::Sender<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchOrdersOpen {
                response_tx,
                instruments,
            },
        )
    }

    pub fn fetch_borrow_capacity(
        time_request: DateTime<Utc>,
        instrument: InstrumentNameExchange,
        side: Side,
        response_tx: oneshot::Sender<Result<BorrowCapacity, UnindexedApiError>>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchBorrowCapacity {
                response_tx,
                instrument,
                side,
            },
        )
    }

    pub fn fetch_orders_ended(
        time_request: DateTime<Utc>,
        orders: Vec<UnindexedOrderKey>,
        response_tx: oneshot::Sender<Vec<UnindexedInactiveOrder>>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchOrdersEnded {
                orders,
                response_tx,
            },
        )
    }

    pub fn fetch_trades(
        time_request: DateTime<Utc>,
        response_tx: oneshot::Sender<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>>,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::FetchTrades {
                response_tx,
                start,
                end,
            },
        )
    }

    pub fn cancel_order(
        time_request: DateTime<Utc>,
        response_tx: oneshot::Sender<UnindexedOrderResponseCancel>,
        request: OrderRequestCancel<ExchangeId, InstrumentNameExchange>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::CancelOrder {
                response_tx,
                request,
            },
        )
    }

    pub fn open_order(
        time_request: DateTime<Utc>,
        response_tx: oneshot::Sender<
            Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>,
        >,
        request: OrderRequestOpen<ExchangeId, InstrumentNameExchange>,
    ) -> Self {
        Self::new(
            time_request,
            MockExchangeRequestKind::OpenOrder {
                response_tx,
                request,
            },
        )
    }
}

/// What a [`MockExchangeRequest`] asks the venue for.
///
/// Non-exhaustive: requests are built through [`MockExchangeRequest`]'s constructors and matched only
/// by the venue's own driver, so a new request kind is not a breaking change.
#[derive(Debug)]
#[non_exhaustive]
pub enum MockExchangeRequestKind {
    FetchAccountSnapshot {
        response_tx: oneshot::Sender<UnindexedAccountSnapshot>,
    },
    FetchBalances {
        assets: Vec<AssetNameExchange>,
        response_tx: oneshot::Sender<Vec<AssetBalance<AssetNameExchange>>>,
    },
    FetchOrdersOpen {
        instruments: Vec<InstrumentNameExchange>,
        response_tx: oneshot::Sender<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>>,
    },
    FetchOrdersEnded {
        orders: Vec<UnindexedOrderKey>,
        response_tx: oneshot::Sender<Vec<UnindexedInactiveOrder>>,
    },
    /// How much more the account can borrow for an order on `instrument` on `side`.
    FetchBorrowCapacity {
        instrument: InstrumentNameExchange,
        side: Side,
        response_tx: oneshot::Sender<Result<BorrowCapacity, UnindexedApiError>>,
    },
    /// Every trade from `start` to `end`, both inclusive.
    FetchTrades {
        response_tx: oneshot::Sender<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>>,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    },
    CancelOrder {
        response_tx: oneshot::Sender<UnindexedOrderResponseCancel>,
        request: OrderRequestCancel<ExchangeId, InstrumentNameExchange>,
    },
    OpenOrder {
        response_tx:
            oneshot::Sender<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>,
        /// The order to open, carrying on
        /// [`RequestOpen::market`](crate::order::request::RequestOpen::market) the market its
        /// sender observed when it decided to open it.
        ///
        /// That snapshot is the venue's only price source for a Market order, which carries no
        /// limit price of its own. It is not repeated as a field of its own here: two copies of
        /// the same snapshot on one message could disagree, and nothing could arbitrate.
        request: OrderRequestOpen<ExchangeId, InstrumentNameExchange>,
    },
}