ccxt_exchanges/hyperliquid/
exchange_impl.rs

1//! Exchange trait implementation for HyperLiquid
2//!
3//! This module implements the unified `Exchange` trait from `ccxt-core` for HyperLiquid.
4
5use async_trait::async_trait;
6use ccxt_core::{
7    Result,
8    exchange::{Capability, Exchange, ExchangeCapabilities},
9    types::{
10        Balance, Market, Ohlcv, Order, OrderBook, OrderSide, OrderType, Ticker, Timeframe, Trade,
11    },
12};
13use rust_decimal::Decimal;
14use rust_decimal::prelude::ToPrimitive;
15use std::collections::HashMap;
16
17use super::HyperLiquid;
18
19#[async_trait]
20impl Exchange for HyperLiquid {
21    // ==================== Metadata ====================
22
23    fn id(&self) -> &str {
24        "hyperliquid"
25    }
26
27    fn name(&self) -> &str {
28        "HyperLiquid"
29    }
30
31    fn version(&self) -> &'static str {
32        "1"
33    }
34
35    fn certified(&self) -> bool {
36        false
37    }
38
39    fn has_websocket(&self) -> bool {
40        true
41    }
42
43    fn capabilities(&self) -> ExchangeCapabilities {
44        // HyperLiquid supports:
45        // - Market Data: markets, ticker, tickers, order_book, trades, ohlcv
46        // - Trading: create_order, cancel_order, cancel_all_orders, open_orders
47        // - Account: balance
48        // - Margin: funding_rate, positions, set_leverage
49        // - WebSocket: ticker, order_book, trades, orders
50        ExchangeCapabilities::builder()
51            .market_data()
52            .trading()
53            // Remove unsupported market data capabilities
54            .without_capability(Capability::FetchCurrencies)
55            .without_capability(Capability::FetchStatus)
56            .without_capability(Capability::FetchTime)
57            // Remove unsupported trading capabilities
58            .without_capability(Capability::EditOrder)
59            .without_capability(Capability::FetchOrder)
60            .without_capability(Capability::FetchOrders)
61            .without_capability(Capability::FetchClosedOrders)
62            .without_capability(Capability::FetchCanceledOrders)
63            // Add account capabilities
64            .capability(Capability::FetchBalance)
65            // Add margin capabilities
66            .capability(Capability::FetchFundingRate)
67            .capability(Capability::FetchPositions)
68            .capability(Capability::SetLeverage)
69            // Add WebSocket capabilities
70            .capability(Capability::Websocket)
71            .capability(Capability::WatchTicker)
72            .capability(Capability::WatchOrderBook)
73            .capability(Capability::WatchTrades)
74            .capability(Capability::WatchOrders)
75            .build()
76    }
77
78    fn timeframes(&self) -> Vec<Timeframe> {
79        vec![
80            Timeframe::M1,
81            Timeframe::M5,
82            Timeframe::M15,
83            Timeframe::M30,
84            Timeframe::H1,
85            Timeframe::H4,
86            Timeframe::D1,
87            Timeframe::W1,
88        ]
89    }
90
91    fn rate_limit(&self) -> u32 {
92        100
93    }
94
95    // ==================== Market Data (Public API) ====================
96
97    async fn fetch_markets(&self) -> Result<Vec<Market>> {
98        let markets = HyperLiquid::fetch_markets(self).await?;
99        Ok(markets.into_values().map(|m| (*m).clone()).collect())
100    }
101
102    async fn load_markets(&self, reload: bool) -> Result<HashMap<String, Market>> {
103        let markets = HyperLiquid::load_markets(self, reload).await?;
104        Ok(markets
105            .into_iter()
106            .map(|(k, v)| (k, (*v).clone()))
107            .collect())
108    }
109
110    async fn fetch_ticker(&self, symbol: &str) -> Result<Ticker> {
111        HyperLiquid::fetch_ticker(self, symbol).await
112    }
113
114    async fn fetch_tickers(&self, symbols: Option<&[String]>) -> Result<Vec<Ticker>> {
115        let symbols_vec = symbols.map(|s| s.to_vec());
116        HyperLiquid::fetch_tickers(self, symbols_vec).await
117    }
118
119    async fn fetch_order_book(&self, symbol: &str, limit: Option<u32>) -> Result<OrderBook> {
120        HyperLiquid::fetch_order_book(self, symbol, limit).await
121    }
122
123    async fn fetch_trades(&self, symbol: &str, limit: Option<u32>) -> Result<Vec<Trade>> {
124        HyperLiquid::fetch_trades(self, symbol, limit).await
125    }
126
127    async fn fetch_ohlcv(
128        &self,
129        symbol: &str,
130        timeframe: Timeframe,
131        since: Option<i64>,
132        limit: Option<u32>,
133    ) -> Result<Vec<Ohlcv>> {
134        let timeframe_str = timeframe.to_string();
135        HyperLiquid::fetch_ohlcv(self, symbol, &timeframe_str, since, limit).await
136    }
137
138    // ==================== Trading (Private API) ====================
139
140    async fn create_order(
141        &self,
142        symbol: &str,
143        order_type: OrderType,
144        side: OrderSide,
145        amount: Decimal,
146        price: Option<Decimal>,
147    ) -> Result<Order> {
148        let amount_f64 = amount
149            .to_f64()
150            .ok_or_else(|| ccxt_core::Error::invalid_request("Failed to convert amount to f64"))?;
151        let price_f64 = match price {
152            Some(p) => Some(p.to_f64().ok_or_else(|| {
153                ccxt_core::Error::invalid_request("Failed to convert price to f64")
154            })?),
155            None => None,
156        };
157
158        HyperLiquid::create_order(self, symbol, order_type, side, amount_f64, price_f64).await
159    }
160
161    async fn cancel_order(&self, id: &str, symbol: Option<&str>) -> Result<Order> {
162        let symbol_str = symbol.ok_or_else(|| {
163            ccxt_core::Error::invalid_request("Symbol is required for cancel_order on HyperLiquid")
164        })?;
165        HyperLiquid::cancel_order(self, id, symbol_str).await
166    }
167
168    async fn cancel_all_orders(&self, symbol: Option<&str>) -> Result<Vec<Order>> {
169        HyperLiquid::cancel_all_orders(self, symbol).await
170    }
171
172    async fn fetch_order(&self, _id: &str, _symbol: Option<&str>) -> Result<Order> {
173        Err(ccxt_core::Error::not_implemented("fetch_order"))
174    }
175
176    async fn fetch_open_orders(
177        &self,
178        symbol: Option<&str>,
179        since: Option<i64>,
180        limit: Option<u32>,
181    ) -> Result<Vec<Order>> {
182        HyperLiquid::fetch_open_orders(self, symbol, since, limit).await
183    }
184
185    async fn fetch_closed_orders(
186        &self,
187        _symbol: Option<&str>,
188        _since: Option<i64>,
189        _limit: Option<u32>,
190    ) -> Result<Vec<Order>> {
191        Err(ccxt_core::Error::not_implemented("fetch_closed_orders"))
192    }
193
194    // ==================== Account (Private API) ====================
195
196    async fn fetch_balance(&self) -> Result<Balance> {
197        HyperLiquid::fetch_balance(self).await
198    }
199
200    async fn fetch_my_trades(
201        &self,
202        _symbol: Option<&str>,
203        _since: Option<i64>,
204        _limit: Option<u32>,
205    ) -> Result<Vec<Trade>> {
206        Err(ccxt_core::Error::not_implemented("fetch_my_trades"))
207    }
208
209    // ==================== Helper Methods ====================
210
211    async fn market(&self, symbol: &str) -> Result<Market> {
212        self.base().market(symbol).await.map(|m| (*m).clone())
213    }
214
215    async fn markets(&self) -> HashMap<String, Market> {
216        let cache = self.base().market_cache.read().await;
217        cache
218            .markets
219            .iter()
220            .map(|(k, v)| (k.clone(), (**v).clone()))
221            .collect()
222    }
223}