Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

An asynchronous OANDA v20 API client.

The client is cheap to clone (all clones share one connection pool and one rate limiter) and is Send + Sync, so a single instance can be shared freely between concurrent tokio tasks:

use oanda_rs::{Client, Environment};

let client = Client::new(Environment::Practice, "my-token");
let for_task = client.clone(); // shares pool + rate limiter

Rate limiting is built in and enabled by default (100 REST requests/s, 2 stream connections/s — under OANDA’s 120/s and 2/s caps). See Client::builder for tuning.

Implementations§

Source§

impl Client

Source

pub fn new(environment: Environment, token: impl Into<String>) -> Client

Creates a client for environment authenticating with the given personal access token, using default settings.

Use Client::builder to customize the datetime format, rate limits, or the underlying HTTP client.

§Panics

Panics if the system TLS backend cannot be initialized (same condition as reqwest::Client::new).

Source

pub fn builder() -> ClientBuilder

Starts building a client with custom configuration.

Source

pub fn datetime_format(&self) -> AcceptDatetimeFormat

The datetime wire format this client requests via the Accept-Datetime-Format header.

Source§

impl Client

Source

pub async fn account( &self, account_id: impl Into<AccountId>, ) -> Result<AccountResponse, Error>

Get the full details for a single account, including a full list of its open trades, open positions, and pending orders.

GET /v3/accounts/{accountID}

Source

pub fn account_changes( &self, account_id: impl Into<AccountId>, ) -> AccountChangesRequest

Poll an account for its current state and changes since a specified transaction ID.

This is OANDA’s recommended pattern for tracking account state: fetch Client::account once, then poll this endpoint with the last seen transaction ID and apply the returned AccountChanges to your snapshot.

GET /v3/accounts/{accountID}/changes

Source

pub async fn list_accounts(&self) -> Result<ListAccountsResponse, Error>

Get a list of all accounts authorized for the provided token.

GET /v3/accounts

§Examples
for account in client.list_accounts().await?.accounts {
    println!("{:?}", account.id);
}
Source

pub async fn account_summary( &self, account_id: impl Into<AccountId>, ) -> Result<AccountSummaryResponse, Error>

Get a summary for a single account.

GET /v3/accounts/{accountID}/summary

§Examples
let summary = client.account_summary("101-004-1234567-001").await?.account;
println!("balance: {:?}", summary.balance);
Source

pub fn account_instruments( &self, account_id: impl Into<AccountId>, ) -> AccountInstrumentsRequest

Get the list of tradeable instruments for the given account. The list of tradeable instruments is dependent on the regulatory division that the account is located in.

GET /v3/accounts/{accountID}/instruments

§Examples
use oanda_rs::models::InstrumentName;

// All instruments:
let all = client.account_instruments("101-004-1234567-001").send().await?;
// Or a specific subset:
let some = client
    .account_instruments("101-004-1234567-001")
    .instruments([InstrumentName::EurUsd, InstrumentName::XauUsd])
    .send()
    .await?;
Source

pub fn configure_account( &self, account_id: impl Into<AccountId>, ) -> ConfigureAccountRequest

Set client-configurable properties of the account: its alias and margin rate.

PATCH /v3/accounts/{accountID}/configuration

§Errors

Rejections (HTTP 400/403) carry a ClientConfigureRejectTransaction; recover it with ApiErrorBody::details::<ConfigureAccountRejectBody>.

§Examples
use oanda_rs::models::DecimalNumber;

client
    .configure_account("101-004-1234567-001")
    .alias("my-strategy-account")
    .margin_rate("0.02".parse::<DecimalNumber>().unwrap())
    .send()
    .await?;
Source§

impl Client

Source

pub fn candles(&self, instrument: impl Into<InstrumentName>) -> CandlesRequest

Fetch candlestick data for an instrument.

GET /v3/instruments/{instrument}/candles

§Examples
use oanda_rs::models::CandlestickGranularity;

