
estonlabs.cxtl.exchanges.hyperliquid.api.v0.lib.HyperliquidRestClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cxtl Show documentation
Show all versions of cxtl Show documentation
CXTL – Crypto eXchange Trading Library
package estonlabs.cxtl.exchanges.hyperliquid.api.v0.lib;
import java.math.BigDecimal;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import estonlabs.cxtl.exchanges.a.specification.domain.*;
import estonlabs.cxtl.exchanges.coinbase.api.v3.domain.request.CandleRequest;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import estonlabs.cxtl.common.auth.Credentials;
import estonlabs.cxtl.common.exception.CxtlApiException;
import estonlabs.cxtl.common.exception.ErrorCode;
import estonlabs.cxtl.common.http.Event;
import estonlabs.cxtl.common.http.JsonRestClient;
import estonlabs.cxtl.common.http.MetricsLogger;
import estonlabs.cxtl.common.http.RestClient;
import estonlabs.cxtl.exchanges.a.specification.lib.Cex;
import estonlabs.cxtl.exchanges.a.specification.lib.ExchangeDataInterface;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CancelOrderAction;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CancelRequest;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CreateOrderAction;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CreateOrderAction.HyperliquidOrder;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CreateOrderAction.Limit;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.CreateOrderAction.OrderType;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.ExchApiRequest;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.InfoApiRequest;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.OrderQueryRequest;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.request.OrderRequest;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.ApiResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.CancelAck;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.CancelAckResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.CreateAck;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.CreateAckResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.ExchangeApiResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.HyperliquidFilledOrder;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.HyperliquidFilledOrderList;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.HyperliquidOpenOrder;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.HyperliquidOpenOrderList;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.HyperliquidTicker;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.ListResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.OrderResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.TickerListResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.response.TradeListResponse;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.types.OrderStatus;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.domain.types.RequestType;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.util.HyperliquidSignature;
import estonlabs.cxtl.exchanges.hyperliquid.api.v0.util.HyperliquidSigner;
import reactor.core.publisher.Mono;
public class HyperliquidRestClient implements Cex, ExchangeDataInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(HyperliquidRestClient.class);
private static final String INFO_ENDPOINT_URL = "/info";
private static final String EXCHANGE_ENDPOINT_URL = "/exchange";
//These are acceptable order size range by hyperliquid API.
//"Failed to deserialize the JSON body into the target type" error will be thrown if out of these range, so need to valid the order size manually to make sure the order can be processed by hyperliquid API
private static final BigDecimal SYS_MIN_ORDER_SIZE = new BigDecimal("0.00000001");
private static final BigDecimal SYS_MAX_ORDER_SIZE = new BigDecimal("99999999999");
private final RestClientAdapter client;
public HyperliquidRestClient(JsonRestClient restClient, URI baseUri, MetricsLogger metricsLogger) {
this.client = new RestClientAdapter(restClient, baseUri, metricsLogger);
}
@Override
public Exchange getExchange() {
return Exchange.HYPERLIQUID;
}
@Override
public AssetClass[] getSupportedAssetClasses() {
return new AssetClass[] { AssetClass.PERP };
}
@Override
public Mono extends List extends Olhcv>> getOlhcv(Object request) {
return Mono.empty();
}
@NotNull
@Override
public Mono placeOrder(@NotNull Credentials credentials, @NotNull OrderRequest order) {
if(order.getSize().doubleValue() == 0) {
return Mono.error(new CxtlApiException("Order has zero size", null, ErrorCode.INVALID_QTY));
}
if(order.getSize().compareTo(HyperliquidRestClient.SYS_MAX_ORDER_SIZE)> 0) {
return Mono.error(new CxtlApiException("Order size too big", null, ErrorCode.INVALID_QTY));
}
if(order.getSize().compareTo(SYS_MIN_ORDER_SIZE) < 0) {
return Mono.error(new CxtlApiException("Order size too small", null, ErrorCode.INVALID_QTY));
}
Mono mono = createOrder(order);
CreateOrderAction action = mono.block();
try {
long nonce = System.currentTimeMillis();
HyperliquidSignature signature = HyperliquidSigner.signL1Action(credentials.getSecretKey(), action, null, System.currentTimeMillis(), true);
ExchApiRequest apiRequest = new ExchApiRequest<>(action, nonce, signature);
return client.postExchange(apiRequest, CreateAckResponse.class);
} catch (Exception e) {
return Mono.error(new CxtlApiException("Unable to create order", "SYMBOL_NOT_FOUND", ErrorCode.UNKNOWN_ERROR));
}
}
@NotNull
@Override
public Mono cancelOrder(@NotNull Credentials credentials, @NotNull CancelRequest request) {
Mono mono = cancelOrder(request);
CancelOrderAction action = mono.block();
try {
long nonce = System.currentTimeMillis();
HyperliquidSignature signature = HyperliquidSigner.signL1Action(credentials.getSecretKey(), action, null, System.currentTimeMillis(), true);
ExchApiRequest apiRequest = new ExchApiRequest<>(action, nonce, signature);
return client.postExchange(apiRequest, CancelAckResponse.class);
} catch (Exception e) {
return Mono.error(new CxtlApiException("Unable to cancel order", "SYMBOL_NOT_FOUND", ErrorCode.UNKNOWN_ERROR));
}
}
@NotNull
@Override
public Mono> getOrders(@NotNull Credentials credentials, OrderQueryRequest orderQueryRequest) {
Mono> openOrders = Mono.just(List.of());
Mono> fillOrders = Mono.just(List.of());
if(orderQueryRequest.getStatus() == OrderStatus.open || orderQueryRequest.getStatus() == null) {
InfoApiRequest request = InfoApiRequest.builder()
.type(RequestType.openOrders)
.user(credentials.getApiKey())
.build();
openOrders = client.postInfoForList(request, HyperliquidOpenOrderList.class);
}
if(orderQueryRequest.getStatus() == OrderStatus.filled || orderQueryRequest.getStatus() == null) {
InfoApiRequest request = InfoApiRequest.builder()
.type(RequestType.userFills)
.user(credentials.getApiKey())
.build();
fillOrders = client.postInfoForList(request, HyperliquidFilledOrderList.class);
}
return Mono.zip(openOrders, fillOrders)
.flatMap(tuple -> {
List combinedList = new ArrayList<>();
combinedList.addAll(tuple.getT1());
combinedList.addAll(tuple.getT2());
return Mono.just(combinedList);
});
}
@Override
public Mono extends Order> getOrder(Credentials credentials, OrderQueryRequest orderQueryRequest) {
InfoApiRequest request = InfoApiRequest.builder()
.type(RequestType.orderStatus)
.user(credentials.getApiKey())
.oid(orderQueryRequest.getOid() != null ? Long.valueOf(orderQueryRequest.getOid()): orderQueryRequest.getClOid())
.build();
return client.postInfo(request, OrderResponse.class);
}
@Override
public Mono
© 2015 - 2025 Weber Informatics LLC | Privacy Policy