Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bybit-stream] implementation #4985

Merged
merged 3 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 51 additions & 54 deletions xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

import static org.knowm.xchange.bybit.dto.BybitCategory.INVERSE;
import static org.knowm.xchange.bybit.dto.BybitCategory.OPTION;
import static org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo.OptionType.CALL;
import static org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo.OptionType.PUT;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.knowm.xchange.bybit.dto.BybitCategory;
import org.knowm.xchange.bybit.dto.BybitResult;
import org.knowm.xchange.bybit.dto.account.allcoins.BybitAllCoinBalance;
Expand Down Expand Up @@ -50,7 +58,9 @@ public class BybitAdapters {
private static final ThreadLocal<SimpleDateFormat> OPTION_DATE_FORMAT =
ThreadLocal.withInitial(() -> new SimpleDateFormat("ddMMMyy"));
public static final List<String> QUOTE_CURRENCIES =
Arrays.asList("USDT", "USDC", "EUR", "BTC", "ETH", "DAI", "BRZ");
Arrays.asList("USDT", "USDC", "USDE", "EUR", "BRL", "PLN", "TRY", "SOL", "BTC", "ETH", "DAI",
"BRZ");


public static Wallet adaptBybitBalances(List<BybitCoinWalletBalance> coinWalletBalances) {
List<Balance> balances = new ArrayList<>(coinWalletBalances.size());
Expand All @@ -77,10 +87,10 @@ public static Wallet adaptBybitBalances(BybitAllCoinsBalance allCoinsBalance) {
}

public static BybitSide getSideString(Order.OrderType type) {
if (type == Order.OrderType.ASK) {
if (type == Order.OrderType.ASK || type == OrderType.EXIT_BID) {
return BybitSide.SELL;
}
if (type == Order.OrderType.BID) {
if (type == Order.OrderType.BID || type == OrderType.EXIT_ASK) {
return BybitSide.BUY;
}
throw new IllegalArgumentException("invalid order type");
Expand Down Expand Up @@ -144,6 +154,7 @@ public static String convertToBybitSymbol(Instrument instrument) {
}

public static CurrencyPair guessSymbol(String symbol) {
//SPOT Only
for (String quoteCurrency : QUOTE_CURRENCIES) {
if (symbol.endsWith(quoteCurrency)) {
int splitIndex = symbol.lastIndexOf(quoteCurrency);
Expand All @@ -154,27 +165,6 @@ public static CurrencyPair guessSymbol(String symbol) {
return new CurrencyPair(symbol.substring(0, splitIndex), symbol.substring(splitIndex));
}

public static Instrument guessSymbol(String symbol, BybitCategory category) {
switch (category) {
case SPOT:
{
return guessSymbol(symbol);
}
case LINEAR:
{
if (symbol.endsWith("USDT")) {
int splitIndex = symbol.lastIndexOf("USDT");
return new FuturesContract(
(symbol.substring(0, splitIndex) + "/" + symbol.substring(splitIndex) + "/PERP"));
} else if (symbol.endsWith("PERP")) {
int splitIndex = symbol.lastIndexOf("PERP");
return new FuturesContract((symbol.substring(0, splitIndex) + "/" + "USDC/PERP"));
}
}
}
return null;
}

public static Instrument adaptInstrumentInfo(BybitInstrumentInfo instrumentInfo) {
if (instrumentInfo instanceof BybitSpotInstrumentInfo) {
return new CurrencyPair(instrumentInfo.getBaseCoin(), instrumentInfo.getQuoteCoin());
Expand All @@ -198,7 +188,7 @@ public static Instrument adaptInstrumentInfo(BybitInstrumentInfo instrumentInfo)
.expireDate(OPTION_DATE_FORMAT.get().parse(expireDateString))
.strike(strike)
.type(
optionInstrumentInfo.getOptionsType().equals(OptionType.CALL)
optionInstrumentInfo.getOptionsType().equals(CALL)
? OptionsContract.OptionType.CALL
: OptionsContract.OptionType.PUT)
.build();
Expand Down Expand Up @@ -264,7 +254,7 @@ public static Order adaptBybitOrderDetails(BybitOrderDetail bybitOrderResult) {
case LIMIT:
builder =
new LimitOrder.Builder(
adaptOrderType(bybitOrderResult), guessSymbol(bybitOrderResult.getSymbol()))
adaptOrderType(bybitOrderResult), guessSymbol(bybitOrderResult.getSymbol()))
.limitPrice(bybitOrderResult.getPrice());
break;
default:
Expand Down Expand Up @@ -396,38 +386,45 @@ private static Builder adaptBybitTickerBuilder(

public static Instrument convertBybitSymbolToInstrument(String symbol, BybitCategory category) {
switch (category) {
case SPOT:
{
return guessSymbol(symbol);
}
case LINEAR:
{
if (symbol.endsWith("USDT")) {
int splitIndex = symbol.lastIndexOf("USDT");
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USDT"), "PERP");
}
if (symbol.endsWith("PERP")) {
int splitIndex = symbol.lastIndexOf("PERP");
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USDC"), "PERP");
}
// USDC Futures
int splitIndex = symbol.lastIndexOf("-");
case SPOT: {
return guessSymbol(symbol);
}
case LINEAR: {
if (symbol.endsWith("USDT")) {
int splitIndex = symbol.lastIndexOf("USDT");
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USDC"),
symbol.substring(splitIndex + 1));
new CurrencyPair(symbol.substring(0, splitIndex), "USDT"), "PERP");
}
case INVERSE:
{
int splitIndex = symbol.lastIndexOf("USD");
String perp = symbol.length() > splitIndex + 3 ? symbol.substring(splitIndex + 3) : "";
if (symbol.endsWith("PERP")) {
int splitIndex = symbol.lastIndexOf("PERP");
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USD"), perp);
}
case OPTION:
{
new CurrencyPair(symbol.substring(0, splitIndex), "USDC"), "PERP");
}
// USDC Futures
int splitIndex = symbol.lastIndexOf("-");
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USDC"),
symbol.substring(splitIndex + 1));
}
case INVERSE: {
int splitIndex = symbol.lastIndexOf("USD");
String perp = symbol.length() > splitIndex + 3 ? symbol.substring(splitIndex + 3) : "";
return new FuturesContract(
new CurrencyPair(symbol.substring(0, splitIndex), "USD"), perp);
}
case OPTION: {
DateTimeFormatter dateParser = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("ddLLLyy")
.toFormatter(Locale.US);
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyMMdd");
String[] tokens = symbol.split("-");
String base = tokens[0];
String quote = "USDC";
String date = dateFormat.format(LocalDate.parse(tokens[1], dateParser));
BigDecimal strike = new BigDecimal(tokens[2]);
return new OptionsContract(base + "/" + quote + "/" + date + "/" + strike + "/" + tokens[3]);
}
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@
import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
import org.knowm.xchange.bybit.dto.BybitResult;
import org.knowm.xchange.bybit.dto.account.BybitAccountInfoResponse;
import org.knowm.xchange.bybit.dto.account.BybitCancelAllOrdersPayload;
import org.knowm.xchange.bybit.dto.account.BybitCancelAllOrdersResponse;
import org.knowm.xchange.bybit.dto.account.allcoins.BybitAllCoinsBalance;
import org.knowm.xchange.bybit.dto.account.feerates.BybitFeeRates;
import org.knowm.xchange.bybit.dto.account.position.BybitSetLeveragePayload;
import org.knowm.xchange.bybit.dto.account.position.BybitSwitchModePayload;
import org.knowm.xchange.bybit.dto.account.walletbalance.BybitWalletBalance;
import org.knowm.xchange.bybit.dto.trade.BybitAmendOrderPayload;
import org.knowm.xchange.bybit.dto.trade.BybitCancelOrderPayload;
Expand Down Expand Up @@ -82,54 +87,91 @@ BybitResult<BybitOrderDetails<BybitOrderDetail>> getOpenOrders(
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/create-order">API</a>
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/cancel-order">API</a>
*/
@POST
@Path("/order/create")
@Path("/order/cancel")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitOrderResponse> placeMarketOrder(
BybitResult<BybitOrderResponse> cancelOrder(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitPlaceOrderPayload payload)
BybitCancelOrderPayload payload)
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/create-order">API</a>
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/amend-order">API</a>
*/
@POST
@Path("/order/create")
@Path("/order/amend")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitOrderResponse> placeLimitOrder(
BybitResult<BybitOrderResponse> amendOrder(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitPlaceOrderPayload payload)
BybitAmendOrderPayload payload)
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/cancel-order">API</a>
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/position/leverage">API</a>
*/
@POST
@Path("/order/cancel")
@Path("/position/set-leverage")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitOrderResponse> cancelOrder(
BybitResult<Object> setLeverage(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitCancelOrderPayload payload)
BybitSetLeveragePayload payload)
throws IOException, BybitException;

/**
* @apiSpec <https://bybit-exchange.github.io/docs/v5/order/amend-order">API</a>
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/position/position-mode">API</a>
*/
@POST
@Path("/order/amend")
@Path("/position/switch-mode")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitOrderResponse> amendOrder(
BybitResult<Object> switchMode(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitAmendOrderPayload payload)
BybitSwitchModePayload payload)
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/create-order">API</a>
*/
@POST
@Path("/order/create")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitOrderResponse> placeOrder(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitPlaceOrderPayload payload)
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/account/account-info">API</a>
*/
@GET
@Path("/account/info")
BybitResult<BybitAccountInfoResponse> getAccountInfo(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp)
throws IOException, BybitException;

/**
* @apiSpec <a href="https://bybit-exchange.github.io/docs/v5/order/cancel-all">API</a>
*/
@POST
@Path("/order/cancel-all")
@Consumes(MediaType.APPLICATION_JSON)
BybitResult<BybitCancelAllOrdersResponse> cancelAllOrders(
@HeaderParam(X_BAPI_API_KEY) String apiKey,
@HeaderParam(X_BAPI_SIGN) ParamsDigest signature,
@HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory<Long> timestamp,
BybitCancelAllOrdersPayload payload)
throws IOException, BybitException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,31 @@
import org.knowm.xchange.bybit.service.BybitMarketDataService;
import org.knowm.xchange.bybit.service.BybitMarketDataServiceRaw;
import org.knowm.xchange.bybit.service.BybitTradeService;
import org.knowm.xchange.client.ResilienceRegistries;
import org.knowm.xchange.exceptions.ExchangeException;

public class BybitExchange extends BaseExchange {
public class BybitExchange extends BaseExchange implements Exchange{

public static final String SPECIFIC_PARAM_ACCOUNT_TYPE = "accountType";
private static final String BASE_URL = "https://api.bybit.com";
// private static final String DEMO_URL = "https://api-demo.bybit.com";
private static final String DEMO_URL = "https://api-demo.bybit.com";
private static final String TESTNET_URL = "https://api-testnet.bybit.com";

// enable DEMO mode
public static final String SPECIFIC_PARAM_TESTNET = "test_net";

private static ResilienceRegistries RESILIENCE_REGISTRIES;

@Override
protected void initServices() {
marketDataService = new BybitMarketDataService(this);
tradeService = new BybitTradeService(this);
marketDataService = new BybitMarketDataService(this,getResilienceRegistries());
tradeService = new BybitTradeService(this,getResilienceRegistries());
accountService =
new BybitAccountService(
this,
(BybitAccountType)
getExchangeSpecification()
.getExchangeSpecificParametersItem(SPECIFIC_PARAM_ACCOUNT_TYPE));
.getExchangeSpecificParametersItem(SPECIFIC_PARAM_ACCOUNT_TYPE),getResilienceRegistries());
}

@Override
Expand All @@ -45,6 +51,8 @@ public ExchangeSpecification getDefaultExchangeSpecification() {
exchangeSpecification.setExchangeSpecificParametersItem(
SPECIFIC_PARAM_ACCOUNT_TYPE, BybitAccountType.UNIFIED);
exchangeSpecification.setExchangeSpecificParametersItem(Exchange.USE_SANDBOX, false);
exchangeSpecification.setExchangeSpecificParametersItem(SPECIFIC_PARAM_TESTNET, false);
exchangeSpecification.getResilience().setRateLimiterEnabled(true);
return exchangeSpecification;
}

Expand Down Expand Up @@ -108,8 +116,23 @@ public void applySpecification(ExchangeSpecification exchangeSpecification) {
if (exchangeSpecification
.getExchangeSpecificParametersItem(Exchange.USE_SANDBOX)
.equals(true)) {
exchangeSpecification.setSslUri(DEMO_URL);
}

if (exchangeSpecification.getExchangeSpecificParametersItem(SPECIFIC_PARAM_TESTNET) != null
&& exchangeSpecification
.getExchangeSpecificParametersItem(SPECIFIC_PARAM_TESTNET)
.equals(true)) {
exchangeSpecification.setSslUri(TESTNET_URL);
}
super.applySpecification(exchangeSpecification);
}

@Override
public ResilienceRegistries getResilienceRegistries() {
if (RESILIENCE_REGISTRIES == null) {
RESILIENCE_REGISTRIES = BybitResilience.createRegistries();
}
return RESILIENCE_REGISTRIES;
}
}
Loading
Loading