let candles = client
    .candles("EUR_USD")
    .granularity(CandlestickGranularity::H1)
    .count(500)
    .send()
    .await?;
println!("got {} candles", candles.candles.len());
Source

pub fn account_candles( &self, account_id: impl Into<AccountId>, instrument: impl Into<InstrumentName>, ) -> CandlesRequest

Fetch candlestick data for an instrument, scoped to an account: the data matches what the account would see, and volume-weighted average prices can be requested via units.

GET /v3/accounts/{accountID}/instruments/{instrument}/candles

Source

pub fn latest_candles( &self, account_id: impl Into<AccountId>, specifications: impl IntoIterator<Item = CandleSpecification>, ) -> LatestCandlesRequest

Get the most recently completed candles within an account for the specified combinations of instrument, granularity, and price component.

GET /v3/accounts/{accountID}/candles/latest

§Examples
use oanda_rs::models::{CandleSpecification, CandlestickGranularity, PricingComponent};

let latest = client
    .latest_candles(
        "101-004-1234567-001",
        [CandleSpecification::new("EUR_USD", CandlestickGranularity::S10)
            .price(PricingComponent::BID.with_mid())],
    )
    .send()
    .await?;
Source

pub fn instrument_order_book( &self, instrument: impl Into<InstrumentName>, ) -> OrderBookRequest

Fetch an order book for an instrument.

GET /v3/instruments/{instrument}/orderBook

Source

pub fn instrument_position_book( &self, instrument: impl Into<InstrumentName>, ) -> PositionBookRequest

Fetch a position book for an instrument.

GET /v3/instruments/{instrument}/positionBook

Source§

impl Client

Source

pub async fn create_order( &self, account_id: impl Into<AccountId>, order: impl Into<OrderRequest>, ) -> Result<CreateOrderResponse, Error>

Create an order for an account.

POST /v3/accounts/{accountID}/orders

§Errors

Rejections (HTTP 400/404) carry an orderRejectTransaction; recover it with ApiErrorBody::details::<CreateOrderRejectBody>.

§Examples
use oanda_rs::models::{MarketOrderRequest, StopLossDetails, TakeProfitDetails};

let response = client
    .create_order(
        "101-004-1234567-001",
        MarketOrderRequest::new("EUR_USD", 100)
            .take_profit_on_fill(TakeProfitDetails::at_price("1.1050".parse().unwrap()))
            .stop_loss_on_fill(StopLossDetails::at_distance("0.0050".parse().unwrap())),
    )
    .await?;
println!("created: {:?}", response.order_create_transaction.id());
Source

pub fn list_orders(&self, account_id: impl Into<AccountId>) -> ListOrdersRequest

Get a list of orders for an account.

GET /v3/accounts/{accountID}/orders

Source

pub async fn list_pending_orders( &self, account_id: impl Into<AccountId>, ) -> Result<ListOrdersResponse, Error>

List all pending orders in an account.

GET /v3/accounts/{accountID}/pendingOrders

Source

pub async fn order( &self, account_id: impl Into<AccountId>, order: impl Into<OrderSpecifier>, ) -> Result<OrderResponse, Error>

Get details for a single order in an account.

GET /v3/accounts/{accountID}/orders/{orderSpecifier}

Source

pub async fn replace_order( &self, account_id: impl Into<AccountId>, order: impl Into<OrderSpecifier>, replacement: impl Into<OrderRequest>, ) -> Result<ReplaceOrderResponse, Error>

Replace an order in an account by simultaneously cancelling it and creating a replacement.

PUT /v3/accounts/{accountID}/orders/{orderSpecifier}

Source

pub async fn cancel_order( &self, account_id: impl Into<AccountId>, order: impl Into<OrderSpecifier>, ) -> Result<CancelOrderResponse, Error>

Cancel a pending order in an account.

PUT /v3/accounts/{accountID}/orders/{orderSpecifier}/cancel

