Rust
orderfill: async on Tokio, Result-based, rustls. For bots that should not stop.
[dependencies]
orderfill = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use orderfill::{OrderFill, PlaceOrder, Side};
#[tokio::main]
async fn main() -> orderfill::Result<()> {
let client = OrderFill::new(std::env::var("ORDERFILL_API_KEY").unwrap());
let accounts = client.accounts().await?;
let gold = client.account(&accounts[0].id);
println!("{:?}", gold.info().await?);
gold.place_order(&PlaceOrder::market("XAUUSD", Side::Buy, 0.1)).await?;
Ok(())
}
OrderFill is cheap to clone and shares one connection pool.
Trading
use orderfill::{Modify, ModifyOrder, PlaceOrder, Side};
gold.place_order(&PlaceOrder::market("XAUUSD", Side::Buy, 0.1).stop_loss(2395.0), None).await?;
gold.place_order(&PlaceOrder::limit("EURUSD", Side::Buy, 0.1, 1.07), None).await?;
gold.check_order(&PlaceOrder::market("XAUUSD", Side::Buy, 0.1)).await?;
gold.modify_order(&order_ticket, &ModifyOrder { price: Some(1.075), ..Default::default() }, None).await?;
gold.modify_position(&ticket, &Modify { take_profit: Some(2440.0), ..Default::default() }, None).await?;
gold.close_position(&ticket, Some(0.05), None).await?;
gold.close_losing(None, None).await?;
Each call that changes the account sends an idempotency key: the last argument. None
makes one; pass Some("…") to keep the same key across a restart.
Reading and symbols
gold.quote("XAUUSD").await?;
gold.candles("XAUUSD", &CandlesQuery::latest(Timeframe::M1, 100)).await?;
gold.candles("XAUUSD", &CandlesQuery::range(Timeframe::H1).from("2026-09-01T00:00:00Z")).await?;
gold.executions(&Range::default()).await?;
gold.symbols(&SymbolsQuery { q: Some("gold".into()), all: true }).await?;
gold.health().await?;
gold.margin_for(&MarginFor::new("XAUUSD", Side::Buy, 0.1)).await?;
gold.resolve_symbol("XAUUSD").await?;
Streaming
use orderfill::{OrderFillStream, Quote, StreamName, DEFAULT_STREAM_URL};
let stream = OrderFillStream::open(token, DEFAULT_STREAM_URL).await?;
let mut ticks = stream.watch(&account_id, StreamName::Ticks, Some("XAUUSD"), None).await?;
while let Some(batch) = ticks.next::<Quote>().await {
for quote in batch? { println!("{} {}", quote.bid, quote.ask); }
}
Errors
match gold.place_order(&PlaceOrder::market("NZDCAD", Side::Buy, 0.1)).await {
Err(error) if error.code == orderfill::ErrorCode::BadRequest => eprintln!("{error}"), // includes the hint
other => { other?; }
}
error.retryable() says whether trying again could work; the client has already retried
the ones that could.