synor/sdk/rust/src/dex/perps.rs
Gulshan Yadav e7dc8f70a0 feat(sdk): implement DEX SDK with perpetual futures for all 12 languages
Complete decentralized exchange client implementation featuring:
- AMM swaps (constant product, stable, concentrated liquidity)
- Liquidity provision with impermanent loss tracking
- Perpetual futures (up to 100x leverage, funding rates, liquidation)
- Order books with limit orders (GTC, IOC, FOK, GTD)
- Yield farming & staking with reward claiming
- Real-time WebSocket subscriptions
- Analytics (OHLCV, trade history, volume, TVL)

Languages: JS/TS, Python, Go, Rust, Java, Kotlin, Swift, Flutter,
C, C++, C#, Ruby
2026-01-28 12:32:04 +05:30

222 lines
7.1 KiB
Rust

//! Perpetuals Client
use crate::dex::types::*;
use reqwest::Client;
use serde_json::json;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
/// Perpetuals sub-client
pub struct PerpsClient {
config: DexConfig,
client: Client,
closed: Arc<AtomicBool>,
}
impl PerpsClient {
pub(crate) fn new(config: DexConfig, client: Client, closed: Arc<AtomicBool>) -> Self {
Self { config, client, closed }
}
/// List all perpetual markets
pub async fn list_markets(&self) -> Result<Vec<PerpMarket>, DexError> {
self.get("/perps/markets").await
}
/// Get a specific perpetual market
pub async fn get_market(&self, symbol: &str) -> Result<PerpMarket, DexError> {
self.get(&format!("/perps/markets/{}", symbol)).await
}
/// Open a perpetual position
pub async fn open_position(&self, params: OpenPositionParams) -> Result<PerpPosition, DexError> {
let mut body = json!({
"market": params.market,
"side": params.side,
"size": params.size,
"leverage": params.leverage,
"order_type": params.order_type,
"margin_type": params.margin_type,
"reduce_only": params.reduce_only,
});
if let Some(price) = params.limit_price {
body["limit_price"] = json!(price);
}
if let Some(sl) = params.stop_loss {
body["stop_loss"] = json!(sl);
}
if let Some(tp) = params.take_profit {
body["take_profit"] = json!(tp);
}
self.post("/perps/positions", body).await
}
/// Close a perpetual position
pub async fn close_position(&self, params: ClosePositionParams) -> Result<PerpPosition, DexError> {
let mut body = json!({
"market": params.market,
"order_type": params.order_type,
});
if let Some(size) = params.size {
body["size"] = json!(size);
}
if let Some(price) = params.limit_price {
body["limit_price"] = json!(price);
}
self.post("/perps/positions/close", body).await
}
/// Modify a perpetual position
pub async fn modify_position(&self, params: ModifyPositionParams) -> Result<PerpPosition, DexError> {
let mut body = json!({});
if let Some(leverage) = params.new_leverage {
body["new_leverage"] = json!(leverage);
}
if let Some(margin) = params.new_margin {
body["new_margin"] = json!(margin);
}
if let Some(sl) = params.new_stop_loss {
body["new_stop_loss"] = json!(sl);
}
if let Some(tp) = params.new_take_profit {
body["new_take_profit"] = json!(tp);
}
self.post(&format!("/perps/positions/{}/modify", params.position_id), body).await
}
/// Get all open positions
pub async fn get_positions(&self) -> Result<Vec<PerpPosition>, DexError> {
self.get("/perps/positions").await
}
/// Get position for a specific market
pub async fn get_position(&self, market: &str) -> Result<Option<PerpPosition>, DexError> {
self.get(&format!("/perps/positions/{}", market)).await
}
/// Get all open orders
pub async fn get_orders(&self) -> Result<Vec<PerpOrder>, DexError> {
self.get("/perps/orders").await
}
/// Cancel an order
pub async fn cancel_order(&self, order_id: &str) -> Result<(), DexError> {
self.delete(&format!("/perps/orders/{}", order_id)).await
}
/// Cancel all orders
pub async fn cancel_all_orders(&self, market: Option<&str>) -> Result<u32, DexError> {
let path = match market {
Some(m) => format!("/perps/orders?market={}", m),
None => "/perps/orders".to_string(),
};
#[derive(serde::Deserialize)]
struct CancelResult {
cancelled: u32,
}
let result: CancelResult = self.delete(&path).await?;
Ok(result.cancelled)
}
/// Get funding payment history
pub async fn get_funding_history(&self, market: &str, limit: u32) -> Result<Vec<FundingPayment>, DexError> {
self.get(&format!("/perps/funding/{}?limit={}", market, limit)).await
}
/// Get current funding rate
pub async fn get_funding_rate(&self, market: &str) -> Result<FundingRateInfo, DexError> {
self.get(&format!("/perps/funding/{}/current", market)).await
}
// Internal methods
async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T, DexError> {
use std::sync::atomic::Ordering;
if self.closed.load(Ordering::SeqCst) {
return Err(DexError::ClientClosed);
}
let url = format!("{}{}", self.config.endpoint, path);
let response = self.client
.get(&url)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("X-SDK-Version", "rust/0.1.0")
.send()
.await?;
if !response.status().is_success() {
return Err(DexError::Http {
message: format!("HTTP {}", response.status()),
code: None,
status: Some(response.status().as_u16()),
});
}
response.json().await.map_err(DexError::from)
}
async fn post<T: serde::de::DeserializeOwned>(&self, path: &str, body: serde_json::Value) -> Result<T, DexError> {
use std::sync::atomic::Ordering;
if self.closed.load(Ordering::SeqCst) {
return Err(DexError::ClientClosed);
}
let url = format!("{}{}", self.config.endpoint, path);
let response = self.client
.post(&url)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("X-SDK-Version", "rust/0.1.0")
.json(&body)
.send()
.await?;
if !response.status().is_success() {
return Err(DexError::Http {
message: format!("HTTP {}", response.status()),
code: None,
status: Some(response.status().as_u16()),
});
}
response.json().await.map_err(DexError::from)
}
async fn delete<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T, DexError> {
use std::sync::atomic::Ordering;
if self.closed.load(Ordering::SeqCst) {
return Err(DexError::ClientClosed);
}
let url = format!("{}{}", self.config.endpoint, path);
let response = self.client
.delete(&url)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("X-SDK-Version", "rust/0.1.0")
.send()
.await?;
if !response.status().is_success() {
return Err(DexError::Http {
message: format!("HTTP {}", response.status()),
code: None,
status: Some(response.status().as_u16()),
});
}
response.json().await.map_err(DexError::from)
}
}