use binance_api_client::{
Binance, CancelReplaceMode, CancelReplaceOrderBuilder, OcoOrderBuilder, OrderBuilder,
OrderSide, OrderType, OtoOrderBuilder, OtocoOrderBuilder, TimeInForce,
};
#[tokio::main]
async fn main() -> binance_api_client::Result<()> {
tracing_subscriber::fmt::init();
let _ = dotenv::dotenv();
println!("=== Binance Advanced Order Types Example ===\n");
let api_key = match std::env::var("BINANCE_API_KEY") {
Ok(key) => key,
Err(_) => {
println!("BINANCE_API_KEY not set. Showing example code only.\n");
show_example_code();
return Ok(());
}
};
let secret_key = match std::env::var("BINANCE_SECRET_KEY") {
Ok(key) => key,
Err(_) => {
println!("BINANCE_SECRET_KEY not set. Showing example code only.\n");
show_example_code();
return Ok(());
}
};
let client = Binance::testnet(&api_key, &secret_key)?;
println!("Using testnet: {}", client.config().rest_api_endpoint);
println!("NOTE: Using TESTNET - no real funds at risk!\n");
let symbol = "BTCUSDT";
let price = client.market().price(symbol).await?;
let current_price: f64 = price.price;
println!("Current {} price: {:.2}\n", symbol, current_price);
let take_profit_price = current_price * 1.05; let stop_price = current_price * 0.95; let stop_limit_price = current_price * 0.948; let entry_price = current_price * 0.98;
println!("=== 1. OCO Order (One-Cancels-the-Other) ===\n");
println!("OCO combines a limit order (take profit) and a stop-loss order.");
println!("When one fills, the other is automatically canceled.\n");
let oco = OcoOrderBuilder::new(
symbol,
OrderSide::Sell, "0.001", &format!("{:.2}", take_profit_price), &format!("{:.2}", stop_price), )
.stop_limit_price(&format!("{:.2}", stop_limit_price))
.stop_limit_time_in_force(TimeInForce::GTC)
.build();
println!("OCO Order:");
println!(" Quantity: 0.001 BTC");
println!(" Take Profit: {:.2} (limit sell)", take_profit_price);
println!(" Stop Loss: {:.2} (stop trigger)", stop_price);
println!(" Stop Limit: {:.2}\n", stop_limit_price);
print!("Placing OCO order... ");
match client.account().create_oco(&oco).await {
Ok(result) => {
println!("Success! Order List ID: {}", result.order_list_id);
let _ = client
.account()
.cancel_order_list(symbol, Some(result.order_list_id), None)
.await;
println!(" (Canceled for cleanup)");
}
Err(e) => println!("Failed: {} (expected if no balance)", e),
}
println!();
println!("=== 2. OTO Order (One-Triggers-the-Other) ===\n");
println!("OTO has a 'working' order that triggers a 'pending' order when filled.");
println!("Example: Buy BTC at entry price, then sell at take profit.\n");
let oto = OtoOrderBuilder::new(
symbol,
OrderType::Limit, OrderSide::Buy, &format!("{:.2}", entry_price), "0.001", OrderType::Limit, OrderSide::Sell, "0.001", )
.working_time_in_force(TimeInForce::GTC)
.pending_price(&format!("{:.2}", take_profit_price))
.pending_time_in_force(TimeInForce::GTC)
.build();
println!("OTO Order:");
println!(" Working: BUY 0.001 BTC @ {:.2}", entry_price);
println!(
" Pending: SELL 0.001 BTC @ {:.2} (triggered when working fills)\n",
take_profit_price
);
print!("Placing OTO order... ");
match client.account().create_oto(&oto).await {
Ok(result) => {
println!("Success! Order List ID: {}", result.order_list_id);
let _ = client
.account()
.cancel_order_list(symbol, Some(result.order_list_id), None)
.await;
println!(" (Canceled for cleanup)");
}
Err(e) => println!("Failed: {} (expected if no balance)", e),
}
println!();
println!("=== 3. OTOCO Order (One-Triggers-One-Cancels-Other) ===\n");
println!("OTOCO combines OTO + OCO: A working order triggers an OCO (TP + SL).");
println!("Example: Entry order triggers both take-profit and stop-loss.\n");
let otoco = OtocoOrderBuilder::new(
symbol,
OrderType::Limit, OrderSide::Buy, &format!("{:.2}", entry_price), "0.001", OrderSide::Sell, "0.001", OrderType::LimitMaker, )
.working_time_in_force(TimeInForce::GTC)
.pending_above_price(&format!("{:.2}", take_profit_price)) .pending_below_type(OrderType::StopLossLimit)
.pending_below_stop_price(&format!("{:.2}", stop_price))
.pending_below_price(&format!("{:.2}", stop_limit_price))
.pending_below_time_in_force(TimeInForce::GTC)
.build();
println!("OTOCO Order:");
println!(" Working: BUY 0.001 BTC @ {:.2}", entry_price);
println!(" When filled, triggers:");
println!(" - Take Profit: SELL @ {:.2}", take_profit_price);
println!(
" - Stop Loss: SELL @ {:.2} (stop: {:.2})\n",
stop_limit_price, stop_price
);
print!("Placing OTOCO order... ");
match client.account().create_otoco(&otoco).await {
Ok(result) => {
println!("Success! Order List ID: {}", result.order_list_id);
let _ = client
.account()
.cancel_order_list(symbol, Some(result.order_list_id), None)
.await;
println!(" (Canceled for cleanup)");
}
Err(e) => println!("Failed: {} (expected if no balance)", e),
}
println!();
println!("=== 4. Cancel-Replace Order ===\n");
println!("Atomically cancel an existing order and place a new one.");
println!("Prevents the gap where your order might be filled during modification.\n");
let initial_order = OrderBuilder::new(symbol, OrderSide::Buy, OrderType::Limit)
.quantity("0.001")
.price(&format!("{:.2}", entry_price))
.time_in_force(TimeInForce::GTC)
.build();
println!("Placing initial order to modify...");
match client.account().create_order(&initial_order).await {
Ok(result) => {
println!(" Order placed! ID: {}", result.order_id);
println!(" Price: {}, Qty: {}\n", result.price, result.orig_qty);
let new_price = entry_price * 0.99; println!("Cancel-replacing with new price {:.2}...", new_price);
let cancel_replace = CancelReplaceOrderBuilder::new(
symbol,
OrderSide::Buy,
OrderType::Limit,
CancelReplaceMode::StopOnFailure,
)
.cancel_order_id(result.order_id)
.quantity("0.001")
.price(&format!("{:.2}", new_price))
.time_in_force(TimeInForce::GTC)
.build();
match client.account().cancel_replace_order(&cancel_replace).await {
Ok(response) => {
println!("Cancel-Replace successful!");
println!(" Cancel result: {:?}", response.cancel_result);
println!(" New order result: {:?}", response.new_order_result);
println!(
" Canceled order: {} (status: {:?})",
response.cancel_response.order_id, response.cancel_response.status
);
let new_order_id = match &response.new_order_response {
binance_api_client::OrderResponse::Ack(ack) => Some(ack.order_id),
binance_api_client::OrderResponse::Result(res) => Some(res.order_id),
binance_api_client::OrderResponse::Full(full) => Some(full.order_id),
};
if let Some(id) = new_order_id {
println!(" New order ID: {}", id);
let _ = client.account().cancel_order(symbol, Some(id), None).await;
println!(" (Cleaned up - canceled the new order)");
}
}
Err(e) => println!("Cancel-Replace failed: {}", e),
}
}
Err(e) => {
println!("Failed to place initial order: {}", e);
println!("(This is expected if you don't have sufficient balance)");
}
}
println!();
println!("=== 5. Query Order Lists ===\n");
println!("Getting open order lists...");
match client.account().open_order_lists().await {
Ok(lists) => {
if lists.is_empty() {
println!("No open order lists.\n");
} else {
for list in &lists {
println!("Order List ID: {}", list.order_list_id);
println!(" Contingency Type: {:?}", list.contingency_type);
println!(" List Status Type: {:?}", list.list_status_type);
println!(" Orders: {}", list.orders.len());
}
}
}
Err(e) => println!("Failed to get order lists: {}\n", e),
}
println!("=== Example completed! ===");
Ok(())
}
fn show_example_code() {
println!("=== Advanced Order Types Example Code ===\n");
println!("// Create authenticated client (testnet recommended for testing)");
println!("let client = Binance::testnet(\"api_key\", \"secret_key\")?;\n");
println!("// --- OCO (One-Cancels-the-Other) ---");
println!("// Combines take-profit and stop-loss in one order");
println!("use binance_api_client::{{OcoOrderBuilder, OrderSide, TimeInForce}};\n");
println!("let oco = OcoOrderBuilder::new(");
println!(" \"BTCUSDT\",");
println!(" OrderSide::Sell, // Exit side");
println!(" \"0.001\", // Quantity");
println!(" \"70000.00\", // Take profit (limit price)");
println!(" \"60000.00\", // Stop price");
println!(")");
println!(".stop_limit_price(\"59900.00\") // Stop limit price");
println!(".stop_limit_time_in_force(TimeInForce::GTC)");
println!(".build();\n");
println!("let result = client.account().create_oco(&oco).await?;\n");
println!("// --- OTO (One-Triggers-the-Other) ---");
println!("// Working order triggers a pending order when filled");
println!("use binance_api_client::{{OtoOrderBuilder, OrderType}};\n");
println!("let oto = OtoOrderBuilder::new(");
println!(" \"BTCUSDT\",");
println!(" OrderType::Limit, // Working type");
println!(" OrderSide::Buy, // Working side");
println!(" \"65000.00\", // Working price (entry)");
println!(" \"0.001\", // Working quantity");
println!(" OrderType::Limit, // Pending type");
println!(" OrderSide::Sell, // Pending side (exit)");
println!(" \"0.001\", // Pending quantity");
println!(")");
println!(".working_time_in_force(TimeInForce::GTC)");
println!(".pending_price(\"70000.00\") // Take profit price");
println!(".pending_time_in_force(TimeInForce::GTC)");
println!(".build();\n");
println!("let result = client.account().create_oto(&oto).await?;\n");
println!("// --- OTOCO (One-Triggers-One-Cancels-Other) ---");
println!("// Entry order triggers an OCO (take-profit + stop-loss)");
println!("use binance_api_client::OtocoOrderBuilder;\n");
println!("let otoco = OtocoOrderBuilder::new(");
println!(" \"BTCUSDT\",");
println!(" OrderType::Limit, // Working (entry) type");
println!(" OrderSide::Buy, // Working side");
println!(" \"65000.00\", // Working price");
println!(" \"0.001\", // Working quantity");
println!(" OrderSide::Sell, // Exit side");
println!(" \"0.001\", // Exit quantity");
println!(" OrderType::LimitMaker, // Take profit type");
println!(")");
println!(".working_time_in_force(TimeInForce::GTC)");
println!(".pending_above_price(\"70000.00\") // Take profit");
println!(".pending_below_type(OrderType::StopLossLimit)");
println!(".pending_below_stop_price(\"60000.00\")");
println!(".pending_below_price(\"59900.00\")");
println!(".pending_below_time_in_force(TimeInForce::GTC)");
println!(".build();\n");
println!("let result = client.account().create_otoco(&otoco).await?;\n");
println!("// --- Cancel-Replace ---");
println!("// Atomically cancel and place a new order");
println!("use binance_api_client::CancelReplaceMode;\n");
println!("let cancel_replace = client.account().cancel_replace_order(");
println!(" \"BTCUSDT\",");
println!(" OrderSide::Buy,");
println!(" OrderType::Limit,");
println!(" CancelReplaceMode::StopOnFailure,");
println!(")");
println!(".cancel_order_id(12345678) // Order to cancel");
println!(".quantity(\"0.001\")");
println!(".price(\"64000.00\") // New price");
println!(".time_in_force(TimeInForce::GTC)");
println!(".build();\n");
println!("let result = client.account().cancel_replace(&cancel_replace).await?;\n");
println!("// --- Query Order Lists ---");
println!("// Get open OCO/OTO/OTOCO orders");
println!("let open_lists = client.account().open_oco_orders().await?;");
println!("let all_lists = client.account().all_oco_orders(None, None, Some(10)).await?;\n");
println!("// --- Cancel Order List ---");
println!(
"let result = client.account().cancel_oco(\"BTCUSDT\", Some(order_list_id), None).await?;\n"
);
println!("=== End of Example Code ===");
}