dydx_http_private/
http_private.rs1use std::env;
38
39use nautilus_dydx::{
40 common::consts::DYDX_TESTNET_HTTP_URL, grpc::wallet::Wallet, http::client::DydxHttpClient,
41};
42use tracing::level_filters::LevelFilter;
43
44const DEFAULT_SUBACCOUNT: u32 = 0;
45
46#[tokio::main]
47async fn main() -> Result<(), Box<dyn std::error::Error>> {
48 tracing_subscriber::fmt()
49 .with_max_level(LevelFilter::INFO)
50 .init();
51
52 let args: Vec<String> = env::args().collect();
53 let is_mainnet = args.iter().any(|a| a == "--mainnet");
54 let subaccount_number = args
55 .iter()
56 .position(|a| a == "--subaccount")
57 .and_then(|i| args.get(i + 1))
58 .and_then(|s| s.parse::<u32>().ok())
59 .unwrap_or(DEFAULT_SUBACCOUNT);
60
61 let market_filter = args
62 .iter()
63 .position(|a| a == "--market")
64 .and_then(|i| args.get(i + 1))
65 .map(|s| s.as_str());
66
67 let mnemonic = env::var("DYDX_MNEMONIC").expect("DYDX_MNEMONIC environment variable not set");
68
69 let http_url = if is_mainnet {
70 env::var("DYDX_HTTP_URL").unwrap_or_else(|_| "https://indexer.dydx.trade".to_string())
71 } else {
72 env::var("DYDX_HTTP_URL").unwrap_or_else(|_| DYDX_TESTNET_HTTP_URL.to_string())
73 };
74
75 tracing::info!("Connecting to dYdX HTTP API: {}", http_url);
76 tracing::info!(
77 "Environment: {}",
78 if is_mainnet { "MAINNET" } else { "TESTNET" }
79 );
80 tracing::info!("Subaccount: {}", subaccount_number);
81 if let Some(market) = market_filter {
82 tracing::info!("Market filter: {}", market);
83 }
84 tracing::info!("");
85
86 let wallet = Wallet::from_mnemonic(&mnemonic)?;
87 let account = wallet.account_offline(subaccount_number)?;
88 let wallet_address = account.address.clone();
89 tracing::info!("Wallet address: {}", wallet_address);
90 tracing::info!("");
91
92 let client = DydxHttpClient::new(Some(http_url), Some(30), None, !is_mainnet, None)?;
93
94 tracing::info!("Fetching subaccount info...");
95 let start = std::time::Instant::now();
96 let subaccount = client
97 .raw_client()
98 .get_subaccount(&wallet_address, subaccount_number)
99 .await?;
100 let elapsed = start.elapsed();
101
102 tracing::info!(
103 "SUCCESS: Fetched subaccount data in {:.2}s",
104 elapsed.as_secs_f64()
105 );
106 tracing::info!(
107 " Subaccount: {}/{}",
108 subaccount.subaccount.address,
109 subaccount.subaccount.subaccount_number
110 );
111 tracing::info!(" Equity: {}", subaccount.subaccount.equity);
112 tracing::info!(
113 " Free collateral: {}",
114 subaccount.subaccount.free_collateral
115 );
116 if !subaccount.subaccount.open_perpetual_positions.is_empty() {
117 tracing::info!(
118 " Open positions: {}",
119 subaccount.subaccount.open_perpetual_positions.len()
120 );
121 for pos in subaccount
122 .subaccount
123 .open_perpetual_positions
124 .values()
125 .take(5)
126 {
127 tracing::info!(
128 " - {}: size={}, entry_price={}, unrealized_pnl={}",
129 pos.market,
130 pos.size,
131 pos.entry_price,
132 pos.unrealized_pnl
133 );
134 }
135 } else {
136 tracing::info!(" Open positions: 0");
137 }
138 tracing::info!("");
139
140 tracing::info!("Fetching open orders...");
141 let start = std::time::Instant::now();
142 let orders = client
143 .raw_client()
144 .get_orders(&wallet_address, subaccount_number, market_filter, None)
145 .await?;
146 let elapsed = start.elapsed();
147
148 tracing::info!(
149 "SUCCESS: Fetched {} open orders in {:.2}s",
150 orders.len(),
151 elapsed.as_secs_f64()
152 );
153 if !orders.is_empty() {
154 tracing::info!(" Sample orders:");
155 for order in orders.iter().take(5) {
156 tracing::info!(
157 " - {}: {} {} @ {} ({})",
158 order.id,
159 order.side,
160 order.size,
161 order.price,
162 order.status
163 );
164 }
165 if orders.len() > 5 {
166 tracing::info!(" ... and {} more", orders.len() - 5);
167 }
168 }
169 tracing::info!("");
170
171 tracing::info!("Fetching recent fills...");
172 let start = std::time::Instant::now();
173 let fills = client
174 .raw_client()
175 .get_fills(&wallet_address, subaccount_number, market_filter, Some(100))
176 .await?;
177 let elapsed = start.elapsed();
178
179 tracing::info!(
180 "SUCCESS: Fetched {} fills in {:.2}s",
181 fills.fills.len(),
182 elapsed.as_secs_f64()
183 );
184 if !fills.fills.is_empty() {
185 tracing::info!(" Recent fills:");
186 for fill in fills.fills.iter().take(5) {
187 tracing::info!(
188 " - {}: {} {} @ {} (fee: {})",
189 fill.market_type,
190 fill.side,
191 fill.size,
192 fill.price,
193 fill.fee
194 );
195 }
196 if fills.fills.len() > 5 {
197 tracing::info!(" ... and {} more", fills.fills.len() - 5);
198 }
199 }
200 tracing::info!("");
201
202 tracing::info!("ALL TESTS COMPLETED SUCCESSFULLY");
203 tracing::info!("");
204 tracing::info!("Summary:");
205 tracing::info!(
206 " [PASS] get_subaccount: Equity={}, Positions={}",
207 subaccount.subaccount.equity,
208 subaccount.subaccount.open_perpetual_positions.len()
209 );
210 tracing::info!(" [PASS] get_orders: {} open orders", orders.len());
211 tracing::info!(" [PASS] get_fills: {} fills fetched", fills.fills.len());
212
213 Ok(())
214}