§Errors

A failed cancellation (HTTP 404) carries an orderCancelRejectTransaction; recover it with ApiErrorBody::details::<CancelOrderRejectBody>.

Source

pub fn set_order_client_extensions( &self, account_id: impl Into<AccountId>, order: impl Into<OrderSpecifier>, ) -> SetOrderClientExtensionsRequest

Update the client extensions for an order in an account.

Do not set, modify, or delete client_extensions if the account is associated with MT4.

PUT /v3/accounts/{accountID}/orders/{orderSpecifier}/clientExtensions

Source§

impl Client

Source

pub async fn list_positions( &self, account_id: impl Into<AccountId>, ) -> Result<ListPositionsResponse, Error>

List all positions for an account. The positions returned are for every instrument that has had a position during the lifetime of the account.

GET /v3/accounts/{accountID}/positions

Source

pub async fn list_open_positions( &self, account_id: impl Into<AccountId>, ) -> Result<ListPositionsResponse, Error>

List all open positions for an account. An open position is a position that currently has a trade open.

GET /v3/accounts/{accountID}/openPositions

Source

pub async fn position( &self, account_id: impl Into<AccountId>, instrument: impl Into<InstrumentName>, ) -> Result<PositionResponse, Error>

Get the details of a single instrument’s position in an account.

GET /v3/accounts/{accountID}/positions/{instrument}

Source

pub fn close_position( &self, account_id: impl Into<AccountId>, instrument: impl Into<InstrumentName>, ) -> ClosePositionRequest

Close a position for a specific instrument.

At least one of long_units / short_units should be set; OANDA defaults both to ALL when omitted.

PUT /v3/accounts/{accountID}/positions/{instrument}/close

§Examples
// Close the whole long side of the EUR_USD position:
client
    .close_position("101-004-1234567-001", "EUR_USD")
    .long_units("ALL")
    .send()
    .await?;
Source§

impl Client

Source

pub fn prices<I>( &self, account_id: impl Into<AccountId>, instruments: I, ) -> PricesRequest

Get pricing information for a list of instruments within an account.

GET /v3/accounts/{accountID}/pricing

§Examples
let pricing = client
    .prices("101-004-1234567-001", ["EUR_USD", "USD_JPY"])
    .send()
    .await?;
for price in pricing.prices {
    println!("{:?}: {:?}", price.instrument, price.closeout_bid);
}
Source

pub fn pricing_stream<I>( &self, account_id: impl Into<AccountId>, instruments: I, ) -> PricingStreamRequest

Open a stream of price updates (plus a heartbeat every 5 seconds) for the given instruments.

The returned PricingStream manages its own connection: it detects stale connections via the heartbeat, reconnects with capped exponential backoff, and requests a fresh price snapshot on every reconnect. See crate::streaming for details and tuning.

Connects to the streaming host (stream-fxpractice/stream-fxtrade). OANDA allows at most 20 active streams per IP; prices are throttled to at most 4 updates per second per instrument.

GET /v3/accounts/{accountID}/pricing/stream

§Examples
use futures_util::StreamExt;
use oanda_rs::models::PriceStreamItem;

let mut stream = client
    .pricing_stream("101-004-1234567-001", ["EUR_USD"])
    .send()
    .await?;
while let Some(item) = stream.next().await {
    match item? {
        PriceStreamItem::Price(price) => println!("{:?}", price.closeout_bid),
        PriceStreamItem::Heartbeat(_) => {}
        _ => {}
    }
}
Source§

impl Client

Source

pub fn list_trades(&self, account_id: impl Into<AccountId>) -> ListTradesRequest

Get a list of trades for an account.

GET /v3/accounts/{accountID}/trades

Source

pub async fn list_open_trades( &self, account_id: impl Into<AccountId>, ) -> Result<ListTradesResponse, Error>

Get the list of open trades for an account.

GET /v3/accounts/{accountID}/openTrades

Source

