use crate::prelude::*;
use crate::signing::Eip712Signer;
#[derive(Clone)]
pub struct Trader {
pub client: Client,
}
impl Trader {
pub async fn create_order(
&self,
order_request: &str,
) -> Result<CreateOrderResponse, LimitlessError> {
self.client
.post_signed("orders", Some(order_request.to_string()))
.await
}
pub async fn order_status_batch(
&self,
request_body: &str,
) -> Result<OrderStatusBatchResponse, LimitlessError> {
self.client
.post_signed("orders/status/batch", Some(request_body.to_string()))
.await
}
pub async fn cancel_combined(
&self,
request_body: &str,
) -> Result<CancelOrderResponse, LimitlessError> {
self.client
.post_signed("orders/cancel", Some(request_body.to_string()))
.await
}
pub async fn cancel_batch(
&self,
request_body: &str,
) -> Result<CancelBatchResponse, LimitlessError> {
self.client
.post_signed("orders/cancel-batch", Some(request_body.to_string()))
.await
}
pub async fn cancel_order_by_id(
&self,
order_id: &str,
) -> Result<CancelOrderResponse, LimitlessError> {
let path = format!("orders/{}", order_id);
self.client.delete_signed(&path).await
}
pub async fn cancel_all_in_market(
&self,
slug: &str,
) -> Result<CancelAllResponse, LimitlessError> {
let path = format!("orders/all/{}", slug);
self.client.delete_signed(&path).await
}
pub async fn get_orderbook(&self, slug: &str) -> Result<OrderbookResponse, LimitlessError> {
let path = format!("markets/{}/orderbook", slug);
self.client.get(&path, None).await
}
pub async fn get_historical_prices(
&self,
slug: &str,
interval: Option<&str>,
) -> Result<Vec<HistoricalPriceData>, LimitlessError> {
let mut params = BTreeMap::new();
if let Some(ref v) = interval {
params.insert("interval".into(), v.to_string());
}
let request = build_request(¶ms);
let path = format!("markets/{}/historical-price", slug);
self.client.get(&path, Some(request)).await
}
pub async fn get_locked_balance(
&self,
slug: &str,
) -> Result<LockedBalanceResponse, LimitlessError> {
let path = format!("markets/{}/locked-balance", slug);
self.client.get(&path, None).await
}
pub async fn get_user_orders(
&self,
slug: &str,
statuses: Option<&[&str]>,
limit: Option<u64>,
) -> Result<UserOrdersResponse, LimitlessError> {
let mut params = BTreeMap::new();
if let Some(s) = statuses {
params.insert("statuses".into(), s.join(","));
}
if let Some(v) = limit {
params.insert("limit".into(), v.to_string());
}
let request = build_request(¶ms);
let path = format!("markets/{}/user-orders", slug);
self.client.get(&path, Some(request)).await
}
pub async fn get_market_events(
&self,
slug: &str,
page: Option<u64>,
limit: Option<u64>,
) -> Result<MarketEventsResponse, LimitlessError> {
let mut params = BTreeMap::new();
if let Some(v) = page {
params.insert("page".into(), v.to_string());
}
if let Some(v) = limit {
params.insert("limit".into(), v.to_string());
}
let request = build_request(¶ms);
let path = format!("markets/{}/events", slug);
self.client.get(&path, Some(request)).await
}
pub async fn buy_gtc(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
price: f64,
size: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
self.place_gtc_order(
private_key,
market_slug,
token_id,
OrderSide::Buy,
price,
size,
owner_id,
)
.await
}
pub async fn sell_gtc(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
price: f64,
size: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
self.place_gtc_order(
private_key,
market_slug,
token_id,
OrderSide::Sell,
price,
size,
owner_id,
)
.await
}
pub async fn buy_fok(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
usdc_amount: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
self.place_fok_order(
private_key,
market_slug,
token_id,
OrderSide::Buy,
usdc_amount,
owner_id,
)
.await
}
pub async fn sell_fok(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
share_amount: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
self.place_fok_order(
private_key,
market_slug,
token_id,
OrderSide::Sell,
share_amount,
owner_id,
)
.await
}
pub async fn cancel_all(&self, slug: &str) -> Result<CancelAllResponse, LimitlessError> {
self.cancel_all_in_market(slug).await
}
async fn get_verifying_contract(&self, slug: &str) -> Result<String, LimitlessError> {
let market: MarketDetail = self.client.get(&format!("markets/{}", slug), None).await?;
let venue = market.venue.ok_or_else(|| {
LimitlessError::ValidationError(format!(
"Market '{}' has no venue info — is it a CLOB market?",
slug
))
})?;
Ok(venue.exchange)
}
async fn place_gtc_order(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
side: OrderSide,
price: f64,
size: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
let verifying_contract = self.get_verifying_contract(market_slug).await?;
let signer = Eip712Signer::new(private_key, &verifying_contract)
.map_err(|e| LimitlessError::ValidationError(e))?;
let order_data = signer
.build_gtc_order(
&signer.wallet_address(),
token_id,
side,
price,
size,
0, )
.map_err(|e| LimitlessError::ValidationError(e))?;
let request = CreateOrderRequest {
order: order_data,
owner_id,
order_type: OrderType::Gtc,
market_slug: market_slug.to_string(),
client_order_id: None,
on_behalf_of: None,
};
let body = serde_json::to_string(&request).map_err(|e| LimitlessError::Json(e))?;
self.create_order(&body).await
}
async fn place_fok_order(
&self,
private_key: &str,
market_slug: &str,
token_id: &str,
side: OrderSide,
amount: f64,
owner_id: u64,
) -> Result<CreateOrderResponse, LimitlessError> {
let verifying_contract = self.get_verifying_contract(market_slug).await?;
let signer = Eip712Signer::new(private_key, &verifying_contract)
.map_err(|e| LimitlessError::ValidationError(e))?;
let order_data = signer
.build_fok_order(&signer.wallet_address(), token_id, side, amount, 0)
.map_err(|e| LimitlessError::ValidationError(e))?;
let request = CreateOrderRequest {
order: order_data,
owner_id,
order_type: OrderType::Fok,
market_slug: market_slug.to_string(),
client_order_id: None,
on_behalf_of: None,
};
let body = serde_json::to_string(&request).map_err(|e| LimitlessError::Json(e))?;
self.create_order(&body).await
}
}
impl Limitless for Trader {
fn new(api_key: Option<String>, secret: Option<String>) -> Self {
Self::new_with_config(&Config::default(), api_key, secret)
}
fn new_with_config(config: &Config, api_key: Option<String>, secret: Option<String>) -> Self {
Self {
client: Client::new(api_key, secret, config.rest_api_endpoint.to_string()),
}
}
}