pub trait BracketOrderClient: ExecutionClient {
// Required method
fn open_bracket_order(
&self,
request: BracketOrderRequest<ExchangeId, &InstrumentNameExchange>,
) -> impl Future<Output = BracketOrderResult> + Send;
}Expand description
Extension trait for exchanges that support native bracket orders.
A bracket order consists of three linked orders:
- Entry: Limit order to enter the position
- Take Profit: Limit order to exit at profit target
- Stop Loss: Stop (or stop-limit) order to exit at loss limit
When either exit leg fills, the exchange automatically cancels the other.
§Type-Level Capability
This is a supertrait of ExecutionClient, enabling compile-time capability checks:
impl ExecutionClient— basic order operationsimpl ExecutionClient + BracketOrderClient— includes bracket orders
This follows Rust idioms like Read + Seek or Iterator + ExactSizeIterator.
§Why Supertrait Over Alternatives
vs. associated types on ExecutionClient: Callers can’t construct
Self::BracketRequest without knowing the concrete type — adds trait surface
without enabling generic use.
vs. default impl returning Unsupported: Puts a “dead method” on every
client (MockClient, BinanceClient, HyperliquidClient). Compile-time capability
via trait bounds is better than runtime errors.
§Result Types
BracketOrderResult uses Option<Order> for child legs to document API divergence:
| Exchange | take_profit | stop_loss | Reason |
|---|---|---|---|
| IBKR | Some(...) | Some(...) | Returns all three orders immediately |
| Alpaca | None | None | Child legs created server-side |
§Example
use rustrade_execution::client::{ExecutionClient, BracketOrderClient};
use rustrade_execution::order::bracket::{BracketOrderRequest, RequestOpenBracket};
async fn place_bracket<C: ExecutionClient + BracketOrderClient>(
client: &C,
request: BracketOrderRequest<ExchangeId, &InstrumentNameExchange>,
) -> BracketOrderResult {
client.open_bracket_order(request).await
}Required Methods§
Sourcefn open_bracket_order(
&self,
request: BracketOrderRequest<ExchangeId, &InstrumentNameExchange>,
) -> impl Future<Output = BracketOrderResult> + Send
fn open_bracket_order( &self, request: BracketOrderRequest<ExchangeId, &InstrumentNameExchange>, ) -> impl Future<Output = BracketOrderResult> + Send
Place a bracket order (entry + take-profit + stop-loss).
§Request
The BracketOrderRequest contains:
key: Order key (exchange, instrument, strategy, client order ID)state:RequestOpenBracketwith side, quantity, prices, and optional stop-loss limit price
§Constraints
time_in_forcemust beDayorGoodUntilCancelledon most exchanges- Entry order type is always
Limit - Price ordering must be valid for the side (see
RequestOpenBracket)
§Exchange-Specific Field Handling
RequestOpenBracket::stop_loss_limit_price is not honored uniformly:
- Alpaca: When
Some, the stop-loss leg becomes a stop-limit order at that price. - IBKR: Silently ignored — the stop-loss leg is always a stop (market) order.
Generic callers T: BracketOrderClient must treat this field as advisory.
§Return Value
Returns BracketOrderResult with:
parent: Always present (entry order)take_profit:Someif exchange returns legs immediately (IBKR),Noneotherwise (Alpaca)stop_loss:Someif exchange returns legs immediately (IBKR),Noneotherwise (Alpaca)
Either all orders are Active(Open) or all are Inactive (placement failed).
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.