use std::{thread, time::Duration};
use ndaxrs::{
messages::orders::{create_limit_order, CancelOrderRequest},
ws::{NdaxWsAPI, NdaxWsConfig, PrivateConfig},
NdaxCredentials,
Side,
};
use rust_decimal_macros::dec;
fn main() {
env_logger::init();
log::info!("Starting NDAX trading example");
let credentials = match NdaxCredentials::from_env() {
Ok(c) => {
log::info!("Loaded credentials for user: {}", c.user_id);
c
}
Err(e) => {
eprintln!("Error: Failed to load credentials: {}", e);
eprintln!();
eprintln!("Please set the following environment variables:");
eprintln!(" NDAX_API_KEY - Your NDAX API key");
eprintln!(" NDAX_API_SECRET - Your NDAX API secret");
eprintln!(" NDAX_USER_ID - Your NDAX user ID");
eprintln!();
eprintln!("Or create a .env file with these values.");
return;
}
};
let account_id: u64 = match std::env::var("NDAX_ACCOUNT_ID") {
Ok(id) =>
match id.parse() {
Ok(n) => n,
Err(_) => {
eprintln!("Error: NDAX_ACCOUNT_ID must be a valid number");
return;
}
},
Err(_) => {
eprintln!("Error: NDAX_ACCOUNT_ID environment variable not set");
eprintln!("This is required for trading operations.");
return;
}
};
log::info!("Using account ID: {}", account_id);
let config = NdaxWsConfig::builder()
.credentials(credentials)
.private(PrivateConfig::new(account_id).with_account_events())
.build();
log::info!("Connecting to NDAX with authentication...");
let api = match NdaxWsAPI::new(config) {
Ok(api) => {
if api.is_authenticated() {
log::info!("Successfully connected and authenticated to NDAX");
} else {
eprintln!("Error: Connected but authentication failed");
return;
}
api
}
Err(e) => {
eprintln!("Error: Failed to connect to NDAX: {}", e);
eprintln!("Check your credentials and network connectivity.");
return;
}
};
log::info!("Fetching account positions...");
match api.get_account_positions(account_id) {
Ok(positions) => {
println!("\n=== Account Positions ===");
if positions.is_empty() {
println!(" No positions found");
} else {
for pos in &positions {
let available = pos.amount - pos.hold;
println!(
" {}: {} (available: {}, hold: {})",
pos.product_symbol, pos.amount, available, pos.hold
);
}
}
println!();
}
Err(e) => {
eprintln!("Warning: Failed to get positions: {}", e);
}
}
log::info!("Fetching available instruments...");
let btc_cad_id: u64 = match api.get_instruments() {
Ok(instruments) => {
let btc_cad = instruments
.iter()
.find(|i| i.product1_symbol == "BTC" && i.product2_symbol == "CAD");
match btc_cad {
Some(inst) => {
log::info!("Found BTC/CAD: instrument_id={}", inst.instrument_id);
inst.instrument_id as u64
}
None => {
log::info!("BTC/CAD not found, using default instrument_id=1");
1
}
}
}
Err(e) => {
log::warn!("Failed to get instruments: {}, using default", e);
1
}
};
println!("=== Placing Test Order ===");
println!(" Instrument: BTC/CAD (id={})", btc_cad_id);
println!(" Side: Buy");
println!(" Quantity: 0.0001 BTC");
println!(" Price: $1.00 CAD (far below market - won't execute)");
println!();
let order = create_limit_order(
btc_cad_id,
account_id,
Side::Buy,
dec!(0.0001), dec!(1.00), );
let order_id = match api.send_order(&order) {
Ok(response) => {
println!("Order Response:");
println!(" Status: {}", response.status);
if let Some(ref err) = response.errormsg {
if !err.is_empty() {
println!(" Message: {}", err);
}
}
if let Some(id) = response.order_id {
println!(" Order ID: {}", id);
Some(id)
} else {
println!(" Order ID: (not assigned)");
None
}
}
Err(e) => {
eprintln!("Error placing order: {}", e);
None
}
};
println!();
if order_id.is_some() {
log::info!("Waiting for order to be acknowledged...");
thread::sleep(Duration::from_secs(2));
}
if let Some(id) = order_id {
println!("=== Canceling Order ===");
println!(" Order ID: {}", id);
let cancel_request = CancelOrderRequest {
oms_id: 1,
account_id,
order_id: Some(id),
client_order_id: None,
};
match api.cancel_order(&cancel_request) {
Ok(response) => {
println!("Cancel Response:");
println!(" Result: {}", response.result);
if let Some(ref err) = response.errormsg {
if !err.is_empty() {
println!(" Message: {}", err);
}
}
}
Err(e) => {
eprintln!("Error canceling order: {}", e);
}
}
println!();
log::info!("Checking open orders...");
thread::sleep(Duration::from_secs(1));
match api.get_open_orders(account_id) {
Ok(orders) => {
println!("=== Open Orders ===");
let our_order = orders.iter().find(|o| o.order_id == id);
if our_order.is_some() {
println!(" Warning: Order {} is still open!", id);
} else {
println!(" Order {} successfully canceled (not in open orders)", id);
}
if !orders.is_empty() {
println!(" Total open orders: {}", orders.len());
}
}
Err(e) => {
eprintln!("Warning: Failed to check open orders: {}", e);
}
}
println!();
}
log::info!("Closing WebSocket connection...");
api.close();
log::info!("Disconnected. Trading example complete.");
}