Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
estonlabs.cxtl.exchanges.mexc.spot.v3.MEXCCex Maven / Gradle / Ivy
package estonlabs.cxtl.exchanges.mexc.spot.v3;
import estonlabs.cxtl.common.auth.Credentials;
import estonlabs.cxtl.common.exception.CxtlApiException;
import estonlabs.cxtl.common.exception.CxtlEventException;
import estonlabs.cxtl.common.exception.ErrorCode;
import estonlabs.cxtl.common.http.Event;
import estonlabs.cxtl.common.http.JsonRestClient;
import estonlabs.cxtl.common.http.Method;
import estonlabs.cxtl.common.http.MetricsLogger;
import estonlabs.cxtl.exchanges.a.specification.domain.AssetClass;
import estonlabs.cxtl.exchanges.a.specification.domain.Exchange;
import estonlabs.cxtl.exchanges.a.specification.lib.Cex;
import estonlabs.cxtl.exchanges.a.specification.lib.ExchangeDataInterface;
import estonlabs.cxtl.exchanges.mexc.spot.v3.domain.*;
import lombok.NonNull;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
public class MEXCCex implements Cex, ExchangeDataInterface {
private static final String API_KEY_HEADER = "X-MEXC-APIKEY";
private static final MediaType FORM_MEDIA_TYPE = MediaType.parse("application/x-www-form-urlencoded");
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
private final JsonRestClient client;
private final MetricsLogger metricsLogger;
public MEXCCex(JsonRestClient restClient, MetricsLogger metricsLogger) {
this.client = restClient;
this.metricsLogger = metricsLogger;
}
public Mono getOrderBook(String symbol, int limit) {
return handleResponse(client.get("/depth", new OrderBookRequest().setSymbol(symbol).setLimit(limit), OrderBookSnapshot.class));
}
@Override
public Mono> getLatestPublicTrades(AssetClass assetClass, String symbol) {
return handleResponse(client.getMany("/trades", new TradeRequest().setSymbol(symbol), MEXCTrade.class));
}
@Override
public Mono>> getTickers() {
return handleResponse(client.get("/exchangeInfo", ExchangeInfo.class)).map(r -> Map.of(AssetClass.SPOT, r.getSymbols()));
}
@Override
public Mono> getOlhcv(KlineRequest klineRequest) {
return handleResponse(client.getMany("/klines", klineRequest, Kline.class));
}
@Override
public Exchange getExchange() {
return Exchange.MEXC;
}
@Override
public AssetClass[] getSupportedAssetClasses() {
return new AssetClass[]{AssetClass.SPOT};
}
private Map accessKeyHeader(Credentials credentials){
return Map.of(API_KEY_HEADER, credentials.getApiKey());
}
@Override
public Mono placeOrder(Credentials credentials, NewOrderRequest order) {
return handleResponse(
client.parameterisedRequest(
Method.POST,
accessKeyHeader(credentials),
"/order",
null,
RequestBody.create(order.queryString(credentials), JSON_MEDIA_TYPE),
NewOrderResponse.class
));
}
@Override
public Mono cancelOrder(Credentials credentials, CancelRequest request) {
return handleResponse(
client.parameterisedRequest(
Method.DELETE,
accessKeyHeader(credentials),
"/order",
request.queryString(credentials),
RequestBody.create("", JSON_MEDIA_TYPE),
CancelResponse.class
));
}
@Override
public Mono> getOrders(Credentials credentials, OrderQueryRequest request) {
return handleResponse(
client.parameterisedRequestMany(
Method.GET,
accessKeyHeader(credentials),
"/order",
request.queryString(credentials),
null,
OrderQueryResponse.class
));
}
@Override
public Mono getOrder(Credentials credentials, OrderQueryRequest request) {
return handleResponse(client.parameterisedRequest(
Method.GET,
accessKeyHeader(credentials),
"/order",
request.queryString(credentials),
null,
OrderQueryResponse.class
));
}
public Mono createListenKey(Credentials credentials, long timestamp) {
var request = new WSListenKeyRequest().setTimestamp(timestamp);
return handleResponse(
client.parameterisedRequest(
Method.POST,
accessKeyHeader(credentials),
"/userDataStream",
null,
RequestBody.create(request.queryString(credentials), JSON_MEDIA_TYPE),
WSListenKey.class)
);
}
public Mono refreshListenKey(Credentials credentials, WSListenKeyRequest listenKey) {
return handleResponse(
client.parameterisedRequest(
Method.PUT,
accessKeyHeader(credentials),
"/userDataStream",
null,
RequestBody.create(listenKey.queryString(credentials), JSON_MEDIA_TYPE),
WSListenKey.class
));
}
public Mono> getOpenOrders(Credentials credentials, long timestamp, String symbol) {
var request = new OrderQueryRequest()
.setTimestamp(timestamp)
.setSymbol(symbol);
return handleResponse(client.parameterisedRequestMany(
Method.GET,
accessKeyHeader(credentials),
"/openOrders",
request.queryString(credentials),
null,
OrderQueryResponse.class
));
}
private I onSuccess(Event e) {
metricsLogger.finishedSuccess(e);
return e.getResponse();
}
@NonNull
private Mono onError(Throwable t) {
if(t instanceof CxtlEventException e){
metricsLogger.finishedError(e.getEvent());
ErrorResponse response = client.getCodec().quietFromJson(e.getEvent().getResponseJson(), ErrorResponse.class);
if (response != null){
return Mono.error(new CxtlApiException(response.toString(), response.getMsg(), mapErrorCode(response.getCode(), response.getMsg())));
}
}
return Mono.error(new CxtlApiException(t.getMessage(), "UNKNOWN", ErrorCode.UNKNOWN_ERROR, t));
}
private ErrorCode mapErrorCode(int mexcErrorCode, String msg) {
// if price is invalid, they send errCode=700004
if (mexcErrorCode == 400 && msg.equals("price and quantity must be positive")){
return ErrorCode.INVALID_QTY;
}
return switch (mexcErrorCode) {
case 10101, 30004 -> ErrorCode.INSUFFICIENT_BALANCE;
case 10095, 10096, 10097, 10102, 30032, 30029 -> ErrorCode.INVALID_QTY;
case 30010, 30026, 700004 -> ErrorCode.BAD_PX;
case 10007, 30014, 30016, 30021 -> ErrorCode.INVALID_SYMBOL;
case -2011 -> ErrorCode.UNKNOWN_ORDER;
default -> ErrorCode.UNKNOWN_ERROR;
};
}
private Mono handleResponse(Mono> response){
return response.map(this::onSuccess).onErrorResume(this::onError);
}
}