pub async fn trade( &self, account_id: impl Into<AccountId>, trade: impl Into<TradeSpecifier>, ) -> Result<TradeResponse, Error>

Get the details of a specific trade in an account.

GET /v3/accounts/{accountID}/trades/{tradeSpecifier}

Source

pub fn close_trade( &self, account_id: impl Into<AccountId>, trade: impl Into<TradeSpecifier>, ) -> CloseTradeRequest

Close (partially or fully) a specific open trade in an account.

PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/close

§Examples
// Close a trade fully (the default) ...
client.close_trade("101-004-1234567-001", "6543").send().await?;
// ... or partially:
client
    .close_trade("101-004-1234567-001", "6543")
    .units("50")
    .send()
    .await?;
Source

pub async fn set_trade_client_extensions( &self, account_id: impl Into<AccountId>, trade: impl Into<TradeSpecifier>, extensions: ClientExtensions, ) -> Result<SetTradeClientExtensionsResponse, Error>

Update the client extensions for a trade.

Do not add, update, or delete the client extensions if your account is associated with MT4.

PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/clientExtensions

Source

pub fn set_trade_dependent_orders( &self, account_id: impl Into<AccountId>, trade: impl Into<TradeSpecifier>, ) -> SetTradeDependentOrdersRequest

Create, replace and cancel the dependent orders (take-profit, stop-loss and trailing stop-loss) of a trade.

Setting a detail replaces the existing dependent order (or creates one); setting it to “cancel” removes it. Details not set are left unmodified.

PUT /v3/accounts/{accountID}/trades/{tradeSpecifier}/orders

Source§

impl Client

Source

pub fn list_transactions( &self, account_id: impl Into<AccountId>, ) -> ListTransactionsRequest

Get a list of transaction pages that satisfy a time-based transaction query. The response contains page URLs, not transactions; fetch pages via Client::transactions_id_range.

GET /v3/accounts/{accountID}/transactions

Source

pub async fn transaction( &self, account_id: impl Into<AccountId>, transaction_id: impl Into<TransactionId>, ) -> Result<TransactionResponse, Error>

Get the details of a single account transaction.

GET /v3/accounts/{accountID}/transactions/{transactionID}

Source

pub fn transactions_id_range( &self, account_id: impl Into<AccountId>, from: impl Into<TransactionId>, to: impl Into<TransactionId>, ) -> TransactionsIdRangeRequest

Get a range of transactions for an account based on transaction IDs.

GET /v3/accounts/{accountID}/transactions/idrange

Source

pub fn transaction_stream( &self, account_id: impl Into<AccountId>, ) -> TransactionStreamRequest

Open a stream of the account’s transactions (plus a heartbeat every 5 seconds).

The returned TransactionStream manages its own connection: it detects stale connections via the heartbeat, reconnects with capped exponential backoff, and back-fills transactions missed while disconnected via Client::transactions_since_id, deduplicating by transaction ID. See crate::streaming for details and tuning.

Connects to the streaming host (stream-fxpractice/stream-fxtrade). OANDA allows at most 20 active streams per IP.

GET /v3/accounts/{accountID}/transactions/stream

§Examples
use futures_util::StreamExt;
use oanda_rs::models::transaction::TransactionStreamItem;

let mut stream = client
    .transaction_stream("101-004-1234567-001")
    .send()
    .await?;
while let Some(item) = stream.next().await {
    if let TransactionStreamItem::Transaction(tx) = item? {
        println!("{:?}: {:?}", tx.type_name(), tx.id());
    }
}
Source

pub async fn transactions_since_id( &self, account_id: impl Into<AccountId>, id: impl Into<TransactionId>, ) -> Result<TransactionsResponse, Error>

Get a range of transactions for an account starting at (but not including) a provided transaction ID.

This is the canonical way to back-fill transactions missed while a transaction stream was disconnected.

GET /v3/accounts/{accountID}/transactions/sinceid

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Client

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more