diff --git a/pom.xml b/pom.xml index 9e3af921e02..e55c6d48b09 100644 --- a/pom.xml +++ b/pom.xml @@ -211,6 +211,7 @@ xchange-stream-service-pubnub xchange-stream-coincheck xchange-stream-krakenfutures + xchange-stream-bybit diff --git a/xchange-bibox/src/main/java/org/knowm/xchange/bibox/dto/BiboxAdapters.java b/xchange-bibox/src/main/java/org/knowm/xchange/bibox/dto/BiboxAdapters.java index 6d5642fde96..c250e2813c1 100644 --- a/xchange-bibox/src/main/java/org/knowm/xchange/bibox/dto/BiboxAdapters.java +++ b/xchange-bibox/src/main/java/org/knowm/xchange/bibox/dto/BiboxAdapters.java @@ -172,33 +172,25 @@ public static Date convert(String s) { } public static FundingRecord adaptDeposit(BiboxDeposit d) { - return new FundingRecord( - d.to, - d.getCreatedAt(), - Currency.getInstance(d.coinSymbol), - d.amount, - null, - null, - Type.DEPOSIT, - convertStatus(d.status), - null, - null, - null); + return FundingRecord.builder() + .address(d.to) + .date(d.getCreatedAt()) + .currency(Currency.getInstance(d.coinSymbol)) + .amount(d.amount) + .type(Type.DEPOSIT) + .status(convertStatus(d.status)) + .build(); } public static FundingRecord adaptDeposit(BiboxWithdrawal w) { - return new FundingRecord( - w.toAddress, - w.getCreatedAt(), - Currency.getInstance(w.coinSymbol), - w.amountReal, - null, - null, - Type.WITHDRAWAL, - convertStatus(w.status), - null, - null, - null); + return FundingRecord.builder() + .address(w.toAddress) + .date(w.getCreatedAt()) + .currency(Currency.getInstance(w.coinSymbol)) + .amount(w.amountReal) + .type(Type.WITHDRAWAL) + .status(convertStatus(w.status)) + .build(); } public static Status convertStatus(int status) { diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceAccountService.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceAccountService.java index 6aab63f855e..a3cd9d962b3 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceAccountService.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceAccountService.java @@ -288,57 +288,49 @@ public List getFundingHistory(TradeHistoryParams params) throws I super.withdrawHistory(asset, startTime, endTime) .forEach( w -> result.add( - new FundingRecord( - w.getAddress(), - w.getAddressTag(), - BinanceAdapters.toDate(w.getApplyTime()), - Currency.getInstance(w.getCoin()), - w.getAmount(), - w.getId(), - w.getTxId(), - Type.WITHDRAWAL, - withdrawStatus(w.getStatus()), - null, - w.getTransactionFee(), - null))); + FundingRecord.builder() + .address(w.getAddress()) + .addressTag(w.getAddressTag()) + .date(BinanceAdapters.toDate(w.getApplyTime())) + .currency(Currency.getInstance(w.getCoin())) + .amount(w.getAmount()) + .internalId(w.getId()) + .blockchainTransactionHash(w.getTxId()) + .type(Type.WITHDRAWAL) + .status(withdrawStatus(w.getStatus())) + .fee(w.getTransactionFee()) + .build())); } if (deposits) { super.depositHistory(asset, startTime, endTime) .forEach( d -> result.add( - new FundingRecord( - d.getAddress(), - d.getAddressTag(), - new Date(d.getInsertTime()), - Currency.getInstance(d.getCoin()), - d.getAmount(), - null, - d.getTxId(), - Type.DEPOSIT, - depositStatus(d.getStatus()), - null, - null, - null))); + FundingRecord.builder() + .address(d.getAddress()) + .addressTag(d.getAddressTag()) + .date(new Date(d.getInsertTime())) + .currency(Currency.getInstance(d.getCoin())) + .amount(d.getAmount()) + .blockchainTransactionHash(d.getTxId()) + .type(Type.DEPOSIT) + .status(depositStatus(d.getStatus())) + .build())); } if (otherInflow) { super.getAssetDividend(asset, startTime, endTime) .forEach( a -> result.add( - new FundingRecord( - null, - null, - new Date(a.getDivTime()), - Currency.getInstance(a.getAsset()), - a.getAmount(), - null, - String.valueOf(a.getTranId()), - Type.OTHER_INFLOW, - Status.COMPLETE, - null, - null, - a.getEnInfo()))); + FundingRecord.builder() + .date(new Date(a.getDivTime())) + .currency(Currency.getInstance(a.getAsset())) + .amount(a.getAmount()) + .blockchainTransactionHash(String.valueOf(a.getTranId())) + .type(Type.OTHER_INFLOW) + .status(Status.COMPLETE) + .description(a.getEnInfo()) + .build())); } final String finalEmail = email; @@ -347,13 +339,13 @@ public List getFundingHistory(TradeHistoryParams params) throws I super.getTransferHistory(email, startTime, endTime, page, limit) .forEach( a -> result.add( - new FundingRecord.Builder() - .setAddress(finalEmail) - .setDate(new Date(a.getTime())) - .setCurrency(Currency.getInstance(a.getAsset())) - .setAmount(a.getQty()) - .setType(Type.INTERNAL_WITHDRAWAL) - .setStatus(transferHistoryStatus(a.getStatus())) + FundingRecord.builder() + .address(finalEmail) + .date(new Date(a.getTime())) + .currency(Currency.getInstance(a.getAsset())) + .amount(a.getQty()) + .type(Type.INTERNAL_WITHDRAWAL) + .status(transferHistoryStatus(a.getStatus())) .build())); } @@ -363,16 +355,16 @@ public List getFundingHistory(TradeHistoryParams params) throws I super.getSubUserHistory(asset, type, startTime, endTime, limit) .forEach( a -> result.add( - new FundingRecord.Builder() - .setAddress(a.getEmail()) - .setDate(new Date(a.getTime())) - .setCurrency(Currency.getInstance(a.getAsset())) - .setAmount(a.getQty()) - .setType( + FundingRecord.builder() + .address(a.getEmail()) + .date(new Date(a.getTime())) + .currency(Currency.getInstance(a.getAsset())) + .amount(a.getQty()) + .type( a.getType().equals(1) ? Type.INTERNAL_DEPOSIT : Type.INTERNAL_WITHDRAWAL) - .setStatus(Status.COMPLETE) + .status(Status.COMPLETE) .build())); } diff --git a/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/service/BitbayAccountServiceRaw.java b/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/service/BitbayAccountServiceRaw.java index 12df1e6daa6..9e917a5ab4e 100644 --- a/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/service/BitbayAccountServiceRaw.java +++ b/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/service/BitbayAccountServiceRaw.java @@ -5,6 +5,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; @@ -13,6 +14,7 @@ import org.knowm.xchange.bitbay.dto.acount.BitbayAccountInfoResponse; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.exceptions.ExchangeException; /** @author Z. Dolezal */ @@ -103,18 +105,15 @@ else if (map.get("operation_type").toString().equals("-transfer")) else continue; res.add( - new FundingRecord( - null, - dateFormat.parse(map.get("time").toString()), - Currency.getInstance(map.get("currency").toString()), - new BigDecimal(map.get("amount").toString()), - map.get("id").toString(), - null, - type, - FundingRecord.Status.COMPLETE, - null, - null, - map.get("comment").toString())); + FundingRecord.builder() + .date(dateFormat.parse(map.get("time").toString())) + .currency(Currency.getInstance(map.get("currency").toString())) + .amount(new BigDecimal(map.get("amount").toString())) + .internalId(map.get("id").toString()) + .type(type) + .status(Status.COMPLETE) + .description(map.get("comment").toString()) + .build()); } catch (ParseException e) { throw new IllegalStateException("Should not happen", e); } diff --git a/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/v3/service/BitbayAccountService.java b/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/v3/service/BitbayAccountService.java index 35003687aaa..04dad9af454 100644 --- a/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/v3/service/BitbayAccountService.java +++ b/xchange-bitbay/src/main/java/org/knowm/xchange/bitbay/v3/service/BitbayAccountService.java @@ -86,17 +86,17 @@ private static FundingRecord adaptFundingRecord(BitbayBalanceHistoryEntry item) ? FundingRecord.Type.WITHDRAWAL : FundingRecord.Type.DEPOSIT; - return new FundingRecord.Builder() - .setType(type) - .setBlockchainTransactionHash(null) // not available in the API yet - .setAddress(null) // not available in the API yet - .setAmount(item.getValue().abs()) - .setCurrency(Currency.getInstance(item.getBalance().getCurrency())) - .setDate(DateUtils.fromMillisUtc(item.getTime())) - .setInternalId(item.getHistoryId().toString()) - .setFee(null) // not available in the API yet - .setStatus(FundingRecord.Status.COMPLETE) - .setBalance(item.getFundsAfter().getTotal()) + return FundingRecord.builder() + .type(type) + .blockchainTransactionHash(null) // not available in the API yet + .address(null) // not available in the API yet + .amount(item.getValue().abs()) + .currency(Currency.getInstance(item.getBalance().getCurrency())) + .date(DateUtils.fromMillisUtc(item.getTime())) + .internalId(item.getHistoryId().toString()) + .fee(null) // not available in the API yet + .status(FundingRecord.Status.COMPLETE) + .balance(item.getFundsAfter().getTotal()) .build(); } } diff --git a/xchange-bitcoinde/src/main/java/org/knowm/xchange/bitcoinde/v4/BitcoindeAdapters.java b/xchange-bitcoinde/src/main/java/org/knowm/xchange/bitcoinde/v4/BitcoindeAdapters.java index c5999c2a92b..dd84b1a6314 100644 --- a/xchange-bitcoinde/src/main/java/org/knowm/xchange/bitcoinde/v4/BitcoindeAdapters.java +++ b/xchange-bitcoinde/src/main/java/org/knowm/xchange/bitcoinde/v4/BitcoindeAdapters.java @@ -284,20 +284,20 @@ public static List adaptFundingHistory( ledger -> { FundingRecord.Type type = adaptFundingRecordType(ledger.getType()); - FundingRecord.Builder builder = - new FundingRecord.Builder() - .setType(type) - .setDate(ledger.getDate()) - .setCurrency(currency) - .setAmount(ledger.getCashflow().abs()) - .setBalance(ledger.getBalance()) - .setStatus(FundingRecord.Status.COMPLETE) - .setDescription(ledger.getType().getValue()); + FundingRecord.FundingRecordBuilder builder = + FundingRecord.builder() + .type(type) + .date(ledger.getDate()) + .currency(currency) + .amount(ledger.getCashflow().abs()) + .balance(ledger.getBalance()) + .status(FundingRecord.Status.COMPLETE) + .description(ledger.getType().getValue()); if (INPAYMENT == ledger.getType() || PAYOUT == ledger.getType()) { - builder.setBlockchainTransactionHash(ledger.getReference()); + builder.blockchainTransactionHash(ledger.getReference()); } else { - builder.setInternalId(ledger.getReference()); + builder.internalId(ledger.getReference()); } if (!leaveFeesSeperate && PAYOUT == ledger.getType()) { @@ -305,8 +305,8 @@ public static List adaptFundingHistory( findFeeLedger(ledger.getReference(), feeLedgers); if (feeLedger.isPresent()) { BigDecimal fee = feeLedger.get().getCashflow().abs(); - builder.setAmount(ledger.getCashflow().abs().add(fee)); - builder.setFee(fee); + builder.amount(ledger.getCashflow().abs().add(fee)); + builder.fee(fee); /* * There can be multiple {@code PAYOUTS}s with the same reference/ blockchain diff --git a/xchange-bitfinex/src/main/java/org/knowm/xchange/bitfinex/service/BitfinexAdapters.java b/xchange-bitfinex/src/main/java/org/knowm/xchange/bitfinex/service/BitfinexAdapters.java index 6690dd6e279..f209b9cac90 100644 --- a/xchange-bitfinex/src/main/java/org/knowm/xchange/bitfinex/service/BitfinexAdapters.java +++ b/xchange-bitfinex/src/main/java/org/knowm/xchange/bitfinex/service/BitfinexAdapters.java @@ -48,6 +48,8 @@ import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -805,19 +807,17 @@ public static List adaptFundingHistory(List movementHis } FundingRecord fundingRecordEntry = - new FundingRecord( - movement.getDestinationAddress(), - null, - movement.getMtsUpdated(), - currency, - amount, - movement.getId(), - movement.getTransactionId(), - type, - status, - null, - fee, - null); + FundingRecord.builder() + .address(movement.getDestinationAddress()) + .date(movement.getMtsStarted()) + .currency(currency) + .amount(amount) + .internalId(movement.getId()) + .blockchainTransactionHash(movement.getTransactionId()) + .type(type) + .status(status) + .fee(fee) + .build(); fundingRecords.add(fundingRecordEntry); } @@ -868,18 +868,17 @@ and sometimes like this (with the address in it as well as the txn hash): } FundingRecord fundingRecordEntry = - new FundingRecord( - address, - responseEntry.getTimestamp(), - currency, - responseEntry.getAmount(), - String.valueOf(responseEntry.getId()), - txnId, - responseEntry.getType(), - status, - null, - null, - description); + FundingRecord.builder() + .address(address) + .date(responseEntry.getTimestamp()) + .currency(currency) + .amount(responseEntry.getAmount()) + .internalId(String.valueOf(responseEntry.getId())) + .blockchainTransactionHash(txnId) + .type(responseEntry.getType()) + .status(status) + .description(description) + .build(); fundingRecords.add(fundingRecordEntry); } diff --git a/xchange-bitflyer/src/main/java/org/knowm/xchange/bitflyer/BitflyerAdapters.java b/xchange-bitflyer/src/main/java/org/knowm/xchange/bitflyer/BitflyerAdapters.java index 6d87c982e6b..8f7d4ea3590 100755 --- a/xchange-bitflyer/src/main/java/org/knowm/xchange/bitflyer/BitflyerAdapters.java +++ b/xchange-bitflyer/src/main/java/org/knowm/xchange/bitflyer/BitflyerAdapters.java @@ -121,29 +121,29 @@ public static List adaptFundingRecordsFromDepositHistory( public static FundingRecord adaptFundingRecord( BitflyerCoinHistory history, FundingRecord.Type type) { - return new FundingRecord.Builder() - .setDate(BitflyerUtils.parseDate(history.getEventDate())) - .setCurrency(new Currency(history.getCurrencyCode())) - .setAmount(history.getAmount()) - .setAddress(history.getAddress()) - .setInternalId(history.getID()) - .setType(type) - .setStatus(adaptStatus(history.getStatus())) - .setBalance(history.getAmount()) - .setFee(add(history.getFee(), history.getAdditionalFee())) + return FundingRecord.builder() + .date(BitflyerUtils.parseDate(history.getEventDate())) + .currency(new Currency(history.getCurrencyCode())) + .amount(history.getAmount()) + .address(history.getAddress()) + .internalId(history.getID()) + .type(type) + .status(adaptStatus(history.getStatus())) + .balance(history.getAmount()) + .fee(add(history.getFee(), history.getAdditionalFee())) .build(); } public static FundingRecord adaptFundingRecord( BitflyerDepositOrWithdrawal history, FundingRecord.Type type) { - return new FundingRecord.Builder() - .setDate(BitflyerUtils.parseDate(history.getEventDate())) - .setCurrency(new Currency(history.getCurrencyCode())) - .setAmount(history.getAmount()) - .setInternalId(history.getID()) - .setType(type) - .setStatus(adaptStatus(history.getStatus())) - .setBalance(history.getAmount()) + return FundingRecord.builder() + .date(BitflyerUtils.parseDate(history.getEventDate())) + .currency(new Currency(history.getCurrencyCode())) + .amount(history.getAmount()) + .internalId(history.getID()) + .type(type) + .status(adaptStatus(history.getStatus())) + .balance(history.getAmount()) .build(); } diff --git a/xchange-bitmex/src/main/java/org/knowm/xchange/bitmex/BitmexAdapters.java b/xchange-bitmex/src/main/java/org/knowm/xchange/bitmex/BitmexAdapters.java index ede595c8ddb..764f6542105 100644 --- a/xchange-bitmex/src/main/java/org/knowm/xchange/bitmex/BitmexAdapters.java +++ b/xchange-bitmex/src/main/java/org/knowm/xchange/bitmex/BitmexAdapters.java @@ -380,18 +380,19 @@ private static OrderType convertType(String side) { } public static FundingRecord adaptFundingRecord(BitmexWalletTransaction walletTransaction) { - return new FundingRecord( - walletTransaction.getAddress(), - walletTransaction.getTransactTime(), - adaptCurrency(walletTransaction.getCurrency()), - walletTransaction.getAmount().abs(), - walletTransaction.getTransactID(), - walletTransaction.getTx(), - adaptFundingRecordtype(walletTransaction), - adaptFundingRecordStatus(walletTransaction.getTransactStatus()), - walletTransaction.getWalletBalance(), - walletTransaction.getFee(), - walletTransaction.getText()); + return FundingRecord.builder() + .address(walletTransaction.getAddress()) + .date(walletTransaction.getTransactTime()) + .currency(adaptCurrency(walletTransaction.getCurrency())) + .amount(walletTransaction.getAmount().abs()) + .internalId(walletTransaction.getTransactID()) + .blockchainTransactionHash(walletTransaction.getTx()) + .type(adaptFundingRecordtype(walletTransaction)) + .status(adaptFundingRecordStatus(walletTransaction.getTransactStatus())) + .balance(walletTransaction.getWalletBalance()) + .fee(walletTransaction.getFee()) + .description(walletTransaction.getText()) + .build(); } private static FundingRecord.Type adaptFundingRecordtype( diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAdapters.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAdapters.java index 48bb0455c03..6b846f5241f 100644 --- a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAdapters.java +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAdapters.java @@ -28,6 +28,7 @@ import org.knowm.xchange.dto.account.AccountInfo; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -284,18 +285,15 @@ public static List adaptFundingHistory( } FundingRecord record = - new FundingRecord( - null, - trans.getDatetime(), - Currency.getInstance(amount.getKey()), - amount.getValue().abs(), - String.valueOf(trans.getId()), - null, - type, - FundingRecord.Status.COMPLETE, - null, - trans.getFee(), - null); + FundingRecord.builder() + .date(trans.getDatetime()) + .currency(Currency.getInstance(amount.getKey())) + .amount(amount.getValue().abs()) + .internalId(String.valueOf(trans.getId())) + .type(type) + .status(Status.COMPLETE) + .fee(trans.getFee()) + .build(); fundingRecords.add(record); } } diff --git a/xchange-bl3p/src/main/java/org/knowm/xchange/bl3p/Bl3pAdapters.java b/xchange-bl3p/src/main/java/org/knowm/xchange/bl3p/Bl3pAdapters.java index a98064816bc..6cc55e0667e 100644 --- a/xchange-bl3p/src/main/java/org/knowm/xchange/bl3p/Bl3pAdapters.java +++ b/xchange-bl3p/src/main/java/org/knowm/xchange/bl3p/Bl3pAdapters.java @@ -110,13 +110,13 @@ public static List adaptUserTransactionsToFundingRecords( for (Bl3pUserTransactions.Bl3pUserTransaction tx : transactions) { list.add( - new FundingRecord.Builder() - .setAmount(tx.amount.value) - .setBalance(tx.balance.value) - .setCurrency(Currency.getInstance(tx.amount.currency)) - .setDate(tx.date) - .setFee(tx.fee == null ? null : tx.fee.value) - .setType( + FundingRecord.builder() + .amount(tx.amount.value) + .balance(tx.balance.value) + .currency(Currency.getInstance(tx.amount.currency)) + .date(tx.date) + .fee(tx.fee == null ? null : tx.fee.value) + .type( tx.type == "deposit" ? FundingRecord.Type.DEPOSIT : FundingRecord.Type.WITHDRAWAL) .build()); } diff --git a/xchange-bleutrade/src/main/java/org/knowm/xchange/bleutrade/service/BleutradeAccountService.java b/xchange-bleutrade/src/main/java/org/knowm/xchange/bleutrade/service/BleutradeAccountService.java index e821b39c93a..cc8b7ea7822 100644 --- a/xchange-bleutrade/src/main/java/org/knowm/xchange/bleutrade/service/BleutradeAccountService.java +++ b/xchange-bleutrade/src/main/java/org/knowm/xchange/bleutrade/service/BleutradeAccountService.java @@ -5,6 +5,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.TimeZone; import org.knowm.xchange.Exchange; @@ -13,6 +14,8 @@ import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AccountInfo; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.exceptions.NotAvailableFromExchangeException; import org.knowm.xchange.service.account.AccountService; import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams; @@ -88,34 +91,31 @@ public List getFundingHistory(TradeHistoryParams params) throws I } fundingRecords.add( - new FundingRecord( - address, - dateFormat.parse(record.timestamp), - Currency.getInstance(record.coin), - amount, - record.id, - record.transactionId, - FundingRecord.Type.WITHDRAWAL, - FundingRecord.Status.COMPLETE, - null, - fee, - label)); + FundingRecord.builder() + .address(address) + .date(dateFormat.parse(record.timestamp)) + .currency(Currency.getInstance(record.coin)) + .amount(amount) + .internalId(record.id) + .blockchainTransactionHash(record.transactionId) + .type(Type.WITHDRAWAL) + .status(Status.COMPLETE) + .fee(fee) + .description(label) + .build()); } for (DepositRecord record : depositHistory()) { fundingRecords.add( - new FundingRecord( - null, - dateFormat.parse(record.timestamp), - Currency.getInstance(record.coin), - record.amount, - record.id, - null, - FundingRecord.Type.DEPOSIT, - FundingRecord.Status.COMPLETE, - null, - null, - record.label)); + FundingRecord.builder() + .date(dateFormat.parse(record.timestamp)) + .currency(Currency.getInstance(record.coin)) + .amount(record.amount) + .internalId(record.id) + .type(Type.DEPOSIT) + .status(Status.COMPLETE) + .description(record.label) + .build()); } } catch (ParseException e) { throw new IllegalStateException("Should not happen", e); diff --git a/xchange-blockchain/src/main/java/org/knowm/xchange/blockchain/BlockchainAdapters.java b/xchange-blockchain/src/main/java/org/knowm/xchange/blockchain/BlockchainAdapters.java index ce415765960..d4f3d6e72eb 100644 --- a/xchange-blockchain/src/main/java/org/knowm/xchange/blockchain/BlockchainAdapters.java +++ b/xchange-blockchain/src/main/java/org/knowm/xchange/blockchain/BlockchainAdapters.java @@ -13,6 +13,8 @@ import org.knowm.xchange.dto.Order; import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Trades; import org.knowm.xchange.dto.meta.CurrencyMetaData; @@ -75,35 +77,29 @@ public static FundingRecord.Status toDepositStatus(String status) { } public static FundingRecord toFundingWithdrawal(BlockchainWithdrawal w){ - return new FundingRecord( - w.getBeneficiary(), - null, - w.getTimestamp(), - w.getCurrency(), - w.getAmount(), - w.getWithdrawalId(), - null, - FundingRecord.Type.WITHDRAWAL, - BlockchainAdapters.toWithdrawStatus(w.getState()), - null, - w.getFee(), - null); + return FundingRecord.builder() + .address(w.getBeneficiary()) + .date(w.getTimestamp()) + .currency(w.getCurrency()) + .amount(w.getAmount()) + .internalId(w.getWithdrawalId()) + .type(Type.WITHDRAWAL) + .status(BlockchainAdapters.toWithdrawStatus(w.getState())) + .fee(w.getFee()) + .build(); } public static FundingRecord toFundingDeposit(BlockchainDeposits d){ - return new FundingRecord( - d.getAddress(), - null, - d.getTimestamp(), - d.getCurrency(), - d.getAmount(), - d.getDepositId(), - d.getTxHash(), - FundingRecord.Type.DEPOSIT, - BlockchainAdapters.toDepositStatus(d.getState()), - null, - null, - null); + return FundingRecord.builder() + .address(d.getAddress()) + .date(d.getTimestamp()) + .currency(d.getCurrency()) + .amount(d.getAmount()) + .internalId(d.getDepositId()) + .blockchainTransactionHash(d.getTxHash()) + .type(Type.DEPOSIT) + .status(BlockchainAdapters.toDepositStatus(d.getState())) + .build(); } public static CurrencyPair toCurrencyPairBySymbol(BlockchainSymbol blockchainSymbol) { diff --git a/xchange-btcmarkets/src/main/java/org/knowm/xchange/btcmarkets/BTCMarketsAdapters.java b/xchange-btcmarkets/src/main/java/org/knowm/xchange/btcmarkets/BTCMarketsAdapters.java index 2ceec8877a6..4e4878dd611 100644 --- a/xchange-btcmarkets/src/main/java/org/knowm/xchange/btcmarkets/BTCMarketsAdapters.java +++ b/xchange-btcmarkets/src/main/java/org/knowm/xchange/btcmarkets/BTCMarketsAdapters.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -244,18 +245,18 @@ public static List adaptFundingHistory( } result.add( - new FundingRecord( - address, - transfer.getCreationTime(), - Currency.getInstance(transfer.getCurrency()), - transfer.getAmount(), - Long.toString(transfer.getFundTransferId()), - blockchainTransactionHash, - fundingrecordType, - fundingRecordStatus, - null, - transfer.getFee(), - transfer.getDescription())); + FundingRecord.builder() + .address(address) + .date(transfer.getCreationTime()) + .currency(Currency.getInstance(transfer.getCurrency())) + .amount(transfer.getAmount()) + .internalId(Long.toString(transfer.getFundTransferId())) + .blockchainTransactionHash(blockchainTransactionHash) + .type(fundingrecordType) + .status(fundingRecordStatus) + .fee(transfer.getFee()) + .description(transfer.getDescription()) + .build()); } return result; } diff --git a/xchange-btcturk/src/main/java/org/knowm/xchange/btcturk/BTCTurkAdapters.java b/xchange-btcturk/src/main/java/org/knowm/xchange/btcturk/BTCTurkAdapters.java index 606a75c23e9..822ec40ef18 100644 --- a/xchange-btcturk/src/main/java/org/knowm/xchange/btcturk/BTCTurkAdapters.java +++ b/xchange-btcturk/src/main/java/org/knowm/xchange/btcturk/BTCTurkAdapters.java @@ -208,15 +208,15 @@ public static FundingRecord adaptTransaction(BTCTurkUserTransactions transaction description += ", index: " + transaction.getId(); } - return new FundingRecord.Builder() - .setInternalId(transaction.getId().toString()) - .setDate(transaction.getDate()) - .setType(transaction.getOperation().getType()) - .setCurrency(transaction.getCurrency()) - .setAmount(transaction.getAmount()) - .setFee(transaction.getFee()) - .setBalance(transaction.getFunds()) - .setDescription(description) + return FundingRecord.builder() + .internalId(transaction.getId().toString()) + .date(transaction.getDate()) + .type(transaction.getOperation().getType()) + .currency(transaction.getCurrency()) + .amount(transaction.getAmount()) + .fee(transaction.getFee()) + .balance(transaction.getFunds()) + .description(description) .build(); } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/Bybit.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/Bybit.java index cfcb9778c52..458d68fb193 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/Bybit.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/Bybit.java @@ -1,33 +1,46 @@ package org.knowm.xchange.bybit; -import java.io.IOException; -import java.util.List; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; +import java.io.IOException; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.marketdata.BybitSymbol; -import org.knowm.xchange.bybit.dto.marketdata.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers; import org.knowm.xchange.bybit.service.BybitException; -@Path("") +@Path("/v5/market") @Produces(MediaType.APPLICATION_JSON) public interface Bybit { - /** - * @apiSpec API - */ + /** @apiSpec API */ @GET - @Path("/v2/public/tickers") - BybitResult> getTicker24h(@QueryParam("symbol") String symbol) throws IOException, BybitException; + @Path("/tickers") + BybitResult> getTicker24h( + @QueryParam("category") String category, @QueryParam("symbol") String symbol) + throws IOException, BybitException; - /** - * @apiSpec API - */ + /** @apiSpec API */ @GET - @Path("/v2/public/symbols") - BybitResult> getSymbols() throws IOException, BybitException; + @Path("/instruments-info") + BybitResult> getInstrumentsInfo( + @QueryParam("category") String category, + @QueryParam("symbol") String symbol, + @QueryParam("status") String status, + @QueryParam("baseCoin") String baseCoin, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor -} \ No newline at end of file + ) throws IOException, BybitException; + + @GET + @Path("/asset/transfer/query-asset-info") + BybitResult getAssetsInfo( + @QueryParam("accountType") String accountType, + @QueryParam("coin") String coin + ) throws IOException, BybitException; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java index 9178a3b8929..c2ff18af7ea 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java @@ -1,50 +1,107 @@ package org.knowm.xchange.bybit; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.account.BybitBalance; -import org.knowm.xchange.bybit.dto.trade.BybitOrderDetails; +import org.knowm.xchange.bybit.dto.account.BybitDepositRecordsResponse.BybitDepositRecord; +import org.knowm.xchange.bybit.dto.account.BybitInternalDepositRecordsResponse.BybitInternalDepositRecord; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse.BybitTransactionLog; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse.BybitTransactionLog.BybitTransactionLogType; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse.BybitTransfer; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse.BybitTransferStatus; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance.BybitCoinBalance; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse.BybitWithdrawRecord; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo.InstrumentStatus; +import org.knowm.xchange.bybit.dto.marketdata.instruments.linear.BybitLinearInverseInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.spot.BybitSpotInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.linear.BybitLinearInverseTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.option.BybitOptionTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.spot.BybitSpotTicker; +import org.knowm.xchange.bybit.dto.trade.BybitOrderStatus; +import org.knowm.xchange.bybit.dto.trade.BybitSide; +import org.knowm.xchange.bybit.dto.trade.BybitTradeHistoryResponse; +import org.knowm.xchange.bybit.dto.trade.BybitUserTradeDto; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; import org.knowm.xchange.bybit.service.BybitException; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.derivative.FuturesContract; +import org.knowm.xchange.derivative.OptionsContract; +import org.knowm.xchange.derivative.OptionsContract.OptionType; import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.account.Balance; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.account.Wallet; +import org.knowm.xchange.dto.account.Wallet.WalletFeature; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.marketdata.Ticker.Builder; +import org.knowm.xchange.dto.meta.CurrencyMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.dto.trade.LimitOrder; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.instrument.Instrument; public class BybitAdapters { - public static final List QUOTE_CURRENCIES = Arrays.asList("USDT", "USDC", "BTC", "DAI"); + protected static final List QUOTE_CURRENCIES = + Arrays.asList("USDT", "USDC", "BTC", "DAI", "EUR", "ETH"); + + public static final String FUTURES_CONTRACT_QUOTE_CURRENCY = "USD"; + + private static final String BYBIT_PERPETUAL = "PERP"; - public static Wallet adaptBybitBalances(List bybitBalances) { - List balances = new ArrayList<>(bybitBalances.size()); - for (BybitBalance bybitBalance : bybitBalances) { - balances.add(new Balance(new Currency(bybitBalance.getCoin()), - new BigDecimal(bybitBalance.getTotal()), - new BigDecimal(bybitBalance.getFree()) - )); + public static final SimpleDateFormat OPTIONS_EXPIRED_DATE_PARSER = new SimpleDateFormat("ddMMMyy", Locale.ENGLISH); + + public static Wallet adaptBybitBalances(BybitAllCoinsBalance allCoinsBalance, Set features) { + List balances = new ArrayList<>(allCoinsBalance.getBalance().size()); + for (BybitCoinBalance coinBalance : allCoinsBalance.getBalance()) { + balances.add( + new Balance( + new Currency(coinBalance.getCoin()), + coinBalance.getWalletBalance(), + coinBalance.getTransferBalance())); } - return Wallet.Builder.from(balances).build(); + + return Wallet.Builder.from(balances) + .id(allCoinsBalance.getAccountType().name()) + .features(features) + .build(); } - public static String getSideString(Order.OrderType type) { - if (type == Order.OrderType.ASK) - return "Sell"; - if (type == Order.OrderType.BID) - return "Buy"; + public static BybitSide getSideString(Order.OrderType type) { + if (type == Order.OrderType.ASK) { + return BybitSide.SELL; + } + if (type == Order.OrderType.BID) { + return BybitSide.BUY; + } throw new IllegalArgumentException("invalid order type"); } - public static Order.OrderType getOrderType(String side) { - if ("sell".equalsIgnoreCase(side)) { + public static Order.OrderType getOrderType(BybitSide side) { + if ("sell".equalsIgnoreCase(side.name())) { return Order.OrderType.ASK; } - if ("buy".equalsIgnoreCase(side)) { + if ("buy".equalsIgnoreCase(side.name())) { return Order.OrderType.BID; } throw new IllegalArgumentException("invalid order type"); @@ -63,31 +120,478 @@ public static CurrencyPair guessSymbol(String symbol) { } int splitIndex = symbol.length() - 3; return new CurrencyPair(symbol.substring(0, splitIndex), symbol.substring(splitIndex)); - } - public static LimitOrder adaptBybitOrderDetails(BybitOrderDetails bybitOrderResult) { - LimitOrder limitOrder = new LimitOrder( + public static LimitOrder adaptBybitOrderDetails(BybitOrderDetail bybitOrderResult) { + LimitOrder limitOrder = + new LimitOrder( getOrderType(bybitOrderResult.getSide()), - new BigDecimal(bybitOrderResult.getOrigQty()), - new BigDecimal(bybitOrderResult.getExecutedQty()), + bybitOrderResult.getQty(), + bybitOrderResult.getCumExecQty(), guessSymbol(bybitOrderResult.getSymbol()), bybitOrderResult.getOrderId(), - new Date(Long.parseLong(bybitOrderResult.getTime())), - new BigDecimal(bybitOrderResult.getPrice())) { - }; - BigDecimal averagePrice = new BigDecimal(bybitOrderResult.getAvgPrice()); - limitOrder.setAveragePrice(averagePrice); - limitOrder.setOrderStatus(Order.OrderStatus.valueOf(bybitOrderResult.getStatus())); + bybitOrderResult.getCreatedTime(), + bybitOrderResult.getPrice()) {}; + limitOrder.setAveragePrice(bybitOrderResult.getAvgPrice()); + limitOrder.setOrderStatus(adaptBybitOrderStatus(bybitOrderResult.getOrderStatus())); return limitOrder; } + private static OrderStatus adaptBybitOrderStatus(BybitOrderStatus orderStatus) { + switch (orderStatus) { + case CREATED: + return OrderStatus.OPEN; + case NEW: + return OrderStatus.NEW; + case REJECTED: + return OrderStatus.REJECTED; + case PARTIALLY_FILLED: + case ACTIVE: + return OrderStatus.PARTIALLY_FILLED; + case PARTIALLY_FILLED_CANCELED: + return OrderStatus.PARTIALLY_CANCELED; + case FILLED: + return OrderStatus.FILLED; + case CANCELLED: + return OrderStatus.CANCELED; + case UNTRIGGERED: + case TRIGGERED: + return OrderStatus.UNKNOWN; + case DEACTIVATED: + return OrderStatus.STOPPED; + default: + throw new IllegalStateException("Unexpected value: " + orderStatus); + } + } + public static BybitException createBybitExceptionFromResult(BybitResult walletBalances) { return new BybitException( - walletBalances.getRetCode(), - walletBalances.getRetMsg(), - walletBalances.getExtCode(), - walletBalances.getExtCode() - ); + walletBalances.getRetCode(), walletBalances.getRetMsg(), walletBalances.getRetExtInfo()); + } + + public static Ticker adaptBybitLinearInverseTicker( + Instrument instrument, Date time, BybitLinearInverseTicker bybitTicker) { + return adaptBybitTickerBuilder(instrument, time, bybitTicker) + .open(bybitTicker.getPrevPrice24h()) + .percentageChange(bybitTicker.getPrice24hPcnt()) + .build(); + } + + public static Ticker adaptBybitSpotTicker( + Instrument instrument, Date time, BybitSpotTicker bybitTicker) { + return adaptBybitTickerBuilder(instrument, time, bybitTicker) + .open(bybitTicker.getPrevPrice24h()) + .percentageChange(bybitTicker.getPrice24hPcnt()) + .build(); + } + + public static Ticker adaptBybitOptionTicker( + Instrument instrument, Date time, BybitOptionTicker bybitTicker) { + return adaptBybitTickerBuilder(instrument, time, bybitTicker).build(); + } + + private static Builder adaptBybitTickerBuilder( + Instrument instrument, Date time, BybitTicker bybitTicker) { + return new Ticker.Builder() + .timestamp(time) + .instrument(instrument) + .last(bybitTicker.getLastPrice()) + .bid(bybitTicker.getBid1Price()) + .bidSize(bybitTicker.getBid1Size()) + .ask(bybitTicker.getAsk1Price()) + .askSize(bybitTicker.getAsk1Size()) + .high(bybitTicker.getHighPrice24h()) + .low(bybitTicker.getLowPrice24h()) + .quoteVolume(bybitTicker.getTurnover24h()) + .volume(bybitTicker.getVolume24h()); + } + + public static Map adaptBybitInstruments( + List instrumentList) { + Map map = new HashMap<>(); + + instrumentList.forEach( + info -> { + if (info instanceof BybitSpotInstrumentInfo) { + BybitSpotInstrumentInfo spotInstrumentInfo = (BybitSpotInstrumentInfo) info; + map.put( + adaptInstrument(spotInstrumentInfo.getSymbol(), BybitCategory.SPOT), + new InstrumentMetaData.Builder() + .minimumAmount(spotInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .maximumAmount(spotInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .counterMinimumAmount(spotInstrumentInfo.getLotSizeFilter().getMinOrderAmt()) + .counterMaximumAmount(spotInstrumentInfo.getLotSizeFilter().getMaxOrderAmt()) + .priceScale(spotInstrumentInfo.getPriceFilter().getTickSize().scale()) + .volumeScale(spotInstrumentInfo.getLotSizeFilter().getBasePrecision().scale()) + .amountStepSize(spotInstrumentInfo.getLotSizeFilter().getBasePrecision()) + .priceStepSize(spotInstrumentInfo.getPriceFilter().getTickSize()) + .marketOrderEnabled( + spotInstrumentInfo.getStatus().equals(InstrumentStatus.TRADING)) + .build()); + } else if (info instanceof BybitLinearInverseInstrumentInfo) { + BybitLinearInverseInstrumentInfo perpetualInstrumentInfo = + (BybitLinearInverseInstrumentInfo) info; + map.put( + adaptInstrument(perpetualInstrumentInfo.getSymbol(), BybitCategory.LINEAR), + new InstrumentMetaData.Builder() + .minimumAmount(perpetualInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .maximumAmount(perpetualInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .counterMinimumAmount( + perpetualInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .counterMaximumAmount( + perpetualInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .priceScale(perpetualInstrumentInfo.getPriceScale()) + .volumeScale(perpetualInstrumentInfo.getLotSizeFilter().getQtyStep().scale()) + .amountStepSize(perpetualInstrumentInfo.getLotSizeFilter().getQtyStep()) + .priceStepSize(perpetualInstrumentInfo.getPriceFilter().getTickSize()) + .marketOrderEnabled( + perpetualInstrumentInfo.getStatus().equals(InstrumentStatus.TRADING)) + .build()); + } else if (info instanceof BybitOptionInstrumentInfo) { + BybitOptionInstrumentInfo optionsInstrumentInfo = (BybitOptionInstrumentInfo) info; + map.put( + adaptInstrument(optionsInstrumentInfo.getSymbol(), BybitCategory.OPTION), + new InstrumentMetaData.Builder() + .minimumAmount(optionsInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .maximumAmount(optionsInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .counterMinimumAmount(optionsInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .counterMaximumAmount(optionsInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .priceScale(optionsInstrumentInfo.getPriceFilter().getTickSize().scale()) + .volumeScale(optionsInstrumentInfo.getLotSizeFilter().getQtyStep().scale()) + .amountStepSize(optionsInstrumentInfo.getLotSizeFilter().getQtyStep()) + .priceStepSize(optionsInstrumentInfo.getPriceFilter().getTickSize()) + .marketOrderEnabled(optionsInstrumentInfo.getStatus().equals(InstrumentStatus.TRADING)) + .build()); + } + }); + return map; + } + + public static OrderType adaptSide(BybitSide side) { + return (side.equals(BybitSide.BUY)) ? OrderType.BID : OrderType.ASK; + } + + public static String getBybitQuoteCurrency(String symbol) { + String quoteCurrency = FUTURES_CONTRACT_QUOTE_CURRENCY; + + for (String quote : QUOTE_CURRENCIES) { + if (symbol.endsWith(quote)) { + quoteCurrency = quote; + break; + } + } + + return quoteCurrency; + } + + public static Currency getFeeCurrency( + boolean isMaker, BigDecimal feeRate, Instrument instrument, BybitSide side) { + if (instrument instanceof CurrencyPair) { + if (isMaker && feeRate.compareTo(BigDecimal.ZERO) > 0) { + return (side.equals(BybitSide.BUY) + ? ((CurrencyPair) instrument).base + : ((CurrencyPair) instrument).counter); + } else { + if (isMaker) { + return (side.equals(BybitSide.BUY) + ? ((CurrencyPair) instrument).counter + : ((CurrencyPair) instrument).base); + } else { + return (side.equals(BybitSide.BUY) + ? ((CurrencyPair) instrument).base + : ((CurrencyPair) instrument).counter); + } + } + } else { + return instrument.getCounter(); + } + } + + @SneakyThrows + public static Instrument adaptInstrument(String symbol, BybitCategory category) { + Instrument instrument = null; + + String quoteCurrency = getBybitQuoteCurrency(symbol); + + if (category.equals(BybitCategory.SPOT)) { + String baseCurrency = symbol.substring(0, symbol.length() - quoteCurrency.length()); + + instrument = new CurrencyPair(baseCurrency, quoteCurrency); + } else if (category.equals(BybitCategory.LINEAR) || category.equals(BybitCategory.INVERSE)) { + instrument = + (symbol.contains("-")) + ? new FuturesContract(new CurrencyPair(symbol.substring(0, symbol.indexOf("-")), quoteCurrency), symbol.substring(symbol.indexOf("-") + 1)) + : new FuturesContract(new CurrencyPair(symbol.substring(0, symbol.length() - quoteCurrency.length()), quoteCurrency), BYBIT_PERPETUAL); + } else if (category.equals(BybitCategory.OPTION)) { + int secondIndex = symbol.indexOf("-", symbol.indexOf("-") + 1); // second index of "-" after the first one + instrument = + new OptionsContract.Builder() + .currencyPair(new CurrencyPair(symbol.substring(0, symbol.indexOf("-")), quoteCurrency)) + .expireDate(OPTIONS_EXPIRED_DATE_PARSER.parse(symbol.substring(symbol.indexOf("-") + 1, secondIndex))) + .strike(new BigDecimal(symbol.substring(secondIndex + 1, symbol.lastIndexOf("-")))) + .type(symbol.contains("C") ? OptionType.CALL : OptionType.PUT) + .build(); + } + + return instrument; + } + + public static BybitCategory getBybitCategoryFromInstrument(Instrument instrument) { + int count = StringUtils.countMatches(instrument.toString(), "/"); + if(count == 1){ + return BybitCategory.SPOT; + } else if(count == 4){ + return BybitCategory.OPTION; + } else if(instrument.getCounter().equals(Currency.USDC) || instrument.getCounter().equals(Currency.USDT)){ + return BybitCategory.LINEAR; + } else { + return BybitCategory.INVERSE; + } + } + + public static List adaptUserTrades(BybitTradeHistoryResponse result) { + List userTrades = new ArrayList<>(); + + result.getTradeHistoryList().forEach(bybitUserTradeDto -> userTrades.add(adaptUserTrade(bybitUserTradeDto, result.getCategory()))); + + return userTrades; + } + + public static UserTrade adaptUserTrade(BybitUserTradeDto bybitUserTradeDto, BybitCategory bybitCategory) { + Instrument instrument = BybitAdapters.adaptInstrument(bybitUserTradeDto.getSymbol(), bybitCategory); + return new UserTrade.Builder() + .instrument(instrument) + .feeAmount(bybitUserTradeDto.getExecFee()) + .type(BybitAdapters.adaptSide(bybitUserTradeDto.getSide())) + .orderUserReference(bybitUserTradeDto.getOrderLinkId()) + .id(bybitUserTradeDto.getExecId()) + .orderId(bybitUserTradeDto.getOrderId()) + .originalAmount(bybitUserTradeDto.getExecQty()) + .price(bybitUserTradeDto.getExecPrice()) + .timestamp(bybitUserTradeDto.getExecTime()) + .feeCurrency((instrument == null) ? null : BybitAdapters.getFeeCurrency(bybitUserTradeDto.getIsMaker(), bybitUserTradeDto.getFeeRate(), instrument , bybitUserTradeDto.getSide())) + .build(); + } + + public static String adaptBybitSymbol(Instrument instrument) { + if(instrument instanceof CurrencyPair){ + return instrument.toString().replace("/",""); + } else if(instrument instanceof OptionsContract){ + return instrument.toString().replace("/","-"); + } else if(instrument.toString().contains(BYBIT_PERPETUAL)){ + return instrument.toString().replace("/","").replace(BYBIT_PERPETUAL,""); + } else { + return instrument.toString().replace("/","-"); + } + } + + public static Map adaptBybitCurrencies(List list) { + Map currencyCurrencyMetaDataMap = new HashMap<>(); + + list.forEach(bybitInstrumentInfo -> { + BybitSpotInstrumentInfo spotInfo = (BybitSpotInstrumentInfo) bybitInstrumentInfo; + + if(!currencyCurrencyMetaDataMap.containsKey(new Currency(spotInfo.getBaseCoin()))){ + currencyCurrencyMetaDataMap.put( + new Currency(spotInfo.getBaseCoin()), + CurrencyMetaData.builder() + .scale(spotInfo.getLotSizeFilter().getBasePrecision().scale()) + .build()); + } + if(!currencyCurrencyMetaDataMap.containsKey(new Currency(spotInfo.getQuoteCoin()))) { + currencyCurrencyMetaDataMap.put( + new Currency(spotInfo.getQuoteCoin()), + CurrencyMetaData.builder() + .scale(spotInfo.getLotSizeFilter().getQuotePrecision().scale()) + .build()); + } + }); + + return currencyCurrencyMetaDataMap; + } + + public static List adaptBybitInternalTransfers(List internalTransfers) { + List fundingRecords = new ArrayList<>(); + + internalTransfers.forEach(internalTransfer -> fundingRecords.add(FundingRecord.builder() + .internalId(internalTransfer.getTransferId()) + .currency(new Currency(internalTransfer.getCoin())) + .amount(internalTransfer.getAmount()) + .date(internalTransfer.getTimestamp()) + .type(Type.INTERNAL_WALLET_TRANSFER) + .status(Status.resolveStatus(internalTransfer.getStatus().name())) + .fromWallet(internalTransfer.getFromAccountType().name()) + .toWallet(internalTransfer.getToAccountType().name()) + .description(internalTransfer.getFromAccountType().name()+"->"+internalTransfer.getToAccountType().name()) + .build())); + return fundingRecords; + } + + public static List adaptBybitWithdrawRecords(List withdrawRecords) { + List fundingRecords = new ArrayList<>(); + + withdrawRecords.forEach(withdrawRecord -> fundingRecords.add(FundingRecord.builder() + .internalId(withdrawRecord.getWithdrawId()) + .blockchainTransactionHash(withdrawRecord.getTxID()) + .addressTag(withdrawRecord.getTag()) + .address(withdrawRecord.getToAddress()) + .currency(new Currency(withdrawRecord.getCoin())) + .type(withdrawRecord.getWithdrawType() == 0 ? Type.WITHDRAWAL: Type.INTERNAL_WITHDRAWAL) + .amount(withdrawRecord.getAmount()) + .date(withdrawRecord.getCreateTime()) + .status(Status.resolveStatus(withdrawRecord.getStatus().name())) + .fee(withdrawRecord.getWithdrawFee()) + .description(withdrawRecord.getChain()) + .build())); + return fundingRecords; + } + + public static List adaptBybitUniversalTransfers(List universalTransfers) { + List fundingRecords = new ArrayList<>(); + + universalTransfers.forEach(universalTransfer -> fundingRecords.add(FundingRecord.builder() + .internalId(universalTransfer.getTransferId()) + .currency(Currency.getInstance(universalTransfer.getCoin())) + .amount(universalTransfer.getAmount()) + .date(universalTransfer.getTimestamp()) + .type(Type.INTERNAL_SUB_ACCOUNT_TRANSFER) + .status(Status.resolveStatus(universalTransfer.getStatus().name())) + .toSubAccount(universalTransfer.getToMember()) + .fromSubAccount(universalTransfer.getFromMember()) + .toWallet(universalTransfer.getToAccountType().name()) + .fromWallet(universalTransfer.getFromAccountType().name()) + .description(universalTransfer.getFromMember()+"."+universalTransfer.getFromAccountType().name()+"->"+universalTransfer.getToMember()+"."+universalTransfer.getToAccountType().name()) + .build())); + + return fundingRecords; + } + + + public static List adaptBybitDepositRecords(List bybitDepositRecords) { + List fundingRecords = new ArrayList<>(); + + bybitDepositRecords.forEach(depositRecord -> fundingRecords.add(FundingRecord.builder() + .internalId(depositRecord.getTxID()) + .addressTag(depositRecord.getTag()) + .address((depositRecord.getToAddress() == null) ? "" : depositRecord.getToAddress()) + .type(Type.DEPOSIT) + .fee((depositRecord.getDepositFee() == null) ? BigDecimal.ZERO : depositRecord.getDepositFee()) + .blockchainTransactionHash(depositRecord.getBlockHash()) + .currency(Currency.getInstance(depositRecord.getCoin())) + .amount(depositRecord.getAmount()) + .date(depositRecord.getSuccessAt()) + .status(Status.resolveStatus(depositRecord.getStatus().name())) + .description(depositRecord.getDepositType().name()) + .build())); + + return fundingRecords; + } + + public static List adaptBybitInternalDepositRecords(List bybitInternalDepositRecords) { + List fundingRecords = new ArrayList<>(); + + bybitInternalDepositRecords.forEach(internalRecord -> fundingRecords.add(FundingRecord.builder() + .internalId(internalRecord.getId()) + .address(internalRecord.getAddress()) + .type(Type.INTERNAL_DEPOSIT) + .currency(Currency.getInstance(internalRecord.getCoin())) + .amount(internalRecord.getAmount()) + .date(internalRecord.getCreatedTime()) + .status(Status.resolveStatus(internalRecord.getStatus().name())) + .build())); + + return fundingRecords; + } + + public static BybitTransferStatus convertToBybitStatus(FundingRecord.Status status) { + BybitTransferStatus bybitStatus = null; + + if(status != null){ + switch (status){ + case CANCELLED: + case FAILED: + bybitStatus = BybitTransferStatus.FAILED; + break; + case COMPLETE: + bybitStatus = BybitTransferStatus.SUCCESS; + break; + case PROCESSING: + bybitStatus = BybitTransferStatus.PENDING; + break; + default: + break; + } + } + + return bybitStatus; + } + + public static List adaptBybitLedger(List list) { + List fundingRecords = new ArrayList<>(); + + list.forEach( + bybitTransactionLog -> fundingRecords.add( + FundingRecord.builder() + .currency(Currency.getInstance(bybitTransactionLog.getCurrency())) + .balance(bybitTransactionLog.getCashBalance()) + .internalId(bybitTransactionLog.getId()) + .fee(bybitTransactionLog.getFee()) + .amount(bybitTransactionLog.getChange()) + .type(convertToFundingRecordType(bybitTransactionLog.getType())) + .status(Status.COMPLETE) + .date(bybitTransactionLog.getTransactionTime()) + .build())); + + return fundingRecords; + } + + private static Type convertToFundingRecordType(BybitTransactionLogType type) { + Type fundingRecordType = null; + + if(type != null){ + switch (type) { + case TRANSFER_IN: + case TRANSFER_IN_INS_LOAN: + fundingRecordType = Type.DEPOSIT; + break; + case TRANSFER_OUT: + case TRANSFER_OUT_INS_LOAN: + fundingRecordType = Type.WITHDRAWAL; + break; + case TRADE: + case CURRENCY_BUY: + case CURRENCY_SELL: + case SPOT_REPAYMENT_BUY: + case SPOT_REPAYMENT_SELL: + case AUTO_BUY_LIABILITY_INS_LOAN: + case LIQUIDATION: + case AUTO_SOLD_COLLATERAL_INS_LOAN: + fundingRecordType = Type.TRADE; + break; + case DELIVERY: + fundingRecordType = Type.DELIVERY; + break; + case INTEREST: + fundingRecordType = Type.INTEREST; + break; + case SETTLEMENT: + fundingRecordType = Type.SETTLEMENT; + break; + case BONUS: + case FEE_REFUND: + case BORROWED_AMOUNT_INS_LOAN: + fundingRecordType = Type.OTHER_INFLOW; + break; + case AUTO_PRINCIPLE_REPAYMENT_INS_LOAN: + case PRINCIPLE_REPAYMENT_INS_LOAN: + case INTEREST_REPAYMENT_INS_LOAN: + case AUTO_INTEREST_REPAYMENT_INS_LOAN: + fundingRecordType = Type.OTHER_OUTFLOW; + break; + default: + break; + } + } + return fundingRecordType; } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java index ffadff217ca..e202fc2447c 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java @@ -1,49 +1,238 @@ package org.knowm.xchange.bybit; +import static org.knowm.xchange.bybit.service.BybitDigest.X_BAPI_API_KEY; +import static org.knowm.xchange.bybit.service.BybitDigest.X_BAPI_SIGN; +import static org.knowm.xchange.bybit.service.BybitDigest.X_BAPI_TIMESTAMP; + +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import java.io.IOException; +import java.math.BigDecimal; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.account.BybitBalances; -import org.knowm.xchange.bybit.dto.trade.BybitOrderDetails; -import org.knowm.xchange.bybit.dto.trade.BybitOrderRequest; +import org.knowm.xchange.bybit.dto.account.BybitDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitInternalDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance; +import org.knowm.xchange.bybit.dto.account.BybitFeeRates; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitWalletBalance; +import org.knowm.xchange.bybit.dto.trade.BybitTradeHistoryResponse; +import org.knowm.xchange.bybit.dto.trade.BybitOrderResponse; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails; import org.knowm.xchange.bybit.service.BybitException; import si.mazi.rescu.ParamsDigest; import si.mazi.rescu.SynchronizedValueFactory; -import jakarta.ws.rs.*; -import jakarta.ws.rs.core.MediaType; -import java.io.IOException; - -@Path("/spot/v1") +@Path("/v5") @Produces(MediaType.APPLICATION_JSON) public interface BybitAuthenticated { - @GET - @Path("/account") - BybitResult getWalletBalances( - @QueryParam("api_key") String apiKey, - @QueryParam("timestamp") SynchronizedValueFactory timestamp, - @QueryParam("sign") ParamsDigest signature - ) throws IOException, BybitException; - - @GET - @Path("/order") - BybitResult getOrder( - @QueryParam("api_key") String apiKey, - @QueryParam("orderId") String orderId, - @QueryParam("timestamp") SynchronizedValueFactory timestamp, - @QueryParam("sign") ParamsDigest signature - ) throws IOException, BybitException; - - @POST - @Path("/order") - BybitResult placeOrder( - @FormParam("api_key") String apiKey, - @FormParam("symbol") String symbol, - @FormParam("qty") long qty, - @FormParam("side") String side, - @FormParam("type") String type, - @FormParam("timestamp") SynchronizedValueFactory timestamp, - @FormParam("sign") ParamsDigest signature - ) throws IOException, BybitException; - - -} \ No newline at end of file + /** @apiSpec API */ + @GET + @Path("/account/wallet-balance") + BybitResult getWalletBalance( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("accountType") String accountType) + throws IOException, BybitException; + + /** @apiSpec API */ + @GET + @Path("/asset/transfer/query-account-coins-balance") + BybitResult getAllCoinsBalance( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("memberId") String memberId, + @QueryParam("accountType") String accountType, //required + @QueryParam("coin") String coin, + @QueryParam("withBonus") Integer withBonus + ) throws IOException, BybitException; + + @GET + @Path("/asset/transfer/query-account-coin-balance") + BybitResult getSingleCoinBalance( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("memberId") String memberId, + @QueryParam("toMemberId") String toMemberId, + @QueryParam("accountType") String accountType, //required + @QueryParam("toAccountType") String toAccountType, + @QueryParam("coin") String coin, //required + @QueryParam("withBonus") Integer withBonus, + @QueryParam("withTransferSafeAmount") Integer withTransferSafeAmount, + @QueryParam("withLtvTransferSafeAmount") Integer withLtvTransferSafeAmount + ) throws IOException, BybitException; + + /** @apiSpec API */ + @GET + @Path("/account/fee-rate") + BybitResult getFeeRates( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("category") String category, + @QueryParam("symbol") String symbol) + throws IOException, BybitException; + + /** @apiSpec API */ + @GET + @Path("/order/realtime") + BybitResult> getOpenOrders( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("category") String category, + @QueryParam("orderId") String orderId) + throws IOException, BybitException; + + /** @apiSpec API */ + @POST + @Path("/order/create") + BybitResult placeOrder( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @FormParam("category") String category, + @FormParam("symbol") String symbol, + @FormParam("side") String side, + @FormParam("orderType") String orderType, + @FormParam("qty") BigDecimal qty) + throws IOException, BybitException; + + @GET + @Path("/execution/list") + BybitResult getBybitTradeHistory( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("category") String category, + @QueryParam("symbol") String symbol, + @QueryParam("orderId") String orderId, + @QueryParam("orderLinkId") String userReferenceId, + @QueryParam("baseCoin") String baseCoin, // Base coin. Unified account - inverse and Classic account do not support this param + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("execType") String execType, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + @GET + @Path("/asset/transfer/query-inter-transfer-list") + BybitResult getInternalTransferRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("transferId") String transferId, + @QueryParam("coin") String coin, + @QueryParam("status") String status, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + @GET + @Path("/asset/transfer/query-universal-transfer-list") + BybitResult getUniversalTransferRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("transferId") String transferId, + @QueryParam("coin") String coin, + @QueryParam("status") String status, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + @GET + @Path("/account/transaction-log") + BybitResult getTransactionLogRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("accountType") String accountType, + @QueryParam("category") String category, + @QueryParam("currency") String currency, + @QueryParam("baseCoin") String baseCoin, + @QueryParam("type") String type, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + @GET + @Path("/asset/withdraw/query-record") + BybitResult getWithdrawRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("withdrawId") String withdrawID, + @QueryParam("coin") String coin, + @QueryParam("withdrawType") Integer withdrawType, //0(default): on chain. 1: off chain. 2: all + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + @GET + @Path("/asset/deposit/query-record") + BybitResult getOnChainDepositRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("coin") String coin, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; + + /** + * Query the internal transfer records between different account types under the same UID. + */ + @GET + @Path("/asset/deposit/query-internal-record") + BybitResult getInternalDepositRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("coin") String coin, + @QueryParam("cursor") String cursor, + @QueryParam("limit") Integer limit + ) throws IOException, BybitException; + + /** + * Query subaccount's deposit records by main UID's API key. + */ + @GET + @Path("/asset/deposit/query-sub-member-record") + BybitResult getSubAccountDepositRecords( + @HeaderParam(X_BAPI_API_KEY) String apiKey, + @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, + @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, + @QueryParam("subMemberId") String subMemberId, //required + @QueryParam("coin") String coin, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit, + @QueryParam("cursor") String cursor + ) throws IOException, BybitException; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java index 6639acabb13..00eb1cccad4 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java @@ -1,16 +1,13 @@ package org.knowm.xchange.bybit; - import java.io.IOException; -import java.util.List; import org.knowm.xchange.BaseExchange; import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.bybit.dto.marketdata.BybitSymbol; -import org.knowm.xchange.bybit.mappers.MarketDataMapper; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; import org.knowm.xchange.bybit.service.BybitAccountService; 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.dto.meta.ExchangeMetaData; import org.knowm.xchange.exceptions.ExchangeException; public class BybitExchange extends BaseExchange { @@ -19,27 +16,45 @@ public class BybitExchange extends BaseExchange { protected void initServices() { marketDataService = new BybitMarketDataService(this); tradeService = new BybitTradeService(this); - accountService = new BybitAccountService(this); + accountService = + new BybitAccountService( + this, ((BybitExchangeSpecification) getExchangeSpecification()).getAccountType()); } - @Override public ExchangeSpecification getDefaultExchangeSpecification() { - ExchangeSpecification exchangeSpecification = new ExchangeSpecification(this.getClass()); + BybitExchangeSpecification exchangeSpecification = + new BybitExchangeSpecification(this.getClass()); exchangeSpecification.setSslUri("https://api.bybit.com"); exchangeSpecification.setHost("bybit.com"); exchangeSpecification.setPort(80); exchangeSpecification.setExchangeName("Bybit"); exchangeSpecification.setExchangeDescription("BYBIT"); + exchangeSpecification.setAccountType(BybitAccountType.UNIFIED); + exchangeSpecification.setExchangeSpecificParametersItem(USE_SANDBOX, false); return exchangeSpecification; } + @Override + public void applySpecification(ExchangeSpecification exchangeSpecification) { + if(useSandbox(exchangeSpecification)){ + exchangeSpecification.setSslUri("https://api-testnet.bybit.com"); + } + super.applySpecification(exchangeSpecification); + } + @Override public void remoteInit() throws IOException, ExchangeException { - //initialize currency pairs - List symbols = ((BybitMarketDataServiceRaw) marketDataService).getSymbols().getResult(); - symbols.forEach(bybitSymbol -> exchangeMetaData.getInstruments().put( - MarketDataMapper.symbolToCurrencyPair(bybitSymbol), - MarketDataMapper.symbolToCurrencyPairMetaData(bybitSymbol)) - ); + // initialize currency pairs & currencies + exchangeMetaData = new ExchangeMetaData( + marketDataService.getInstruments(), + marketDataService.getCurrencies(), + null, + null, + true); + } + + protected boolean useSandbox(ExchangeSpecification exchangeSpecification){ + return Boolean.TRUE.equals( + exchangeSpecification.getExchangeSpecificParametersItem(USE_SANDBOX)); } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchangeSpecification.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchangeSpecification.java new file mode 100644 index 00000000000..28f942e5014 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchangeSpecification.java @@ -0,0 +1,16 @@ +package org.knowm.xchange.bybit; + +import lombok.Getter; +import lombok.Setter; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; + +public class BybitExchangeSpecification extends ExchangeSpecification { + + @Getter @Setter private BybitAccountType accountType = BybitAccountType.UNIFIED; + + public BybitExchangeSpecification(Class exchangeClass) { + super(exchangeClass); + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategorizedPayload.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategorizedPayload.java new file mode 100644 index 00000000000..34a25a41e87 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategorizedPayload.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.bybit.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; + +@Data +public class BybitCategorizedPayload { + + @JsonProperty("category") + BybitCategory category; + + @JsonProperty("list") + List list; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategory.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategory.java new file mode 100644 index 00000000000..3a4548edb94 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitCategory.java @@ -0,0 +1,16 @@ +package org.knowm.xchange.bybit.dto; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BybitCategory { + SPOT("spot"), + LINEAR("linear"), + INVERSE("inverse"), + OPTION("option"); + + @JsonValue private final String value; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitResult.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitResult.java index 9d9ea219c53..46dd46b37ba 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitResult.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/BybitResult.java @@ -1,39 +1,32 @@ package org.knowm.xchange.bybit.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.Date; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; -import org.knowm.xchange.utils.jackson.UnixTimestampNanoSecondsDeserializer; @Builder @Jacksonized @Value public class BybitResult { - @JsonProperty("ret_code") + @JsonProperty("retCode") int retCode; - @JsonProperty("ret_msg") + @JsonProperty("retMsg") String retMsg; - @JsonProperty("ext_code") - String extCode; - - @JsonProperty("ext_info") - String extInfo; - @JsonProperty("result") V result; - @JsonProperty("time_now") - @JsonDeserialize(using = UnixTimestampNanoSecondsDeserializer.class) - Date timeNow; + @JsonProperty("retExtInfo") + Object retExtInfo; + + @JsonProperty("time") + Date time; public boolean isSuccess() { return retCode == 0; } - } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitAllCoinsBalance.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitAllCoinsBalance.java new file mode 100644 index 00000000000..3d31c0cb5f9 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitAllCoinsBalance.java @@ -0,0 +1,54 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.List; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; + +@Builder +@Jacksonized +@Value +public class BybitAllCoinsBalance { + + @JsonProperty("accountType") + BybitAccountType accountType; + + @JsonProperty("bizType") + Integer bizType; + + @JsonProperty("accountId") + String accountId; + + @JsonProperty("memberId") + String memberId; + + @JsonProperty("balance") + List balance; + + @Builder + @Jacksonized + @Value + public static class BybitCoinBalance { + + @JsonProperty("coin") + String coin; + + @JsonProperty("walletBalance") + BigDecimal walletBalance; + + @JsonProperty("transferBalance") + BigDecimal transferBalance; + + @JsonProperty("bonus") + BigDecimal bonus; + + @JsonProperty("transferSafeAmount") + BigDecimal transferSafeAmount; + + @JsonProperty("ltvTransferSafeAmount") + BigDecimal ltvTransferSafeAmount; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalance.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalance.java deleted file mode 100644 index 495af4e98d2..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalance.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.knowm.xchange.bybit.dto.account; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Builder; -import lombok.Value; -import lombok.extern.jackson.Jacksonized; - -@Builder -@Jacksonized -@Value -public class BybitBalance { - - @JsonProperty("coin") - String coin; - - @JsonProperty("coinId") - String coinId; - - @JsonProperty("coinName") - String coinName; - - @JsonProperty("total") - String total; - - @JsonProperty("free") - String free; - - @JsonProperty("locked") - String locked; - -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitDepositRecordsResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitDepositRecordsResponse.java new file mode 100644 index 00000000000..b04cbac2879 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitDepositRecordsResponse.java @@ -0,0 +1,102 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitDepositRecordsResponse { + + @JsonProperty("rows") + private List rows; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; + + @Getter + @ToString + @Builder + @Jacksonized + public static class BybitDepositRecord { + + @JsonProperty("coin") + private String coin; + + @JsonProperty("chain") + private String chain; + + @JsonProperty("amount") + private BigDecimal amount; + + @JsonProperty("txID") + private String txID; + + @JsonProperty("status") + private BybitDepositStatus status; + + @JsonProperty("toAddress") + private String toAddress; + + @JsonProperty("tag") + private String tag; + + @JsonProperty("depositFee") + private BigDecimal depositFee; + + @JsonProperty("successAt") + private Date successAt; + + @JsonProperty("confirmations") + private Integer confirmations; + + @JsonProperty("txIndex") + private Integer txIndex; + + @JsonProperty("blockHash") + private String blockHash; + + @JsonProperty("batchReleaseLimit") + private BigDecimal batchReleaseLimit; + + @JsonProperty("depositType") + private BybitDepositType depositType; + + @Getter + @AllArgsConstructor + public enum BybitDepositStatus { + + UNKNOWN(0), + TO_BE_CONFIRMED(1), + PROCESSING(2), + SUCCESS(3), + DEPOSIT_FAILED(4), + PENDING_TO_BE_CREDITED_TO_FUNDING_POOL(10011), + CREDITED_TO_FUNDING_POOL_SUCCESSFULLY(10012); + + @JsonValue + private final Integer value; + } + + @Getter + @AllArgsConstructor + public enum BybitDepositType { + + NORMAL_DEPOSIT(0), + DAILY_DEPOSIT_LIMIT_REACHED(10), + ABNORMAL_DEPOSIT(20); + + @JsonValue + private final Integer value; + } + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitFeeRates.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitFeeRates.java new file mode 100644 index 00000000000..6250aad8303 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitFeeRates.java @@ -0,0 +1,32 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.List; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Jacksonized +@Value +public class BybitFeeRates { + + @JsonProperty("list") + List list; + + @Builder + @Jacksonized + @Value + public static class BybitFeeRate { + + @JsonProperty("symbol") + String symbol; + + @JsonProperty("takerFeeRate") + BigDecimal takerFeeRate; + + @JsonProperty("makerFeeRate") + BigDecimal makerFeeRate; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitInternalDepositRecordsResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitInternalDepositRecordsResponse.java new file mode 100644 index 00000000000..291d16b5333 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitInternalDepositRecordsResponse.java @@ -0,0 +1,66 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitInternalDepositRecordsResponse { + + @JsonProperty("rows") + private List rows; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; + @Getter + @ToString + @Builder + @Jacksonized + public static class BybitInternalDepositRecord { + + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private Integer type; + + @JsonProperty("coin") + private String coin; + + @JsonProperty("amount") + private BigDecimal amount; + + @JsonProperty("status") + private BybitInternalDepositStatus status; + + @JsonProperty("address") + private String address; + + @JsonProperty("createdTime") + private Date createdTime; + + @Getter + @AllArgsConstructor + public enum BybitInternalDepositStatus { + + PROCESSING(0), + + SUCCESS(1), + + FAILED(2); + + @JsonValue + private final int value; + } + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransactionLogResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransactionLogResponse.java new file mode 100644 index 00000000000..44633c7a810 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransactionLogResponse.java @@ -0,0 +1,115 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitTransactionLogResponse { + + @JsonProperty("list") + private List list; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; + + @Getter + @ToString + @Builder + @Jacksonized + public static class BybitTransactionLog { + + @JsonProperty("id") + private String id; + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("category") + private String category; + + @JsonProperty("side") + private String side; + + @JsonProperty("transactionTime") + private Date transactionTime; + + @JsonProperty("type") + private BybitTransactionLogType type; + + @JsonProperty("qty") + private BigDecimal qty; + + @JsonProperty("size") + private BigDecimal size; + + @JsonProperty("currency") + private String currency; + + @JsonProperty("tradePrice") + private BigDecimal tradePrice; + + @JsonProperty("funding") + private BigDecimal funding; + + @JsonProperty("fee") + private BigDecimal fee; + + @JsonProperty("cashFlow") + private BigDecimal cashFlow; + + @JsonProperty("change") + private BigDecimal change; + + @JsonProperty("cashBalance") + private BigDecimal cashBalance; + + @JsonProperty("feeRate") + private BigDecimal feeRate; + + @JsonProperty("bonusChange") + private BigDecimal bonusChange; + + @JsonProperty("tradeId") + private String tradeId; + + @JsonProperty("orderId") + private String orderId; + + @JsonProperty("orderLinkId") + private String orderLinkId; //userReference + + public enum BybitTransactionLogType { + TRANSFER_IN, + TRANSFER_OUT, + TRADE, + SETTLEMENT, + DELIVERY, + LIQUIDATION, + BONUS, + FEE_REFUND, + INTEREST, + CURRENCY_BUY, + CURRENCY_SELL, + BORROWED_AMOUNT_INS_LOAN, + PRINCIPLE_REPAYMENT_INS_LOAN, + INTEREST_REPAYMENT_INS_LOAN, + AUTO_SOLD_COLLATERAL_INS_LOAN, + AUTO_BUY_LIABILITY_INS_LOAN, + AUTO_PRINCIPLE_REPAYMENT_INS_LOAN, + AUTO_INTEREST_REPAYMENT_INS_LOAN, + TRANSFER_IN_INS_LOAN, //Transfer In when in the liquidation of OTC loan + TRANSFER_OUT_INS_LOAN, //Transfer Out when in the liquidation of OTC loan + SPOT_REPAYMENT_SELL, //One-click repayment currency sell + SPOT_REPAYMENT_BUY //One-click repayment currency buy + } + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransfersResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransfersResponse.java new file mode 100644 index 00000000000..fb93c5cabac --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitTransfersResponse.java @@ -0,0 +1,64 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitTransfersResponse { + + @JsonProperty("list") + private List internalTransfers; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; + + @Getter + @ToString + @Builder + @Jacksonized + public static class BybitTransfer { + + @JsonProperty("transferId") + private String transferId; + + @JsonProperty("coin") + private String coin; + + @JsonProperty("amount") + private BigDecimal amount; + + @JsonProperty("fromMemberId") + private String fromMember; + + @JsonProperty("toMemberId") + private String toMember; + + @JsonProperty("fromAccountType") + private BybitAccountType fromAccountType; + + @JsonProperty("toAccountType") + private BybitAccountType toAccountType; + + @JsonProperty("timestamp") + private Date timestamp; + + @JsonProperty("status") + private BybitTransferStatus status; + } + + public enum BybitTransferStatus { + SUCCESS, + FAILED, + PENDING + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitWithdrawRecordsResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitWithdrawRecordsResponse.java new file mode 100644 index 00000000000..af04f61f0cf --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitWithdrawRecordsResponse.java @@ -0,0 +1,93 @@ +package org.knowm.xchange.bybit.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitWithdrawRecordsResponse { + + @JsonProperty("rows") + private List rows; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; + + @Getter + @ToString + @Builder + @Jacksonized + public static class BybitWithdrawRecord { + + @JsonProperty("withdrawId") + private String withdrawId; + + @JsonProperty("txID") + private String txID; + + @JsonProperty("withdrawType") + private Integer withdrawType; + + @JsonProperty("coin") + private String coin; + + @JsonProperty("chain") + private String chain; + + @JsonProperty("amount") + private BigDecimal amount; + + @JsonProperty("withdrawFee") + private BigDecimal withdrawFee; + + @JsonProperty("status") + private BybitWithdrawStatus status; + + @JsonProperty("toAddress") + private String toAddress; + + @JsonProperty("tag") + private String tag; + + @JsonProperty("createTime") + private Date createTime; + + @JsonProperty("updateTime") + private Date updateTime; + + @Getter + @AllArgsConstructor + public enum BybitWithdrawStatus { + SECURITY_CHECK("SecurityCheck"), + PENDING("Pending"), + SUCCESS("success"), + CANCEL_BY_USER("CancelByUser"), + REJECT("Reject"), + FAIL("Fail"), + BLOCKCHAIN_CONFIRMED("BlockchainConfirmed"), + UNKNOWN("Unknown"); + + @JsonValue private final String value; + } + + @Getter + @AllArgsConstructor + public enum BybitWithdrawType { + ON_CHAIN(0), + OFF_CHAIN(1), + ALL(2); + + @JsonValue private final Integer value; + } + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountBalance.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountBalance.java new file mode 100644 index 00000000000..3e5a4837eda --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountBalance.java @@ -0,0 +1,49 @@ +package org.knowm.xchange.bybit.dto.account.walletbalance; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Jacksonized +@Value +public class BybitAccountBalance { + + @JsonProperty("accountType") + BybitAccountType accountType; + + @JsonProperty("accountLTV") + String accountLTV; + + @JsonProperty("accountIMRate") + String accountIMRate; + + @JsonProperty("accountMMRate") + String accountMMRate; + + @JsonProperty("totalEquity") + String totalEquity; + + @JsonProperty("totalWalletBalance") + String totalWalletBalance; + + @JsonProperty("totalMarginBalance") + String totalMarginBalance; + + @JsonProperty("totalAvailableBalance") + String totalAvailableBalance; + + @JsonProperty("totalPerpUPL") + String totalPerpUPL; + + @JsonProperty("totalInitialMargin") + String totalInitialMargin; + + @JsonProperty("totalMaintenanceMargin") + String totalMaintenanceMargin; + + @JsonProperty("coin") + List coin; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountType.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountType.java new file mode 100644 index 00000000000..038c12bbb3f --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitAccountType.java @@ -0,0 +1,10 @@ +package org.knowm.xchange.bybit.dto.account.walletbalance; + +public enum BybitAccountType { + CONTRACT, + SPOT, + OPTION, + UNIFIED, + CLASSIC, + FUND +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitCoinWalletBalance.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitCoinWalletBalance.java new file mode 100644 index 00000000000..5ab2193c849 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitCoinWalletBalance.java @@ -0,0 +1,66 @@ +package org.knowm.xchange.bybit.dto.account.walletbalance; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Jacksonized +@Value +public class BybitCoinWalletBalance { + + @JsonProperty("coin") + String coin; + + @JsonProperty("equity") + String equity; + + @JsonProperty("usdValue") + String usdValue; + + @JsonProperty("walletBalance") + String walletBalance; + + @JsonProperty("free") + String free; + + @JsonProperty("locked") + String locked; + + @JsonProperty("borrowAmount") + String borrowAmount; + + @JsonProperty("availableToBorrow") + String availableToBorrow; + + @JsonProperty("availableToWithdraw") + String availableToWithdraw; + + @JsonProperty("accruedInterest") + String accruedInterest; + + @JsonProperty("totalOrderIM") + String totalOrderIM; + + @JsonProperty("totalPositionIM") + String totalPositionIM; + + @JsonProperty("totalPositionMM") + String totalPositionMM; + + @JsonProperty("unrealisedPnl") + String unrealisedPnl; + + @JsonProperty("cumRealisedPnl") + String cumRealisedPnl; + + @JsonProperty("bonus") + String bonus; + + @JsonProperty("collateralSwitch") + boolean collateralSwitch; + + @JsonProperty("marginCollateral") + boolean marginCollateral; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalances.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitWalletBalance.java similarity index 56% rename from xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalances.java rename to xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitWalletBalance.java index efb92f61443..f46befe7f8d 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/BybitBalances.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/account/walletbalance/BybitWalletBalance.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.bybit.dto.account; +package org.knowm.xchange.bybit.dto.account.walletbalance; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; @@ -9,9 +9,8 @@ @Builder @Jacksonized @Value -public class BybitBalances { - - @JsonProperty("balances") - List balances; +public class BybitWalletBalance { + @JsonProperty("list") + List list; } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitAssetsInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitAssetsInfo.java new file mode 100644 index 00000000000..f8d5437ecc7 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitAssetsInfo.java @@ -0,0 +1,24 @@ +package org.knowm.xchange.bybit.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Data; +import lombok.Getter; +import lombok.ToString; + +public class BybitAssetsInfo { + @ToString + @Getter + @Data + public static class BybitSpotAsset { + + @JsonProperty("coin") + private final String coin; + @JsonProperty("frozen") + private final BigDecimal frozen; + @JsonProperty("free") + private final BigDecimal free; + @JsonProperty("withdraw") + private final BigDecimal withdraw; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitSymbol.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitSymbol.java deleted file mode 100644 index 280cc753600..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitSymbol.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.knowm.xchange.bybit.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import lombok.Builder; -import lombok.Value; -import lombok.extern.jackson.Jacksonized; - -@Builder -@Jacksonized -@Value -public class BybitSymbol { - - @JsonProperty("name") - String name; - - @JsonProperty("alias") - String alias; - - @JsonProperty("status") - String status; - - @JsonProperty("base_currency") - String baseCurrency; - - @JsonProperty("quote_currency") - String quoteCurrency; - - @JsonProperty("price_scale") - Integer priceScale; - - @JsonProperty("taker_fee") - BigDecimal takerFee; - - @JsonProperty("maker_fee") - BigDecimal makerFee; - - @JsonProperty("funding_interval") - Integer fundingInterval; - - @JsonProperty("leverage_filter") - LeverageFilter leverageFilter; - - @JsonProperty("price_filter") - PriceFilter priceFilter; - - @JsonProperty("lot_size_filter") - LotSizeFilter lotSizeFilter; - - - @Builder - @Jacksonized - @Value - public static class LeverageFilter { - - @JsonProperty("min_leverage") - Integer minLeverage; - - @JsonProperty("max_leverage") - Integer maxLeverage; - - @JsonProperty("leverage_step") - BigDecimal leverageStep; - - } - - - @Builder - @Jacksonized - @Value - public static class PriceFilter { - @JsonProperty("min_price") - BigDecimal minPrice; - - @JsonProperty("max_price") - BigDecimal maxPrice; - - @JsonProperty("tick_size") - BigDecimal tickSize; - } - - - @Builder - @Jacksonized - @Value - public static class LotSizeFilter { - - @JsonProperty("max_trading_qty") - BigDecimal maxTradingQty; - - @JsonProperty("min_trading_qty") - BigDecimal minTradingQty; - - @JsonProperty("qty_step") - BigDecimal qtyStep; - - @JsonProperty("post_only_max_trading_qty") - BigDecimal postOnlyMaxTradingQty; - - } - - -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitTicker.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitTicker.java deleted file mode 100644 index 6a0fd544725..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitTicker.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.knowm.xchange.bybit.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.Date; -import lombok.Builder; -import lombok.Value; -import lombok.extern.jackson.Jacksonized; - -@Builder -@Jacksonized -@Value -public class BybitTicker { - - @JsonProperty("symbol") - String symbol; - - @JsonProperty("bid_price") - BigDecimal bestBidPrice; - - @JsonProperty("ask_price") - BigDecimal bestAskPrice; - - @JsonProperty("last_price") - BigDecimal lastPrice; - - @JsonProperty("last_tick_direction") - String lastTickDirection; - - @JsonProperty("prev_price_24h") - BigDecimal prevPrice24h; - - @JsonProperty("price_24h_pcnt") - BigDecimal price24hPercentageChange; - - @JsonProperty("high_price_24h") - BigDecimal highPrice; - - @JsonProperty("low_price_24h") - BigDecimal lowPrice; - - @JsonProperty("prev_price_1h") - BigDecimal prevPrice1h; - - @JsonProperty("price_1h_pcnt") - BigDecimal price1hPercentageChange; - - @JsonProperty("mark_price") - BigDecimal markPrice; - - @JsonProperty("index_price") - BigDecimal indexPrice; - - @JsonProperty("open_interest") - BigDecimal openInterest; - - @JsonProperty("open_value") - BigDecimal openValue; - - @JsonProperty("total_turnover") - BigDecimal totalTurnover; - - @JsonProperty("turnover_24h") - BigDecimal turnover24h; - - @JsonProperty("total_volume") - BigDecimal totalVolume; - - @JsonProperty("volume_24h") - BigDecimal volume24h; - - @JsonProperty("funding_rate") - BigDecimal fundingRate; - - @JsonProperty("predicted_funding_rate") - BigDecimal predictedFundingRate; - - @JsonProperty("next_funding_time") - Date nextFundingTime; - - @JsonProperty("countdown_hour") - Integer countdownHour; - - @JsonProperty("delivery_fee_rate") - BigDecimal deliveryFeeRate; - - @JsonProperty("predicted_delivery_price") - BigDecimal predictedDeliveryPrice; - - @JsonProperty("delivery_time") - Date deliveryTime; - -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentInfo.java new file mode 100644 index 00000000000..e12e65551c0 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentInfo.java @@ -0,0 +1,37 @@ +package org.knowm.xchange.bybit.dto.marketdata.instruments; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@ToString +@Data +public abstract class BybitInstrumentInfo { + + @JsonProperty("symbol") + String symbol; + + @JsonProperty("baseCoin") + String baseCoin; + + @JsonProperty("quoteCoin") + String quoteCoin; + + @JsonProperty("status") + InstrumentStatus status; + + public enum InstrumentStatus { + @JsonProperty("PreLaunch") + PRE_LAUNCH, + @JsonProperty("Trading") + TRADING, + @JsonProperty("Settling") + SETTLING, + @JsonProperty("Delivering") + DELIVERING, + @JsonProperty("Closed") + CLOSED + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentsInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentsInfo.java new file mode 100644 index 00000000000..8f8e0beb955 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/BybitInstrumentsInfo.java @@ -0,0 +1,40 @@ +package org.knowm.xchange.bybit.dto.marketdata.instruments; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.BybitCategorizedPayload; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo.BybitLinearInverseInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo.BybitOptionInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo.BybitSpotInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.linear.BybitLinearInverseInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.spot.BybitSpotInstrumentInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "category", visible = true) +@JsonSubTypes({ + @Type(value = BybitLinearInverseInstrumentsInfo.class, name = "linear"), + @Type(value = BybitLinearInverseInstrumentsInfo.class, name = "inverse"), + @Type(value = BybitOptionInstrumentsInfo.class, name = "option"), + @Type(value = BybitSpotInstrumentsInfo.class, name = "spot"), +}) +public abstract class BybitInstrumentsInfo + extends BybitCategorizedPayload { + + @Jacksonized + @Value + public static class BybitLinearInverseInstrumentsInfo + extends BybitInstrumentsInfo {} + + @Jacksonized + @Value + public static class BybitOptionInstrumentsInfo + extends BybitInstrumentsInfo {} + + @Jacksonized + @Value + public static class BybitSpotInstrumentsInfo + extends BybitInstrumentsInfo {} +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/linear/BybitLinearInverseInstrumentInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/linear/BybitLinearInverseInstrumentInfo.java new file mode 100644 index 00000000000..567b1667f06 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/linear/BybitLinearInverseInstrumentInfo.java @@ -0,0 +1,122 @@ +package org.knowm.xchange.bybit.dto.marketdata.instruments.linear; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Builder; +import lombok.ToString; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; + +@SuperBuilder +@Jacksonized +@ToString(callSuper = true) +@Value +public class BybitLinearInverseInstrumentInfo extends BybitInstrumentInfo { + + @JsonProperty("contractType") + ContractType contractType; + + @JsonProperty("launchTime") + Date launchTime; + + @JsonProperty("deliveryTime") + Date deliveryTime; + + @JsonProperty("deliveryFeeRate") + BigDecimal deliveryFeeRate; + + @JsonProperty("priceScale") + Integer priceScale; + + @JsonProperty("leverageFilter") + LeverageFilter leverageFilter; + + @JsonProperty("priceFilter") + PriceFilter priceFilter; + + @JsonProperty("lotSizeFilter") + LotSizeFilter lotSizeFilter; + + @JsonProperty("unifiedMarginTrade") + boolean unifiedMarginTrade; + + @JsonProperty("fundingInterval") + Integer fundingInterval; + + @JsonProperty("settleCoin") + String settleCoin; + + @JsonProperty("copyTrading") + CopyTrading copyTrading; + + public enum CopyTrading { + @JsonProperty("none") + NONE, + @JsonProperty("both") + BOTH, + @JsonProperty("utaOnly") + UTA_ONLY, + @JsonProperty("normalOnly") + NORMAL_ONLY + } + + public enum ContractType { + @JsonProperty("InversePerpetual") + INVERSE_PERPETUAL, + @JsonProperty("LinearPerpetual") + LINEAR_PERPETUAL, + @JsonProperty("LinearFutures") + LINEAR_FUTURES, + @JsonProperty("InverseFutures") + INVERSE_FUTURES + } + + @Builder + @Jacksonized + @Value + public static class LeverageFilter { + + @JsonProperty("minLeverage") + Integer minLeverage; + + @JsonProperty("maxLeverage") + BigDecimal maxLeverage; + + @JsonProperty("leverageStep") + BigDecimal leverageStep; + } + + @Builder + @Jacksonized + @Value + public static class PriceFilter { + @JsonProperty("tickSize") + BigDecimal tickSize; + + @JsonProperty("minPrice") + BigDecimal minPrice; + + @JsonProperty("maxPrice") + BigDecimal maxPrice; + } + + @Builder + @Jacksonized + @Value + public static class LotSizeFilter { + @JsonProperty("maxOrderQty") + BigDecimal maxOrderQty; + + @JsonProperty("minOrderQty") + BigDecimal minOrderQty; + + @JsonProperty("qtyStep") + BigDecimal qtyStep; + + @JsonProperty("postOnlyMaxOrderQty") + BigDecimal postOnlyMaxOrderQty; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/option/BybitOptionInstrumentInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/option/BybitOptionInstrumentInfo.java new file mode 100644 index 00000000000..e4f889608c1 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/option/BybitOptionInstrumentInfo.java @@ -0,0 +1,81 @@ +package org.knowm.xchange.bybit.dto.marketdata.instruments.option; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Builder; +import lombok.ToString; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; + +@SuperBuilder +@Jacksonized +@ToString(callSuper = true) +@Value +public class BybitOptionInstrumentInfo extends BybitInstrumentInfo { + + @JsonProperty("nextPageCursor") + String nextPageCursor; + + @JsonProperty("list") + Object list; + + @JsonProperty("optionsType") + BybitOptionType optionsType; + + @JsonProperty("settleCoin") + String settleCoin; + + @JsonProperty("launchTime") + Date launchTime; + + @JsonProperty("deliveryTime") + Date deliveryTime; + + @JsonProperty("deliveryFeeRate") + BigDecimal deliveryFeeRate; + + @JsonProperty("priceFilter") + PriceFilter priceFilter; + + @JsonProperty("lotSizeFilter") + LotSizeFilter lotSizeFilter; + + public enum BybitOptionType { + @JsonProperty("Call") + CALL, + + @JsonProperty("Put") + PUT + } + + @Builder + @Jacksonized + @Value + public static class PriceFilter { + @JsonProperty("tickSize") + BigDecimal tickSize; + + @JsonProperty("minPrice") + BigDecimal minPrice; + + @JsonProperty("maxPrice") + BigDecimal maxPrice; + } + + @Builder + @Jacksonized + @Value + public static class LotSizeFilter { + @JsonProperty("maxOrderQty") + BigDecimal maxOrderQty; + + @JsonProperty("minOrderQty") + BigDecimal minOrderQty; + + @JsonProperty("qtyStep") + BigDecimal qtyStep; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/spot/BybitSpotInstrumentInfo.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/spot/BybitSpotInstrumentInfo.java new file mode 100644 index 00000000000..4e626574234 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/instruments/spot/BybitSpotInstrumentInfo.java @@ -0,0 +1,72 @@ +package org.knowm.xchange.bybit.dto.marketdata.instruments.spot; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Builder; +import lombok.ToString; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; + +@SuperBuilder +@Jacksonized +@ToString(callSuper = true) +@Value +public class BybitSpotInstrumentInfo extends BybitInstrumentInfo { + + @JsonProperty("innovation") + int innovation; + + @JsonProperty("marginTrading") + MarginTrading marginTrading; + + @JsonProperty("lotSizeFilter") + LotSizeFilter lotSizeFilter; + + @JsonProperty("priceFilter") + PriceFilter priceFilter; + + public enum MarginTrading { + @JsonProperty("none") + NONE, + @JsonProperty("both") + BOTH, + @JsonProperty("utaOnly") + UTA_ONLY, + @JsonProperty("normalSpotOnly") + NORMAL_SPOT_ONLY + } + + @Builder + @Jacksonized + @Value + public static class PriceFilter { + @JsonProperty("tickSize") + BigDecimal tickSize; + } + + @Builder + @Jacksonized + @Value + public static class LotSizeFilter { + + @JsonProperty("basePrecision") + BigDecimal basePrecision; + + @JsonProperty("quotePrecision") + BigDecimal quotePrecision; + + @JsonProperty("minOrderQty") + BigDecimal minOrderQty; + + @JsonProperty("maxOrderQty") + BigDecimal maxOrderQty; + + @JsonProperty("minOrderAmt") + BigDecimal minOrderAmt; + + @JsonProperty("maxOrderAmt") + BigDecimal maxOrderAmt; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTicker.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTicker.java new file mode 100644 index 00000000000..3293289ba47 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTicker.java @@ -0,0 +1,41 @@ +package org.knowm.xchange.bybit.dto.marketdata.tickers; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Data +public abstract class BybitTicker { + + @JsonProperty("symbol") + String symbol; + + @JsonProperty("lastPrice") + BigDecimal lastPrice; + + @JsonProperty("bid1Price") + BigDecimal bid1Price; + + @JsonProperty("bid1Size") + BigDecimal bid1Size; + + @JsonProperty("ask1Price") + BigDecimal ask1Price; + + @JsonProperty("ask1Size") + BigDecimal ask1Size; + + @JsonProperty("highPrice24h") + BigDecimal highPrice24h; + + @JsonProperty("lowPrice24h") + BigDecimal lowPrice24h; + + @JsonProperty("turnover24h") + BigDecimal turnover24h; + + @JsonProperty("volume24h") + BigDecimal volume24h; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTickers.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTickers.java new file mode 100644 index 00000000000..1955314dd36 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/BybitTickers.java @@ -0,0 +1,36 @@ +package org.knowm.xchange.bybit.dto.marketdata.tickers; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.BybitCategorizedPayload; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers.BybitLinearInverseTickers; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers.BybitOptionTickers; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers.BybitSpotTickers; +import org.knowm.xchange.bybit.dto.marketdata.tickers.linear.BybitLinearInverseTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.option.BybitOptionTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.spot.BybitSpotTicker; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "category", visible = true) +@JsonSubTypes({ + @Type(value = BybitLinearInverseTickers.class, name = "linear"), + @Type(value = BybitLinearInverseTickers.class, name = "inverse"), + @Type(value = BybitOptionTickers.class, name = "option"), + @Type(value = BybitSpotTickers.class, name = "spot") +}) +public abstract class BybitTickers extends BybitCategorizedPayload { + + @Jacksonized + @Value + public static class BybitLinearInverseTickers extends BybitTickers {} + + @Jacksonized + @Value + public static class BybitOptionTickers extends BybitTickers {} + + @Jacksonized + @Value + public static class BybitSpotTickers extends BybitTickers {} +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/linear/BybitLinearInverseTicker.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/linear/BybitLinearInverseTicker.java new file mode 100644 index 00000000000..0a665519bd2 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/linear/BybitLinearInverseTicker.java @@ -0,0 +1,57 @@ +package org.knowm.xchange.bybit.dto.marketdata.tickers.linear; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; + +@SuperBuilder +@Jacksonized +@Value +public class BybitLinearInverseTicker extends BybitTicker { + + @JsonProperty("indexPrice") + BigDecimal indexPrice; + + @JsonProperty("markPrice") + BigDecimal markPrice; + + @JsonProperty("prevPrice1h") + BigDecimal prevPrice1h; + + @JsonProperty("prevPrice24h") + BigDecimal prevPrice24h; + + @JsonProperty("price24hPcnt") + BigDecimal price24hPcnt; + + @JsonProperty("openInterest") + BigDecimal openInterest; + + @JsonProperty("openInterestValue") + BigDecimal openInterestValue; + + @JsonProperty("fundingRate") + BigDecimal fundingRate; + + @JsonProperty("nextFundingTime") + Date nextFundingTime; + + @JsonProperty("predictedDeliveryPrice") + BigDecimal predictedDeliveryPrice; + + @JsonProperty("basisRate") + BigDecimal basisRate; + + @JsonProperty("basis") + BigDecimal basis; + + @JsonProperty("deliveryFeeRate") + BigDecimal deliveryFeeRate; + + @JsonProperty("deliveryTime") + Date deliveryTime; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/option/BybitOptionTicker.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/option/BybitOptionTicker.java new file mode 100644 index 00000000000..0606174b966 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/option/BybitOptionTicker.java @@ -0,0 +1,59 @@ +package org.knowm.xchange.bybit.dto.marketdata.tickers.option; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; + +@SuperBuilder +@Jacksonized +@Value +public class BybitOptionTicker extends BybitTicker { + + @JsonProperty("bid1Iv") + BigDecimal bid1Iv; + + @JsonProperty("ask1Iv") + BigDecimal ask1Iv; + + @JsonProperty("markPrice") + BigDecimal markPrice; + + @JsonProperty("indexPrice") + BigDecimal indexPrice; + + @JsonProperty("markIv") + BigDecimal markIv; + + @JsonProperty("underlyingPrice") + BigDecimal underlyingPrice; + + @JsonProperty("openInterest") + BigDecimal openInterest; + + @JsonProperty("totalVolume") + BigDecimal totalVolume; + + @JsonProperty("totalTurnover") + BigDecimal totalTurnover; + + @JsonProperty("delta") + BigDecimal delta; + + @JsonProperty("gamma") + BigDecimal gamma; + + @JsonProperty("vega") + BigDecimal vega; + + @JsonProperty("theta") + BigDecimal theta; + + @JsonProperty("predictedDeliveryPrice") + BigDecimal predictedDeliveryPrice; + + @JsonProperty("change24h") + BigDecimal change24h; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/spot/BybitSpotTicker.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/spot/BybitSpotTicker.java new file mode 100644 index 00000000000..1ea18bea753 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/tickers/spot/BybitSpotTicker.java @@ -0,0 +1,23 @@ +package org.knowm.xchange.bybit.dto.marketdata.tickers.spot; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; + +@SuperBuilder +@Jacksonized +@Value +public class BybitSpotTicker extends BybitTicker { + + @JsonProperty("prevPrice24h") + BigDecimal prevPrice24h; + + @JsonProperty("price24hPcnt") + BigDecimal price24hPcnt; + + @JsonProperty("usdIndexPrice") + BigDecimal usdIndexPrice; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitExecType.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitExecType.java new file mode 100644 index 00000000000..0eaa40453fc --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitExecType.java @@ -0,0 +1,23 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BybitExecType { + TRADE("Trade"), + + ADL_TRADE("AdlTrade"), + + FUNDING("Funding"), + + BUST_TRADE("BustTrade"), + + DELIVERY("Delivery"), + + BLOCK_TRADE("BlockTrade"); + @JsonValue + private final String value; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderDetails.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderDetails.java deleted file mode 100644 index 026119a4efd..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderDetails.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.knowm.xchange.bybit.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Builder; -import lombok.Value; -import lombok.extern.jackson.Jacksonized; - -@Builder -@Jacksonized -@Value -public class BybitOrderDetails { - - @JsonProperty("accountId") - String accountId; - - @JsonProperty("exchangeId") - String exchangeId; - - @JsonProperty("symbol") - String symbol; - - @JsonProperty("symbolName") - String symbolName; - - @JsonProperty("orderLinkId") - String orderLinkId; - - @JsonProperty("orderId") - String orderId; - - @JsonProperty("price") - String price; - - @JsonProperty("origQty") - String origQty; - - @JsonProperty("executedQty") - String executedQty; - - @JsonProperty("cummulativeQuoteQty") - String cummulativeQuoteQty; - - @JsonProperty("avgPrice") - String avgPrice; - - @JsonProperty("status") - String status; - - @JsonProperty("timeInForce") - String timeInForce; - - @JsonProperty("type") - String type; - - @JsonProperty("side") - String side; - - @JsonProperty("stopPrice") - String stopPrice; - - @JsonProperty("icebergQty") - String icebergQty; - - @JsonProperty("time") - String time; - - @JsonProperty("updateTime") - String updateTime; - - @JsonProperty("isWorking") - boolean isWorking; - - @JsonProperty("locked") - String locked; - -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderRequest.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderRequest.java deleted file mode 100644 index 7bad682ae24..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.knowm.xchange.bybit.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Builder; -import lombok.Value; -import lombok.extern.jackson.Jacksonized; - -@Builder -@Jacksonized -@Value -public class BybitOrderRequest { - - @JsonProperty("accountId") - String accountId; - - @JsonProperty("symbol") - String symbol; - - @JsonProperty("symbolName") - String symbolName; - - @JsonProperty("orderLinkId") - String orderLinkId; - - @JsonProperty("orderId") - String orderId; - - @JsonProperty("transactTime") - String transactTime; - - @JsonProperty("price") - String price; - - @JsonProperty("origQty") - String origQty; - - @JsonProperty("executedQty") - String executedQty; - - @JsonProperty("status") - String status; - - @JsonProperty("timeInForce") - String timeInForce; - - @JsonProperty("type") - String type; - - @JsonProperty("side") - String side; -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderResponse.java new file mode 100644 index 00000000000..b327b87d5b5 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderResponse.java @@ -0,0 +1,18 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Builder +@Jacksonized +@Value +public class BybitOrderResponse { + + @JsonProperty("orderId") + String orderId; + + @JsonProperty("orderLinkId") + String orderLinkId; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderStatus.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderStatus.java new file mode 100644 index 00000000000..5e9d8258a92 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderStatus.java @@ -0,0 +1,38 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum BybitOrderStatus { + @JsonProperty("Created") + CREATED, + + @JsonProperty("New") + NEW, + + @JsonProperty("Rejected") + REJECTED, + + @JsonProperty("PartiallyFilled") + PARTIALLY_FILLED, + + @JsonProperty("PartiallyFilledCanceled") + PARTIALLY_FILLED_CANCELED, + + @JsonProperty("Filled") + FILLED, + + @JsonProperty("Cancelled") + CANCELLED, + + @JsonProperty("Untriggered") + UNTRIGGERED, + + @JsonProperty("Triggered") + TRIGGERED, + + @JsonProperty("Deactivated") + DEACTIVATED, + + @JsonProperty("Active") + ACTIVE +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderType.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderType.java new file mode 100644 index 00000000000..82b7e262317 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitOrderType.java @@ -0,0 +1,31 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BybitOrderType { + MARKET("Market"), + + LIMIT("Limit"), + + UNKNOWN("UNKNOWN"), + + STOP_LOSS("StopLoss"), + + PARTIAL_TAKE_PROFIT("PartialTakeProfit"), + + PARTIAL_STOP_LOSS("PartialStopLoss"), + + TPSL_ORDER("tpslOrder"), + + MM_RATE_CLOSE("MmRateClose"), + + STOP("Stop"), + + TAKE_PROFIT("TakeProfit"); + + @JsonValue private final String value; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitSide.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitSide.java new file mode 100644 index 00000000000..758fbbdf355 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitSide.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BybitSide { + BUY("Buy"), + + SELL("Sell"), + + NONE("None"); + + @JsonValue private final String value; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitTradeHistoryResponse.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitTradeHistoryResponse.java new file mode 100644 index 00000000000..e6740ece07f --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitTradeHistoryResponse.java @@ -0,0 +1,25 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.BybitCategory; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitTradeHistoryResponse { + + @JsonProperty("category") + private BybitCategory category; + + @JsonProperty("list") + private List tradeHistoryList; + + @JsonProperty("nextPageCursor") + private String nextPageCursor; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitUserTradeDto.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitUserTradeDto.java new file mode 100644 index 00000000000..67dd6b0ab13 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitUserTradeDto.java @@ -0,0 +1,101 @@ +package org.knowm.xchange.bybit.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.BybitCategory; + +@Getter +@ToString +@Builder +@Jacksonized +public class BybitUserTradeDto { + + @JsonProperty("category") + private BybitCategory category; + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("isLeverage") + private String isLeverage; + + @JsonProperty("orderId") + private String orderId; + + @JsonProperty("orderLinkId") + private String orderLinkId; + + @JsonProperty("side") + private BybitSide side; + + @JsonProperty("orderPrice") + private BigDecimal orderPrice; + + @JsonProperty("orderQty") + private BigDecimal orderQty; + + @JsonProperty("leavesQty") + private BigDecimal leavesQty; + + @JsonProperty("orderType") + private BybitOrderType orderType; + + @JsonProperty("stopOrderType") + private BybitOrderType stopOrderType; + + @JsonProperty("execFee") + private BigDecimal execFee; + + @JsonProperty("execId") + private String execId; + + @JsonProperty("execPrice") + private BigDecimal execPrice; + + @JsonProperty("execQty") + private BigDecimal execQty; + + @JsonProperty("execType") + private BybitExecType execType; + + @JsonProperty("execValue") + private BigDecimal execValue; + + @JsonProperty("execTime") + private Date execTime; + + @JsonProperty("isMaker") + private Boolean isMaker; + + @JsonProperty("feeRate") + private BigDecimal feeRate; + + @JsonProperty("tradeIv") + private BigDecimal tradeIv; + + @JsonProperty("markIv") + private BigDecimal markIv; + + @JsonProperty("markPrice") + private BigDecimal markPrice; + + @JsonProperty("indexPrice") + private BigDecimal indexPrice; + + @JsonProperty("underlyingPrice") + private BigDecimal underlyingPrice; + + @JsonProperty("blockTradeId") + private String blockTradeId; + + @JsonProperty("closedSize") + private BigDecimal closedSize; + + @JsonProperty("seq") + private Long seq; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetail.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetail.java new file mode 100644 index 00000000000..016264cfe67 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetail.java @@ -0,0 +1,41 @@ +package org.knowm.xchange.bybit.dto.trade.details; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import org.knowm.xchange.bybit.dto.trade.BybitOrderStatus; +import org.knowm.xchange.bybit.dto.trade.BybitSide; + +@SuperBuilder +@Data +public abstract class BybitOrderDetail { + + @JsonProperty("symbol") + String symbol; + + @JsonProperty("side") + BybitSide side; + + @JsonProperty("qty") + BigDecimal qty; + + @JsonProperty("cumExecQty") + BigDecimal cumExecQty; + + @JsonProperty("orderId") + String orderId; + + @JsonProperty("createdTime") + Date createdTime; + + @JsonProperty("price") + BigDecimal price; + + @JsonProperty("avgPrice") + BigDecimal avgPrice; + + @JsonProperty("orderStatus") + BybitOrderStatus orderStatus; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetails.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetails.java new file mode 100644 index 00000000000..6e91fa6c733 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/BybitOrderDetails.java @@ -0,0 +1,34 @@ +package org.knowm.xchange.bybit.dto.trade.details; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.BybitCategorizedPayload; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails.BybitLinearOrderDetails; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails.BybitSpotOrderDetails; +import org.knowm.xchange.bybit.dto.trade.details.linear.BybitLinearOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.spot.BybitSpotOrderDetail; + +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "category", visible = true) +@JsonSubTypes({ + @Type(value = BybitLinearOrderDetails.class, name = "linear"), + @Type(value = BybitSpotOrderDetails.class, name = "spot"), +}) +public class BybitOrderDetails extends BybitCategorizedPayload { + + @JsonProperty("nextPageCursor") + String nextPageCursor; + + @Jacksonized + @Value + public static class BybitLinearOrderDetails extends BybitOrderDetails {} + + @Jacksonized + @Value + public static class BybitSpotOrderDetails extends BybitOrderDetails {} +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/linear/BybitLinearOrderDetail.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/linear/BybitLinearOrderDetail.java new file mode 100644 index 00000000000..6e83c6818e9 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/linear/BybitLinearOrderDetail.java @@ -0,0 +1,112 @@ +package org.knowm.xchange.bybit.dto.trade.details.linear; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.trade.BybitOrderType; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; + +@SuperBuilder +@Jacksonized +@Value +public class BybitLinearOrderDetail extends BybitOrderDetail { + + @JsonProperty("orderLinkId") + String orderLinkId; + + @JsonProperty("blockTradeId") + String blockTradeId; + + @JsonProperty("isLeverage") + String isLeverage; + + @JsonProperty("positionIdx") + int positionIdx; + + @JsonProperty("cancelType") + String cancelType; + + @JsonProperty("rejectReason") + String rejectReason; + + @JsonProperty("leavesQty") + BigDecimal leavesQty; + + @JsonProperty("leavesValue") + BigDecimal leavesValue; + + @JsonProperty("cumExecValue") + BigDecimal cumExecValue; + + @JsonProperty("cumExecFee") + BigDecimal cumExecFee; + + @JsonProperty("timeInForce") + String timeInForce; + + @JsonProperty("orderType") + BybitOrderType orderType; + + @JsonProperty("stopOrderType") + String stopOrderType; + + @JsonProperty("orderIv") + String orderIv; + + @JsonProperty("triggerPrice") + BigDecimal triggerPrice; + + @JsonProperty("takeProfit") + BigDecimal takeProfit; + + @JsonProperty("stopLoss") + BigDecimal stopLoss; + + @JsonProperty("tpTriggerBy") + String tpTriggerBy; + + @JsonProperty("slTriggerBy") + String slTriggerBy; + + @JsonProperty("triggerDirection") + int triggerDirection; + + @JsonProperty("triggerBy") + String triggerBy; + + @JsonProperty("lastPriceOnCreated") + String lastPriceOnCreated; + + @JsonProperty("reduceOnly") + boolean reduceOnly; + + @JsonProperty("closeOnTrigger") + boolean closeOnTrigger; + + @JsonProperty("smpType") + String smpType; + + @JsonProperty("smpGroup") + int smpGroup; + + @JsonProperty("smpOrderId") + String smpOrderId; + + @JsonProperty("tpslMode") + String tpslMode; + + @JsonProperty("tpLimitPrice") + String tpLimitPrice; + + @JsonProperty("slLimitPrice") + String slLimitPrice; + + @JsonProperty("placeType") + String placeType; + + @JsonProperty("updatedTime") + Date updatedTime; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/spot/BybitSpotOrderDetail.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/spot/BybitSpotOrderDetail.java new file mode 100644 index 00000000000..ddeb89e59c9 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/details/spot/BybitSpotOrderDetail.java @@ -0,0 +1,103 @@ +package org.knowm.xchange.bybit.dto.trade.details.spot; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bybit.dto.trade.BybitOrderType; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; + +@SuperBuilder +@Jacksonized +@Value +public class BybitSpotOrderDetail extends BybitOrderDetail { + + @JsonProperty("orderType") + BybitOrderType orderType; + + @JsonProperty("orderLinkId") + String orderLinkId; + + @JsonProperty("cancelType") + String cancelType; + + @JsonProperty("stopOrderType") + String stopOrderType; + + @JsonProperty("lastPriceOnCreated") + BigDecimal lastPriceOnCreated; + + @JsonProperty("takeProfit") + String takeProfit; + + @JsonProperty("cumExecValue") + BigDecimal cumExecValue; + + @JsonProperty("smpType") + String smpType; + + @JsonProperty("triggerDirection") + int triggerDirection; + + @JsonProperty("blockTradeId") + String blockTradeId; + + @JsonProperty("isLeverage") + String isLeverage; + + @JsonProperty("rejectReason") + String rejectReason; + + @JsonProperty("orderIv") + String orderIv; + + @JsonProperty("tpTriggerBy") + String tpTriggerBy; + + @JsonProperty("positionIdx") + int positionIdx; + + @JsonProperty("timeInForce") + String timeInForce; + + @JsonProperty("leavesValue") + BigDecimal leavesValue; + + @JsonProperty("updatedTime") + Date updatedTime; + + @JsonProperty("smpGroup") + int smpGroup; + + @JsonProperty("triggerPrice") + BigDecimal triggerPrice; + + @JsonProperty("cumExecFee") + BigDecimal cumExecFee; + + @JsonProperty("leavesQty") + BigDecimal leavesQty; + + @JsonProperty("slTriggerBy") + String slTriggerBy; + + @JsonProperty("closeOnTrigger") + boolean closeOnTrigger; + + @JsonProperty("placeType") + String placeType; + + @JsonProperty("reduceOnly") + boolean reduceOnly; + + @JsonProperty("stopLoss") + String stopLoss; + + @JsonProperty("smpOrderId") + String smpOrderId; + + @JsonProperty("triggerBy") + String triggerBy; +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/mappers/MarketDataMapper.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/mappers/MarketDataMapper.java deleted file mode 100644 index 313d69d8b76..00000000000 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/mappers/MarketDataMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.knowm.xchange.bybit.mappers; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.knowm.xchange.bybit.dto.marketdata.BybitSymbol; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.meta.InstrumentMetaData; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class MarketDataMapper { - - public static CurrencyPair symbolToCurrencyPair(BybitSymbol symbol) { - return new CurrencyPair(symbol.getBaseCurrency(), symbol.getQuoteCurrency()); - } - - - public static InstrumentMetaData symbolToCurrencyPairMetaData(BybitSymbol bybitSymbol) { - return new InstrumentMetaData.Builder() - //workaround - get maximum of maker and taker fees - .tradingFee(bybitSymbol.getTakerFee().max(bybitSymbol.getMakerFee())) - .minimumAmount(bybitSymbol.getLotSizeFilter().getMinTradingQty()) - .maximumAmount(bybitSymbol.getLotSizeFilter().getMaxTradingQty()) - //e.g. 0.0010 -> 3 - .volumeScale(Math.max(bybitSymbol.getLotSizeFilter().getQtyStep().stripTrailingZeros().scale(), 0)) - .priceScale(bybitSymbol.getPriceScale()) - .counterMinimumAmount(bybitSymbol.getPriceFilter().getMinPrice()) - .counterMaximumAmount(bybitSymbol.getPriceFilter().getMaxPrice()) - .priceScale(bybitSymbol.getPriceScale()) - .amountStepSize(bybitSymbol.getLotSizeFilter().getQtyStep()) - .build(); - - } - -} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountService.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountService.java index ec236ed1ee9..c0177f38e44 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountService.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountService.java @@ -1,29 +1,334 @@ package org.knowm.xchange.bybit.service; +import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitDepositRecords; +import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitInternalDepositRecords; +import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitInternalTransfers; +import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitWithdrawRecords; + +import com.google.common.collect.Sets; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.knowm.xchange.Exchange; -import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.account.BybitBalance; -import org.knowm.xchange.bybit.dto.account.BybitBalances; +import org.knowm.xchange.bybit.BybitAdapters; +import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.account.BybitDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitInternalDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse.BybitWithdrawRecord.BybitWithdrawType; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.Wallet; +import org.knowm.xchange.dto.account.Wallet.WalletFeature; +import org.knowm.xchange.dto.account.params.FundingRecordParamAll; import org.knowm.xchange.service.account.AccountService; -import java.io.IOException; -import java.util.List; +public class BybitAccountService extends BybitAccountServiceRaw implements AccountService { -import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitBalances; + private final BybitAccountType accountType; -public class BybitAccountService extends BybitAccountServiceRaw implements AccountService { + private static final Integer MAX_PAGINATION_LIMIT = 50; + + public BybitAccountService(Exchange exchange, BybitAccountType accountType) { + super(exchange); + this.accountType = accountType; + } + + @Override + public AccountInfo getAccountInfo() throws IOException { + List wallets = new ArrayList<>(); + + if(accountType == BybitAccountType.UNIFIED){ + wallets.add(BybitAdapters.adaptBybitBalances(getAllCoinsBalance(BybitAccountType.UNIFIED, null, null, false).getResult(), + Sets.newHashSet(WalletFeature.MARGIN_TRADING, WalletFeature.TRADING, WalletFeature.FUTURES_TRADING, WalletFeature.OPTIONS_TRADING))); + } else if(accountType == BybitAccountType.CLASSIC) { + wallets.add(BybitAdapters.adaptBybitBalances(getAllCoinsBalance(BybitAccountType.SPOT, null, null, false).getResult(), + Sets.newHashSet(WalletFeature.TRADING, WalletFeature.MARGIN_TRADING))); + } + + return new AccountInfo(wallets); + } + + @Override + public AccountInfo getSubAccountInfo(String subAccountId) throws IOException { + List wallets = new ArrayList<>(); + + if(accountType == BybitAccountType.UNIFIED){ + wallets.add(BybitAdapters.adaptBybitBalances(getAllCoinsBalance(BybitAccountType.UNIFIED, subAccountId, null, false).getResult(), + Sets.newHashSet(WalletFeature.MARGIN_TRADING, WalletFeature.TRADING, WalletFeature.FUTURES_TRADING, WalletFeature.OPTIONS_TRADING))); + } else if(accountType == BybitAccountType.CLASSIC) { + wallets.add(BybitAdapters.adaptBybitBalances(getAllCoinsBalance(BybitAccountType.SPOT, subAccountId, null, false).getResult(), + Sets.newHashSet(WalletFeature.TRADING, WalletFeature.MARGIN_TRADING))); + } + + return new AccountInfo(wallets); + } + + @Override + public List getInternalTransferHistory(FundingRecordParamAll params) throws IOException { + + List fundingRecordList = new ArrayList<>(); + + BybitTransfersResponse res = getBybitUniversalTransfers( + params.getTransferId(), + params.getCurrency(), + BybitAdapters.convertToBybitStatus(params.getStatus()), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); + + fundingRecordList.addAll(BybitAdapters.adaptBybitUniversalTransfers(res.getInternalTransfers())); + + if(params.isUsePagination()){ + String nextPageCursor = res.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + res = getBybitUniversalTransfers( + params.getTransferId(), + params.getCurrency(), + BybitAdapters.convertToBybitStatus(params.getStatus()), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(BybitAdapters.adaptBybitUniversalTransfers(res.getInternalTransfers())); + nextPageCursor = res.getNextPageCursor(); + } + } + + return fundingRecordList; + } + + @Override + public List getWalletTransferHistory(FundingRecordParamAll params) + throws IOException { + List fundingRecordList = new ArrayList<>(); + + BybitTransfersResponse res = getBybitInternalTransfers( + params.getTransferId(), + params.getCurrency(), + BybitAdapters.convertToBybitStatus(params.getStatus()), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); + + fundingRecordList.addAll(adaptBybitInternalTransfers(res.getInternalTransfers())); + + if(params.isUsePagination()){ + String nextPageCursor = res.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + res = getBybitInternalTransfers( + params.getTransferId(), + params.getCurrency(), + BybitAdapters.convertToBybitStatus(params.getStatus()), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(adaptBybitInternalTransfers(res.getInternalTransfers())); + nextPageCursor = res.getNextPageCursor(); + } + } + + return fundingRecordList; + } + + @Override + public List getWithdrawHistory(FundingRecordParamAll params) throws IOException { + List fundingRecordList = new ArrayList<>(); + + BybitWithdrawRecordsResponse res = getBybitWithdrawRecords( + params.getTransferId(), + params.getCurrency(), + BybitWithdrawType.ALL, + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); - public BybitAccountService(Exchange exchange) { - super(exchange); + fundingRecordList.addAll(adaptBybitWithdrawRecords(res.getRows())); + + if(params.isUsePagination()){ + String nextPageCursor = res.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + res = getBybitWithdrawRecords( + params.getTransferId(), + params.getCurrency(), + BybitWithdrawType.ALL, + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(adaptBybitWithdrawRecords(res.getRows())); + nextPageCursor = res.getNextPageCursor(); + } } - @Override - public AccountInfo getAccountInfo() throws IOException { - BybitResult walletBalances = getWalletBalances(); - BybitBalances walletBalancesResult = walletBalances.getResult(); - List balances = walletBalancesResult.getBalances(); - return new AccountInfo(adaptBybitBalances(balances)); + return fundingRecordList; + } + + @Override + public List getSubAccountDepositHistory(FundingRecordParamAll params) + throws IOException { + + if (params.getSubAccountId() == null) { + throw new IllegalArgumentException("Sub account id is required"); + } + + List fundingRecordList = new ArrayList<>(); + + BybitDepositRecordsResponse res = getBybitSubAccountDepositRecords( + params.getSubAccountId(), + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); + + fundingRecordList.addAll(adaptBybitDepositRecords(res.getRows())); + + if(params.isUsePagination()){ + String nextPageCursor = res.getNextPageCursor(); + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + res = getBybitSubAccountDepositRecords( + params.getSubAccountId(), + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(adaptBybitDepositRecords(res.getRows())); + nextPageCursor = res.getNextPageCursor(); + } } + return fundingRecordList; + } + + @Override + public List getDepositHistory(FundingRecordParamAll params) throws IOException { + + BybitDepositRecordsResponse res = getBybitDepositRecords( + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); + + List fundingRecordList = new ArrayList<>(); + + BybitInternalDepositRecordsResponse internalRes = getBybitInternalDepositRecords( + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + null + ).getResult(); + + fundingRecordList.addAll(adaptBybitDepositRecords(res.getRows())); + fundingRecordList.addAll(adaptBybitInternalDepositRecords(internalRes.getRows())); + + if(params.isUsePagination()){ + // Make calls to main deposit history + String nextPageCursor = res.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + + res = getBybitDepositRecords( + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(adaptBybitDepositRecords(res.getRows())); + nextPageCursor = res.getNextPageCursor(); + } + + // Make calls to internal deposit history + nextPageCursor = internalRes.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + + internalRes = getBybitInternalDepositRecords( + params.getCurrency(), + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), // 50 is the maximum + internalRes.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(adaptBybitInternalDepositRecords(internalRes.getRows())); + nextPageCursor = internalRes.getNextPageCursor(); + } + } + + return fundingRecordList; + } + + @Override + public List getLedger(FundingRecordParamAll params) throws IOException { + List fundingRecordList = new ArrayList<>(); + + BybitTransactionLogResponse res = getBybitLedger( + accountType, + params.getAccountCategory() == null ? null : BybitCategory.valueOf(params.getAccountCategory()), + params.getCurrency(), + null, + null, + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), + null + ).getResult(); + + fundingRecordList.addAll(BybitAdapters.adaptBybitLedger(res.getList())); + + if(params.isUsePagination()){ + String nextPageCursor = res.getNextPageCursor(); + + while (nextPageCursor != null && !nextPageCursor.isEmpty()) { + res = getBybitLedger( + accountType, + null, + params.getCurrency(), + null, + null, + params.getStartTime(), + params.getEndTime(), + (params.getLimit() == null) ? MAX_PAGINATION_LIMIT : params.getLimit(), + res.getNextPageCursor() + ).getResult(); + + fundingRecordList.addAll(BybitAdapters.adaptBybitLedger(res.getList())); + + nextPageCursor = res.getNextPageCursor(); + } + + return fundingRecordList; + } else { + return BybitAdapters.adaptBybitLedger(res.getList()); + } + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountServiceRaw.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountServiceRaw.java index 776e55b552c..a5ef2742a96 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountServiceRaw.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitAccountServiceRaw.java @@ -1,13 +1,25 @@ package org.knowm.xchange.bybit.service; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bybit.BybitAdapters; -import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.account.BybitBalances; +import static org.knowm.xchange.bybit.BybitAdapters.createBybitExceptionFromResult; import java.io.IOException; - -import static org.knowm.xchange.bybit.BybitAdapters.createBybitExceptionFromResult; +import java.util.Date; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.BybitResult; +import org.knowm.xchange.bybit.dto.account.BybitDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitInternalDepositRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransactionLogResponse.BybitTransactionLog.BybitTransactionLogType; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse; +import org.knowm.xchange.bybit.dto.account.BybitTransfersResponse.BybitTransferStatus; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance; +import org.knowm.xchange.bybit.dto.account.BybitFeeRates; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse; +import org.knowm.xchange.bybit.dto.account.BybitWithdrawRecordsResponse.BybitWithdrawRecord.BybitWithdrawType; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitWalletBalance; +import org.knowm.xchange.currency.Currency; public class BybitAccountServiceRaw extends BybitBaseService { @@ -15,12 +27,263 @@ public BybitAccountServiceRaw(Exchange exchange) { super(exchange); } - public BybitResult getWalletBalances() throws IOException { - BybitResult walletBalances = bybitAuthenticated.getWalletBalances(apiKey, nonceFactory, signatureCreator); + public BybitResult getWalletBalances(BybitAccountType accountType) + throws IOException { + BybitResult walletBalances = + bybitAuthenticated.getWalletBalance( + apiKey, signatureCreator, nonceFactory, accountType.name()); if (!walletBalances.isSuccess()) { throw createBybitExceptionFromResult(walletBalances); } return walletBalances; } + public BybitResult getAllCoinsBalance( + BybitAccountType accountType, String memberId, String coin, boolean withBonus) + throws IOException { + BybitResult allCoinsBalance = + bybitAuthenticated.getAllCoinsBalance( + apiKey, + signatureCreator, + nonceFactory, + memberId, + accountType.name(), + coin, + !withBonus ? 0 : 1); + if (!allCoinsBalance.isSuccess()) { + throw createBybitExceptionFromResult(allCoinsBalance); + } + return allCoinsBalance; + } + + public BybitResult getSingleCoinBalance( + String memberId, + String toMemberId, + BybitAccountType accountType, + BybitAccountType toAccountType, + Currency coin, + boolean withBonus, + boolean withTransferSafeAmount, + boolean withLtvTransferSafeAmount) + throws IOException { + BybitResult singleCoinBalance = + bybitAuthenticated.getSingleCoinBalance( + apiKey, + signatureCreator, + nonceFactory, + memberId, + toMemberId, + (accountType == null) ? null : accountType.name(), + (toAccountType == null) ? null : toAccountType.name(), + (coin == null) ? null : coin.toString(), + (!withBonus) ? 0 : 1, + (!withTransferSafeAmount) ? 0 : 1, + (!withLtvTransferSafeAmount) ? 0 : 1); + if (!singleCoinBalance.isSuccess()) { + throw createBybitExceptionFromResult(singleCoinBalance); + } + return singleCoinBalance; + } + + public BybitResult getBybitInternalTransfers( + String transferId, + Currency coin, + BybitTransferStatus status, + Date startTime, + Date endTime, + Integer limit, + String cursor) + throws IOException { + BybitResult internalTransfers = + bybitAuthenticated.getInternalTransferRecords( + apiKey, + signatureCreator, + nonceFactory, + transferId, + (coin == null) ? null : coin.toString(), + (status == null) ? null : status.name(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor); + if (!internalTransfers.isSuccess()) { + throw createBybitExceptionFromResult(internalTransfers); + } + return internalTransfers; + } + + public BybitResult getBybitUniversalTransfers( + String transferId, + Currency coin, + BybitTransferStatus status, + Date startTime, + Date endTime, + Integer limit, + String cursor) + throws IOException { + BybitResult universalTransfers = + bybitAuthenticated.getUniversalTransferRecords( + apiKey, + signatureCreator, + nonceFactory, + transferId, + (coin == null) ? null : coin.toString(), + (status == null) ? null : status.name(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor); + if (!universalTransfers.isSuccess()) { + throw createBybitExceptionFromResult(universalTransfers); + } + return universalTransfers; + } + + public BybitResult getBybitLedger( + BybitAccountType accountType, + BybitCategory category, + Currency currency, + Currency baseCoin, + BybitTransactionLogType type, + Date startTime, + Date endTime, + Integer limit, + String cursor) + throws IOException { + BybitResult ledger = + bybitAuthenticated.getTransactionLogRecords( + apiKey, + signatureCreator, + nonceFactory, + (accountType == null) ? null : accountType.name(), + (category == null) ? null : category.getValue(), + (currency == null) ? null : currency.toString(), + (baseCoin == null) ? null : baseCoin.toString(), + (type == null) ? null : type.name(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor); + if (!ledger.isSuccess()) { + throw createBybitExceptionFromResult(ledger); + } + return ledger; + } + + public BybitResult getBybitWithdrawRecords( + String withdrawId, + Currency coin, + BybitWithdrawType withdrawType, + Date startTime, + Date endTime, + Integer limit, + String cursor + ) + throws IOException { + BybitResult withdrawRecords = + bybitAuthenticated.getWithdrawRecords( + apiKey, + signatureCreator, + nonceFactory, + withdrawId, + (coin == null) ? null : coin.toString(), + (withdrawType == null) ? null : withdrawType.getValue(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor + ); + if (!withdrawRecords.isSuccess()) { + throw createBybitExceptionFromResult(withdrawRecords); + } + return withdrawRecords; + } + + public BybitResult getBybitDepositRecords( + Currency coin, + Date startTime, + Date endTime, + Integer limit, + String cursor + ) + throws IOException { + BybitResult depositRecords = + bybitAuthenticated.getOnChainDepositRecords( + apiKey, + signatureCreator, + nonceFactory, + (coin == null) ? null : coin.toString(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor + ); + if (!depositRecords.isSuccess()) { + throw createBybitExceptionFromResult(depositRecords); + } + return depositRecords; + } + + public BybitResult getBybitInternalDepositRecords( + Currency coin, + Date startTime, + Date endTime, + Integer limit, + String cursor + ) + throws IOException { + BybitResult internalDepositRecords = + bybitAuthenticated.getInternalDepositRecords( + apiKey, + signatureCreator, + nonceFactory, + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + (coin == null) ? null : coin.toString(), + cursor, + limit + ); + if (!internalDepositRecords.isSuccess()) { + throw createBybitExceptionFromResult(internalDepositRecords); + } + return internalDepositRecords; + } + + public BybitResult getBybitSubAccountDepositRecords( + String subMemberId, + Currency coin, + Date startTime, + Date endTime, + Integer limit, + String cursor + ) + throws IOException { + BybitResult subAccountDepositRecords = + bybitAuthenticated.getSubAccountDepositRecords( + apiKey, + signatureCreator, + nonceFactory, + subMemberId, + (coin == null) ? null : coin.toString(), + (startTime == null) ? null : startTime.toInstant().toEpochMilli(), + (endTime == null) ? null : endTime.toInstant().toEpochMilli(), + limit, + cursor + ); + if (!subAccountDepositRecords.isSuccess()) { + throw createBybitExceptionFromResult(subAccountDepositRecords); + } + return subAccountDepositRecords; + } + + public BybitResult getFeeRates(BybitCategory category, String symbol) + throws IOException { + BybitResult bybitFeeRatesResult = + bybitAuthenticated.getFeeRates( + apiKey, signatureCreator, nonceFactory, category.getValue(), symbol); + if (!bybitFeeRatesResult.isSuccess()) { + throw createBybitExceptionFromResult(bybitFeeRatesResult); + } + return bybitFeeRatesResult; + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitBaseService.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitBaseService.java index d236d910cd4..1f7ade4a5ce 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitBaseService.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitBaseService.java @@ -1,10 +1,12 @@ package org.knowm.xchange.bybit.service; import java.util.concurrent.TimeUnit; +import lombok.SneakyThrows; import org.knowm.xchange.Exchange; import org.knowm.xchange.bybit.Bybit; import org.knowm.xchange.bybit.BybitAuthenticated; import org.knowm.xchange.client.ExchangeRestProxyBuilder; +import org.knowm.xchange.client.ProxyConfig; import org.knowm.xchange.service.BaseService; import org.knowm.xchange.utils.nonce.CurrentTimeIncrementalNonceFactory; import si.mazi.rescu.ParamsDigest; @@ -15,21 +17,33 @@ public class BybitBaseService implements BaseService { protected final BybitAuthenticated bybitAuthenticated; protected final Bybit bybit; protected final ParamsDigest signatureCreator; - protected final SynchronizedValueFactory nonceFactory = new CurrentTimeIncrementalNonceFactory(TimeUnit.MILLISECONDS); + protected final SynchronizedValueFactory nonceFactory = + new CurrentTimeIncrementalNonceFactory(TimeUnit.MILLISECONDS); protected final String apiKey; + @SneakyThrows public BybitBaseService(Exchange exchange) { - bybit = ExchangeRestProxyBuilder - .forInterface(Bybit.class, exchange.getExchangeSpecification()) - .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new BybitJacksonObjectMapperFactory())) - .build(); - bybitAuthenticated = ExchangeRestProxyBuilder - .forInterface(BybitAuthenticated.class, exchange.getExchangeSpecification()) - .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new BybitJacksonObjectMapperFactory())) - .build(); - signatureCreator = BybitDigest.createInstance(exchange.getExchangeSpecification().getSecretKey()); + bybit = + ExchangeRestProxyBuilder.forInterface(Bybit.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer( + clientConfig -> + clientConfig.setJacksonObjectMapperFactory( + new BybitJacksonObjectMapperFactory())) + .restProxyFactory( + ProxyConfig.getInstance().getRestProxyFactoryClass().getDeclaredConstructor().newInstance()) + .build(); + bybitAuthenticated = + ExchangeRestProxyBuilder.forInterface( + BybitAuthenticated.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer( + clientConfig -> + clientConfig.setJacksonObjectMapperFactory( + new BybitJacksonObjectMapperFactory())) + .restProxyFactory( + ProxyConfig.getInstance().getRestProxyFactoryClass().getDeclaredConstructor().newInstance()) + .build(); + signatureCreator = + BybitDigest.createInstance(exchange.getExchangeSpecification().getSecretKey()); apiKey = exchange.getExchangeSpecification().getApiKey(); } - - } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java index d8f1659080a..faf66ca7289 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java @@ -1,53 +1,58 @@ package org.knowm.xchange.bybit.service; +import static org.knowm.xchange.utils.DigestUtils.bytesToHex; + +import jakarta.ws.rs.HeaderParam; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import javax.crypto.Mac; +import lombok.SneakyThrows; import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; import org.knowm.xchange.service.BaseParamsDigest; -import si.mazi.rescu.Params; import si.mazi.rescu.ParamsDigest; import si.mazi.rescu.RestInvocation; -import javax.crypto.Mac; -import jakarta.ws.rs.FormParam; -import jakarta.ws.rs.QueryParam; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.TreeMap; - -import static org.knowm.xchange.utils.DigestUtils.bytesToHex; - public class BybitDigest extends BaseParamsDigest { - private static final String SIGNATURE = "sign"; - - public BybitDigest(String secretKeyBase64) { - super(secretKeyBase64, HMAC_SHA_256); + public static final String X_BAPI_API_KEY = "X-BAPI-API-KEY"; + public static final String X_BAPI_SIGN = "X-BAPI-SIGN"; + public static final String X_BAPI_TIMESTAMP = "X-BAPI-TIMESTAMP"; + public static final String X_BAPI_RECV_WINDOW = "X-BAPI-RECV-WINDOW"; + + public BybitDigest(String secretKeyBase64) { + super(secretKeyBase64, HMAC_SHA_256); + } + + public static ParamsDigest createInstance(String secretKeyBase64) { + return secretKeyBase64 == null ? null : new BybitDigest(secretKeyBase64); + } + + @SneakyThrows + @Override + public String digestParams(RestInvocation restInvocation) { + Map headers = restInvocation.getParamsMap().get(HeaderParam.class).asHttpHeaders(); + + // timestamp + API key + (recv_window) + (queryString | jsonBodyString) + String plainText = getPlainText(restInvocation); + + String input = + headers.get(X_BAPI_TIMESTAMP) + + headers.get(X_BAPI_API_KEY) + + headers.getOrDefault(X_BAPI_RECV_WINDOW, "") + + plainText; + Mac mac = getMac(); + mac.update(input.getBytes(StandardCharsets.UTF_8)); + return bytesToHex(mac.doFinal()); + } + + private static String getPlainText(RestInvocation restInvocation) { + if ("GET".equals(restInvocation.getHttpMethod())) { + return restInvocation.getQueryString(); } - - public static ParamsDigest createInstance(String secretKeyBase64) { - return new BybitDigest(secretKeyBase64); + if ("POST".equals(restInvocation.getHttpMethod())) { + return restInvocation.getRequestBody(); } - - @Override - public String digestParams(RestInvocation restInvocation) { - Params p = Params.of(); - Map params = getInputParams(restInvocation); - params.remove(SIGNATURE); - Map sortedParams = new TreeMap<>(params); - sortedParams.forEach(p::add); - String input = p.asQueryString(); - Mac mac = getMac(); - mac.update(input.getBytes(StandardCharsets.UTF_8)); - return bytesToHex(mac.doFinal()); - } - - private Map getInputParams(RestInvocation restInvocation) { - if ("GET".equals(restInvocation.getHttpMethod())) { - return restInvocation.getParamsMap().get(QueryParam.class).asHttpHeaders(); - } - if ("POST".equals(restInvocation.getHttpMethod())) { - return restInvocation.getParamsMap().get(FormParam.class).asHttpHeaders(); - } - throw new NotYetImplementedForExchangeException("Only GET and POST are supported in digest"); - } - -} \ No newline at end of file + throw new NotYetImplementedForExchangeException( + "Only GET and POST are supported for plain text"); + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitException.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitException.java index 3222e15c10c..b03ab3a8754 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitException.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitException.java @@ -1,43 +1,29 @@ package org.knowm.xchange.bybit.service; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.ToString; import si.mazi.rescu.HttpStatusExceptionSupport; +@ToString +@Getter public class BybitException extends HttpStatusExceptionSupport { private final int retCode; private final String retMsg; - private final String extCode; - private final String extInfo; + private final Object retExtInfo; - public BybitException(@JsonProperty("ret_code") int retCode, - @JsonProperty("ret_msg") String retMsg, - @JsonProperty("ext_code") String extCode, - @JsonProperty("ext_info") String extInfo) { + public BybitException( + @JsonProperty("retCode") int retCode, + @JsonProperty("retMsg") String retMsg, + @JsonProperty("retExtInfo") Object retExtInfo) { this.retCode = retCode; this.retMsg = retMsg; - this.extCode = extCode; - this.extInfo = extInfo; + this.retExtInfo = retExtInfo; } @Override public String getMessage() { - return "{" + - "retCode=" + retCode + - ", retMsg='" + retMsg + '\'' + - ", extCode='" + extCode + '\'' + - ", extInfo='" + extInfo + '\'' + - '}'; + return toString(); } - - @Override - public String toString() { - return "BybitException{" + - "retCode=" + retCode + - ", retMsg='" + retMsg + '\'' + - ", extCode='" + extCode + '\'' + - ", extInfo='" + extInfo + '\'' + - '}'; - } - } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitJacksonObjectMapperFactory.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitJacksonObjectMapperFactory.java index 7c4ca13d982..250d5568ed6 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitJacksonObjectMapperFactory.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitJacksonObjectMapperFactory.java @@ -9,8 +9,7 @@ public class BybitJacksonObjectMapperFactory extends DefaultJacksonObjectMapperF @Override public void configureObjectMapper(ObjectMapper objectMapper) { super.configureObjectMapper(objectMapper); - //depending on api version bybit sends jsons with double- and single-quotes + // depending on api version bybit sends jsons with double- and single-quotes objectMapper.configure(Feature.ALLOW_SINGLE_QUOTES, true); - } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataService.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataService.java index b4104924286..52b6981ebc7 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataService.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataService.java @@ -1,13 +1,22 @@ package org.knowm.xchange.bybit.service; import java.io.IOException; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import org.knowm.xchange.bybit.BybitAdapters; import org.knowm.xchange.bybit.BybitExchange; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.marketdata.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers; +import org.knowm.xchange.bybit.dto.marketdata.tickers.linear.BybitLinearInverseTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.option.BybitOptionTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.spot.BybitSpotTicker; +import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.meta.CurrencyMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.marketdata.MarketDataService; import org.knowm.xchange.utils.Assert; @@ -18,37 +27,65 @@ public BybitMarketDataService(BybitExchange exchange) { super(exchange); } - @Override public Ticker getTicker(Instrument instrument, Object... args) throws IOException { Assert.notNull(instrument, "Null instrument"); - BybitResult> response = getTicker24h(BybitAdapters.convertToBybitSymbol(instrument.toString())); + BybitCategory category = getCategory(args); + + BybitResult> response = + getTicker24h(category, BybitAdapters.convertToBybitSymbol(instrument.toString())); - if (response.getResult().isEmpty()) { + if (response.getResult().getList().isEmpty()) { return new Ticker.Builder().build(); } else { - BybitTicker bybitTicker = response.getResult().get(0); - return new Ticker.Builder() - .timestamp(response.getTimeNow()) - .instrument(instrument) - .bid(bybitTicker.getBestBidPrice()) - .ask(bybitTicker.getBestAskPrice()) - .volume(bybitTicker.getVolume24h()) - .quoteVolume(bybitTicker.getTurnover24h()) - .last(bybitTicker.getLastPrice()) - .high(bybitTicker.getHighPrice()) - .low(bybitTicker.getLowPrice()) - .open(bybitTicker.getPrevPrice24h()) - .percentageChange(bybitTicker.getPrice24hPercentageChange()) - .build(); + BybitTicker bybitTicker = response.getResult().getList().get(0); + switch (category) { + case SPOT: + return BybitAdapters.adaptBybitSpotTicker( + instrument, response.getTime(), (BybitSpotTicker) bybitTicker); + case LINEAR: + case INVERSE: + return BybitAdapters.adaptBybitLinearInverseTicker( + instrument, response.getTime(), (BybitLinearInverseTicker) bybitTicker); + case OPTION: + return BybitAdapters.adaptBybitOptionTicker( + instrument, response.getTime(), (BybitOptionTicker) bybitTicker); + default: + throw new IllegalStateException("Unexpected value: " + category); + } } } + private static BybitCategory getCategory(Object[] args) { + if (args.length > 0 && args[0] != null) { + return (BybitCategory) args[0]; + } else { + return BybitCategory.LINEAR; + } + } @Override public Ticker getTicker(CurrencyPair currencyPair, Object... args) throws IOException { return getTicker((Instrument) currencyPair, args); } + + @Override + public Map getInstruments() throws IOException { + Map instrumentsMap = new HashMap<>(); + + instrumentsMap.putAll(BybitAdapters.adaptBybitInstruments(getInstrumentsInfo(BybitCategory.SPOT, null, null, null, 1000, null).getResult().getList())); + instrumentsMap.putAll(BybitAdapters.adaptBybitInstruments(getInstrumentsInfo(BybitCategory.LINEAR, null, null, null, 1000, null).getResult().getList())); + instrumentsMap.putAll(BybitAdapters.adaptBybitInstruments(getInstrumentsInfo(BybitCategory.INVERSE, null, null, null, 1000, null).getResult().getList())); + instrumentsMap.putAll(BybitAdapters.adaptBybitInstruments(getInstrumentsInfo(BybitCategory.OPTION, null, null, null, 1000, null).getResult().getList())); + + return instrumentsMap; + } + @Override + public Map getCurrencies() throws IOException { + return new HashMap<>(BybitAdapters.adaptBybitCurrencies( + getInstrumentsInfo(BybitCategory.SPOT, null, null, null, 1000, null).getResult() + .getList())); + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRaw.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRaw.java index 3c8aba80f59..3c49e8aab47 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRaw.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRaw.java @@ -1,12 +1,14 @@ package org.knowm.xchange.bybit.service; import java.io.IOException; -import java.util.List; import org.knowm.xchange.bybit.BybitAdapters; import org.knowm.xchange.bybit.BybitExchange; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.marketdata.BybitSymbol; -import org.knowm.xchange.bybit.dto.marketdata.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers; public class BybitMarketDataServiceRaw extends BybitBaseService { @@ -14,8 +16,9 @@ public BybitMarketDataServiceRaw(BybitExchange exchange) { super(exchange); } - public BybitResult> getTicker24h(String symbol) throws IOException { - BybitResult> result = bybit.getTicker24h(symbol); + public BybitResult> getTicker24h(BybitCategory category, String symbol) + throws IOException { + BybitResult> result = bybit.getTicker24h(category.getValue(), symbol); if (!result.isSuccess()) { throw BybitAdapters.createBybitExceptionFromResult(result); @@ -23,9 +26,23 @@ public BybitResult> getTicker24h(String symbol) throws IOExcep return result; } - - public BybitResult> getSymbols() throws IOException { - BybitResult> result = bybit.getSymbols(); + public BybitResult> getInstrumentsInfo( + BybitCategory category, + String symbol, + String status, + String baseCoin, + Integer limit, + String cursor + ) throws IOException { + BybitResult> result = + bybit.getInstrumentsInfo( + category.getValue(), + symbol, + status, + baseCoin, + limit, + cursor + ); if (!result.isSuccess()) { throw BybitAdapters.createBybitExceptionFromResult(result); @@ -33,4 +50,19 @@ public BybitResult> getSymbols() throws IOException { return result; } + public BybitResult getAssetsInfo( + String accountType, + String coin + ) throws IOException { + BybitResult result = + bybit.getAssetsInfo( + accountType, + coin + ); + + if (!result.isSuccess()) { + throw BybitAdapters.createBybitExceptionFromResult(result); + } + return result; + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeService.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeService.java index 12ca1a37bf2..ccbae718a54 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeService.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeService.java @@ -1,49 +1,145 @@ package org.knowm.xchange.bybit.service; +import static org.knowm.xchange.bybit.BybitAdapters.adaptBybitOrderDetails; +import static org.knowm.xchange.bybit.BybitAdapters.convertToBybitSymbol; +import static org.knowm.xchange.bybit.BybitAdapters.getSideString; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.BybitAdapters; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.trade.BybitOrderDetails; -import org.knowm.xchange.bybit.dto.trade.BybitOrderRequest; +import org.knowm.xchange.bybit.dto.trade.BybitOrderResponse; +import org.knowm.xchange.bybit.dto.trade.BybitOrderType; +import org.knowm.xchange.bybit.dto.trade.BybitTradeHistoryResponse; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails; import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.marketdata.Trades.TradeSortType; import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.dto.trade.UserTrades; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.trade.TradeService; +import org.knowm.xchange.service.trade.params.TradeHistoryParamId; +import org.knowm.xchange.service.trade.params.TradeHistoryParamInstrument; +import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit; +import org.knowm.xchange.service.trade.params.TradeHistoryParamUserReference; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +public class BybitTradeService extends BybitTradeServiceRaw implements TradeService { -import static org.knowm.xchange.bybit.BybitAdapters.*; + public BybitTradeService(Exchange exchange) { + super(exchange); + } -public class BybitTradeService extends BybitTradeServiceRaw implements TradeService { + @Override + public String placeMarketOrder(MarketOrder marketOrder) throws IOException { + BybitResult order = + placeOrder( + BybitCategory.SPOT, + convertToBybitSymbol(marketOrder.getInstrument().toString()), + getSideString(marketOrder.getType()), + BybitOrderType.MARKET, + marketOrder.getOriginalAmount()); + + return order.getResult().getOrderId(); + } + + @Override + public Collection getOrder(String... orderIds) throws IOException { + List results = new ArrayList<>(); + + for (String orderId : orderIds) { + BybitResult> bybitOrder = + getBybitOrder(BybitCategory.SPOT, orderId); + BybitOrderDetail bybitOrderResult = bybitOrder.getResult().getList().get(0); + Order order = adaptBybitOrderDetails(bybitOrderResult); + results.add(order); + } + + return results; + } + + @Override + public UserTrades getTradeHistory(TradeHistoryParams params) throws IOException { + + if (!(params instanceof TradeHistoryParamInstrument)) { + throw new IOException( + "Params must be instance of " + TradeHistoryParamInstrument.class.getSimpleName()); + } + + if(((TradeHistoryParamInstrument) params).getInstrument() == null){ + throw new IllegalArgumentException("Instrument must not be null."); + } - public BybitTradeService(Exchange exchange) { - super(exchange); + Instrument instrument = ((TradeHistoryParamInstrument) params).getInstrument(); + BybitCategory category = BybitAdapters.getBybitCategoryFromInstrument(instrument); + String orderId = null; + String userReference = null; + Date startTime = null; + Date endTime = null; + Integer limit = 100; + + if(params instanceof TradeHistoryParamId) { + orderId = ((TradeHistoryParamId) params).getId(); + } + + if(params instanceof TradeHistoryParamUserReference){ + userReference = ((TradeHistoryParamUserReference) params).getUserReference(); } - @Override - public String placeMarketOrder(MarketOrder marketOrder) throws IOException { - BybitResult order = placeOrder( - convertToBybitSymbol(marketOrder.getInstrument().toString()), - marketOrder.getOriginalAmount().longValue(), - getSideString(marketOrder.getType()), - "MARKET"); + if(params instanceof TradeHistoryParamsTimeSpan){ + startTime = ((TradeHistoryParamsTimeSpan) params).getStartTime(); + endTime = ((TradeHistoryParamsTimeSpan) params).getEndTime(); + } - return order.getResult().getOrderId(); + if(params instanceof TradeHistoryParamLimit) { + limit = ((TradeHistoryParamLimit) params).getLimit(); } - @Override - public Collection getOrder(String... orderIds) throws IOException { - List results = new ArrayList<>(); + BybitTradeHistoryResponse res = getBybitTradeHistory( + category, + instrument, + orderId, + userReference, + null, + startTime, + endTime, + null, + limit, + null).getResult(); + + String nextPageCursor = res.getNextPageCursor(); + + List userTradeList = new ArrayList<>(); + + while (nextPageCursor != null) { + userTradeList.addAll(BybitAdapters.adaptUserTrades(res)); - for (String orderId : orderIds) { - BybitResult bybitOrder = getBybitOrder(orderId); - BybitOrderDetails bybitOrderResult = bybitOrder.getResult(); - Order order = adaptBybitOrderDetails(bybitOrderResult); - results.add(order); - } + res = getBybitTradeHistory( + category, + instrument, + orderId, + userReference, + null, + startTime, + endTime, + null, + limit, + nextPageCursor).getResult(); - return results; + nextPageCursor = res.getNextPageCursor(); + if(nextPageCursor.isEmpty()){ + break; + } } + return new UserTrades(userTradeList, TradeSortType.SortByTimestamp); + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java index b2e0b9fb20e..ad605c694c4 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java @@ -1,13 +1,23 @@ package org.knowm.xchange.bybit.service; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.trade.BybitOrderDetails; -import org.knowm.xchange.bybit.dto.trade.BybitOrderRequest; +import static org.knowm.xchange.bybit.BybitAdapters.createBybitExceptionFromResult; import java.io.IOException; - -import static org.knowm.xchange.bybit.BybitAdapters.createBybitExceptionFromResult; +import java.math.BigDecimal; +import java.util.Date; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.BybitAdapters; +import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.BybitResult; +import org.knowm.xchange.bybit.dto.trade.BybitExecType; +import org.knowm.xchange.bybit.dto.trade.BybitTradeHistoryResponse; +import org.knowm.xchange.bybit.dto.trade.BybitOrderResponse; +import org.knowm.xchange.bybit.dto.trade.BybitOrderType; +import org.knowm.xchange.bybit.dto.trade.BybitSide; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.instrument.Instrument; public class BybitTradeServiceRaw extends BybitBaseService { @@ -15,28 +25,70 @@ public BybitTradeServiceRaw(Exchange exchange) { super(exchange); } - public BybitResult getBybitOrder(String orderId) throws IOException { - BybitResult order = bybitAuthenticated.getOrder(apiKey, orderId, nonceFactory, signatureCreator); + public BybitResult> getBybitOrder( + BybitCategory category, String orderId) throws IOException { + BybitResult> order = + bybitAuthenticated.getOpenOrders( + apiKey, signatureCreator, nonceFactory, category.getValue(), orderId); if (!order.isSuccess()) { throw createBybitExceptionFromResult(order); } return order; } - public BybitResult placeOrder(String symbol, long qty, String side, String type) throws IOException { - BybitResult placeOrder = bybitAuthenticated.placeOrder( + public BybitResult placeOrder( + BybitCategory category, + String symbol, + BybitSide side, + BybitOrderType orderType, + BigDecimal qty) + throws IOException { + BybitResult placeOrder = + bybitAuthenticated.placeOrder( apiKey, - symbol, - qty, - side, - type, + signatureCreator, nonceFactory, - signatureCreator - ); + category.getValue(), + symbol, + side.getValue(), + orderType.getValue(), + qty); if (!placeOrder.isSuccess()) { throw createBybitExceptionFromResult(placeOrder); } return placeOrder; } + public BybitResult getBybitTradeHistory( + BybitCategory category, + Instrument instrument, + String orderId, + String userReferenceId, + Currency baseCoin, + Date startTime, + Date endTime, + BybitExecType execType, + Integer limit, + String cursor) + throws BybitException, IOException { + BybitResult userTrades = + bybitAuthenticated.getBybitTradeHistory( + apiKey, + signatureCreator, + nonceFactory, + category.getValue(), + BybitAdapters.adaptBybitSymbol(instrument), + orderId, + userReferenceId, + baseCoin == null ? null : baseCoin.getCurrencyCode(), + startTime == null ? null : startTime.toInstant().toEpochMilli(), + endTime == null ? null : endTime.toInstant().toEpochMilli(), + execType == null ? null : execType.getValue(), + limit, + cursor); + if (!userTrades.isSuccess()) { + throw createBybitExceptionFromResult(userTrades); + } + return userTrades; + } } diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitAdaptersTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitAdaptersTest.java index e689abf00a9..b55631abe20 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitAdaptersTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitAdaptersTest.java @@ -1,22 +1,19 @@ package org.knowm.xchange.bybit; -import org.junit.Test; -import org.knowm.xchange.currency.CurrencyPair; - import static org.assertj.core.api.Assertions.assertThat; import static org.knowm.xchange.bybit.BybitAdapters.guessSymbol; -public class BybitAdaptersTest { - - - @Test - public void testGuessSymbol() { - assertThat(guessSymbol("BTCUSDT")).isEqualTo(new CurrencyPair("BTC", "USDT")); - assertThat(guessSymbol("BTCUSDC")).isEqualTo(new CurrencyPair("BTC", "USDC")); - assertThat(guessSymbol("LTCBTC")).isEqualTo(new CurrencyPair("LTC", "BTC")); - assertThat(guessSymbol("BTCDAI")).isEqualTo(new CurrencyPair("BTC", "DAI")); - assertThat(guessSymbol("ABCDEFG")).isEqualTo(new CurrencyPair("ABCD", "EFG")); +import org.junit.Test; +import org.knowm.xchange.currency.CurrencyPair; - } +public class BybitAdaptersTest { + @Test + public void testGuessSymbol() { + assertThat(guessSymbol("BTCUSDT")).isEqualTo(new CurrencyPair("BTC", "USDT")); + assertThat(guessSymbol("BTCUSDC")).isEqualTo(new CurrencyPair("BTC", "USDC")); + assertThat(guessSymbol("LTCBTC")).isEqualTo(new CurrencyPair("LTC", "BTC")); + assertThat(guessSymbol("BTCDAI")).isEqualTo(new CurrencyPair("BTC", "DAI")); + assertThat(guessSymbol("ABCDEFG")).isEqualTo(new CurrencyPair("ABCD", "EFG")); + } } diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeInit.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeInit.java new file mode 100644 index 00000000000..188220426ee --- /dev/null +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeInit.java @@ -0,0 +1,11 @@ +package org.knowm.xchange.bybit; + +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeFactory; + +public class BybitExchangeInit { + + public static Exchange getBybitExchange() { + return ExchangeFactory.INSTANCE.createExchange(BybitExchange.class); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeTest.java index 23059b7d133..7be4ea463e6 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitExchangeTest.java @@ -1,15 +1,8 @@ package org.knowm.xchange.bybit; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import jakarta.ws.rs.core.Response.Status; -import org.apache.commons.io.IOUtils; import org.junit.Test; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeSpecification; @@ -17,26 +10,17 @@ public class BybitExchangeTest extends BaseWiremockTest { - @Test public void testSymbolLoading() throws IOException { Exchange bybitExchange = createExchange(); - stubFor( - get(urlPathEqualTo("/v2/public/symbols")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(IOUtils.resourceToString("/getSymbols.json5", StandardCharsets.UTF_8)) - ) - ); + initGetStub("/v5/market/instruments-info", "/getInstrumentSpot.json5"); + initGetStub("/v5/account/fee-rate", "/getFeeRates.json5"); ExchangeSpecification specification = bybitExchange.getExchangeSpecification(); specification.setShouldLoadRemoteMetaData(true); bybitExchange.applySpecification(specification); - assertThat(bybitExchange.getExchangeMetaData().getInstruments()).hasSize(2); - + assertThat(bybitExchange.getExchangeMetaData().getInstruments()).hasSize(1); } -} \ No newline at end of file +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPrivateEndpointsTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPrivateEndpointsTest.java new file mode 100644 index 00000000000..b88c88f67eb --- /dev/null +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPrivateEndpointsTest.java @@ -0,0 +1,257 @@ +package org.knowm.xchange.bybit; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; +import java.util.Properties; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.params.FundingRecordParamAll; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsAll; + +@Ignore +public class BybitPrivateEndpointsTest { + + static Exchange exchange; + + Instrument instrument = new CurrencyPair("BTC/USDT"); + static String subAccountId; + + @BeforeClass + public static void setUp(){ + Properties properties = new Properties(); + + try { + properties.load(BybitPrivateEndpointsTest.class.getResourceAsStream("/secret.keys")); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ExchangeSpecification spec = new BybitExchange().getDefaultExchangeSpecification(); + + spec.setApiKey(properties.getProperty("apikey")); + spec.setSecretKey(properties.getProperty("secret")); + spec.setExchangeSpecificParametersItem(Exchange.USE_SANDBOX, true); + + exchange = ExchangeFactory.INSTANCE.createExchange(BybitExchange.class); + exchange.applySpecification(spec); + + subAccountId = properties.getProperty("subAccountId"); + } + + @Test + public void testTradeHistory() throws IOException { + + TradeHistoryParamsAll params = new TradeHistoryParamsAll(); + + params.setInstrument(instrument); + + List userTrades = exchange + .getTradeService() + .getTradeHistory(params) + .getUserTrades(); + + assertThat(userTrades.isEmpty()).isFalse(); + + userTrades.forEach( + userTrade -> { + assertThat(userTrade.getId()).isNotNull(); + assertThat(userTrade.getOrderId()).isNotNull(); + assertThat(userTrade.getOrderUserReference()).isNotNull(); + assertThat(userTrade.getOriginalAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getPrice()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getFeeAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getType()).isNotNull(); + assertThat(userTrade.getFeeCurrency()).isNotNull(); + assertThat(userTrade.getTimestamp()).isNotNull(); + }); + } + + @Test + public void testAccountInfo() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getAccountInfo(); + + assertThat(accountInfo.getWallets().size()).isGreaterThan(1); + accountInfo + .getWallets() + .forEach( + (s, wallet) -> { + assertThat(BybitAccountType.valueOf(s)).isInstanceOf(BybitAccountType.class); + assertThat(BybitAccountType.valueOf(wallet.getId())).isInstanceOf(BybitAccountType.class); + assertThat(wallet.getFeatures()).isNotNull(); + + wallet.getBalances().forEach((currency, balance) -> { + assertThat(currency).isNotNull(); + assertThat(balance.getAvailable()).isNotNull(); + assertThat(balance.getTotal()).isNotNull(); + assertThat(balance.getFrozen()).isNotNull(); + assertThat(balance.getBorrowed()).isNotNull(); + }); + }); + } + + @Test + public void testSubAccountInfo() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getSubAccountInfo(subAccountId); + + assertThat(accountInfo.getWallets().size()).isGreaterThan(1); + accountInfo + .getWallets() + .forEach( + (s, wallet) -> { + assertThat(BybitAccountType.valueOf(s)).isInstanceOf(BybitAccountType.class); + assertThat(BybitAccountType.valueOf(wallet.getId())).isInstanceOf(BybitAccountType.class); + assertThat(wallet.getFeatures()).isNotNull(); + + wallet.getBalances().forEach((currency, balance) -> { + assertThat(currency).isNotNull(); + assertThat(balance.getAvailable()).isNotNull(); + assertThat(balance.getTotal()).isNotNull(); + assertThat(balance.getFrozen()).isNotNull(); + assertThat(balance.getBorrowed()).isNotNull(); + }); + }); + } + + @Test + public void testWalletTransfers() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .status(FundingRecord.Status.COMPLETE) + .build(); + List records = exchange.getAccountService().getWalletTransferHistory(paramAll); + + assertThat(records.isEmpty()).isFalse(); + + records.forEach( + internalFundingRecord -> { + assertThat(internalFundingRecord.getInternalId()).isNotNull(); + assertThat(internalFundingRecord.getDate()).isNotNull(); + assertThat(internalFundingRecord.getAmount()).isNotNull(); + assertThat(internalFundingRecord.getFromWallet()).isNotNull(); + assertThat(internalFundingRecord.getToWallet()).isNotNull(); + assertThat(internalFundingRecord.getStatus()).isNotNull(); + assertThat(internalFundingRecord.getDescription()).isNotNull(); + }); + } + + @Test + public void testSubAccountTransfers() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .build(); + List records = exchange.getAccountService().getInternalTransferHistory(paramAll); + + assertThat(records.isEmpty()).isFalse(); + + records.forEach( + fundingRecord -> { + assertThat(fundingRecord.getInternalId()).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isNotNull(); + assertThat(fundingRecord.getStatus()).isNotNull(); + assertThat(fundingRecord.getToSubAccount()).isNotNull(); + assertThat(fundingRecord.getFromSubAccount()).isNotNull(); + assertThat(fundingRecord.getToWallet()).isNotNull(); + assertThat(fundingRecord.getFromWallet()).isNotNull(); + assertThat(fundingRecord.getDescription()).isNotNull(); + }); + } + + @Test + public void testAccountDepositHistory() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .build(); + List records = exchange.getAccountService().getDepositHistory(paramAll); + + assertThat(records.isEmpty()).isFalse(); + + records.forEach( + fundingRecord -> { + assertThat(fundingRecord.getInternalId()).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isNotNull(); + assertThat(fundingRecord.getStatus()).isNotNull(); + assertThat(fundingRecord.getAddress()).isNotNull(); + assertThat(fundingRecord.getAddressTag()).isNotNull(); + assertThat(fundingRecord.getFee()).isNotNull(); + assertThat(fundingRecord.getType()).isNotNull(); + assertThat(fundingRecord.getBlockchainTransactionHash()).isNotNull(); + }); + } + + @Test + public void testAccountWithdrawHistory() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .build(); + List records = exchange.getAccountService().getWithdrawHistory(paramAll); + + assertThat(records.isEmpty()).isFalse(); + + records.forEach( + fundingRecord -> { + assertThat(fundingRecord.getInternalId()).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isNotNull(); + assertThat(fundingRecord.getStatus()).isNotNull(); + assertThat(fundingRecord.getAddress()).isNotNull(); + assertThat(fundingRecord.getAddressTag()).isNotNull(); + assertThat(fundingRecord.getFee()).isNotNull(); + assertThat(fundingRecord.getType()).isNotNull(); + assertThat(fundingRecord.getBlockchainTransactionHash()).isNotNull(); + }); + } + @Test + public void testSubAccountDepositHistory() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .subAccountId(subAccountId) + .build(); + List records = exchange.getAccountService().getSubAccountDepositHistory(paramAll); + + records.forEach( + fundingRecord -> { + assertThat(fundingRecord.getInternalId()).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isNotNull(); + assertThat(fundingRecord.getStatus()).isNotNull(); + assertThat(fundingRecord.getAddress()).isNotNull(); + assertThat(fundingRecord.getAddressTag()).isNotNull(); + assertThat(fundingRecord.getFee()).isNotNull(); + assertThat(fundingRecord.getType()).isNotNull(); + assertThat(fundingRecord.getBlockchainTransactionHash()).isNotNull(); + assertThat(fundingRecord.getDescription()).isNotNull(); + }); + } + + @Test + public void testLedger() throws IOException { + FundingRecordParamAll paramAll = FundingRecordParamAll.builder() + .usePagination(true) + .build(); + List records = exchange.getAccountService().getLedger(paramAll); + + assertThat(records.isEmpty()).isFalse(); + + records.forEach( + fundingRecord -> { + assertThat(fundingRecord.getInternalId()).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isNotNull(); + assertThat(fundingRecord.getBalance()).isNotNull(); + assertThat(fundingRecord.getStatus()).isEqualTo(FundingRecord.Status.COMPLETE); + assertThat(fundingRecord.getType()).isNotNull(); + assertThat(fundingRecord.getFee()).isNotNull(); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPublicEndpointsTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPublicEndpointsTest.java new file mode 100644 index 00000000000..d5986e99dc8 --- /dev/null +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/BybitPublicEndpointsTest.java @@ -0,0 +1,54 @@ +package org.knowm.xchange.bybit; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.meta.CurrencyMetaData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BybitPublicEndpointsTest { + + private static final Exchange exchange = BybitExchangeInit.getBybitExchange(); + private final Logger LOG = LoggerFactory.getLogger(BybitPublicEndpointsTest.class); + @Test + public void checkInstrumentMetaData() { + exchange + .getExchangeMetaData() + .getInstruments() + .forEach( + (instrument1, instrumentMetaData) -> { + LOG.debug(instrument1 + "||" + instrumentMetaData); + assertThat(instrumentMetaData.getMinimumAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(instrumentMetaData.getMaximumAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(instrumentMetaData.getAmountStepSize()).isGreaterThan(BigDecimal.ZERO); + assertThat(instrumentMetaData.getPriceStepSize()).isGreaterThan(BigDecimal.ZERO); + assertThat(instrumentMetaData.getPriceScale()).isNotNegative(); + assertThat(instrumentMetaData.getVolumeScale()).isNotNegative(); + }); + } + + @Test + public void checkCurrenciesMetaData() { + Map currencyMetaDataMap = exchange.getExchangeMetaData().getCurrencies(); + Map numberOfOccurrences = new HashMap<>(); + + currencyMetaDataMap.forEach((currency,currencyMetaData) -> { + assertThat(currency).isNotNull(); + assertThat(currencyMetaData.getScale()).isNotNegative(); + + if(numberOfOccurrences.containsKey(currency)){ + numberOfOccurrences.put(currency, numberOfOccurrences.get(currency) + 1); + } else { + numberOfOccurrences.put(currency, 1); + } + }); + + numberOfOccurrences.forEach((currency,integer) -> assertThat(integer).isEqualTo(1)); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BaseWiremockTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BaseWiremockTest.java index 6530e230eb2..e43d187daab 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BaseWiremockTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BaseWiremockTest.java @@ -1,30 +1,46 @@ package org.knowm.xchange.bybit.service; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; + import com.github.tomakehurst.wiremock.junit.WireMockRule; +import jakarta.ws.rs.core.Response.Status; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; import org.junit.Rule; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.bybit.BybitExchange; -import java.io.IOException; - public class BaseWiremockTest { - @Rule - public WireMockRule wireMockRule = new WireMockRule(); + @Rule public WireMockRule wireMockRule = new WireMockRule(); + + public Exchange createExchange() throws IOException { + Exchange exchange = + ExchangeFactory.INSTANCE.createExchangeWithoutSpecification(BybitExchange.class); + ExchangeSpecification specification = exchange.getDefaultExchangeSpecification(); + specification.setHost("localhost"); + specification.setSslUri("http://localhost:" + wireMockRule.port()); + specification.setPort(wireMockRule.port()); + specification.setApiKey("test_api_key"); + specification.setSecretKey("test_secret_key"); + specification.setShouldLoadRemoteMetaData(false); + exchange.applySpecification(specification); + return exchange; + } - public Exchange createExchange() throws IOException { - Exchange exchange = - ExchangeFactory.INSTANCE.createExchangeWithoutSpecification(BybitExchange.class); - ExchangeSpecification specification = exchange.getDefaultExchangeSpecification(); - specification.setHost("localhost"); - specification.setSslUri("http://localhost:" + wireMockRule.port()); - specification.setPort(wireMockRule.port()); - specification.setApiKey("test_api_key"); - specification.setSecretKey("test_secret_key"); - specification.setShouldLoadRemoteMetaData(false); - exchange.applySpecification(specification); - return exchange; - } + protected void initGetStub(String url, String responseBody) throws IOException { + stubFor( + get(urlPathEqualTo(url)) + .willReturn( + aResponse() + .withStatus(Status.OK.getStatusCode()) + .withHeader("Content-Type", "application/json") + .withBody(IOUtils.resourceToString(responseBody, StandardCharsets.UTF_8)))); + } } diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceRawTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceRawTest.java index 5ef8e44d5f1..1ee2b89065b 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceRawTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceRawTest.java @@ -1,82 +1,115 @@ package org.knowm.xchange.bybit.service; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.util.List; -import jakarta.ws.rs.core.Response.Status; +import org.junit.Before; import org.junit.Test; import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.account.BybitBalance; -import org.knowm.xchange.bybit.dto.account.BybitBalances; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance; +import org.knowm.xchange.bybit.dto.account.BybitAllCoinsBalance.BybitCoinBalance; +import org.knowm.xchange.bybit.dto.account.BybitFeeRates; +import org.knowm.xchange.bybit.dto.account.BybitFeeRates.BybitFeeRate; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountBalance; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitCoinWalletBalance; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitWalletBalance; public class BybitAccountServiceRawTest extends BaseWiremockTest { + private BybitAccountServiceRaw bybitAccountServiceRaw; + + @Before + public void setUp() throws Exception { + Exchange bybitExchange = createExchange(); + bybitAccountServiceRaw = new BybitAccountServiceRaw(bybitExchange); + } + @Test public void testGetWalletBalances() throws IOException { - Exchange bybitExchange = createExchange(); - BybitAccountServiceRaw bybitAccountServiceRaw = new BybitAccountServiceRaw(bybitExchange); - - String walletBalanceDetails = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"balances\":[\n" + - " {\n" + - " \"coin\":\"COIN\",\n" + - " \"coinId\":\"COIN\",\n" + - " \"coinName\":\"COIN\",\n" + - " \"total\":\"66419.616666666666666666\",\n" + - " \"free\":\"56583.326666666666666666\",\n" + - " \"locked\":\"9836.29\"\n" + - " },\n" + - " {\n" + - " \"coin\":\"USDT\",\n" + - " \"coinId\":\"USDT\",\n" + - " \"coinName\":\"USDT\",\n" + - " \"total\":\"61.50059688096\",\n" + - " \"free\":\"61.50059688096\",\n" + - " \"locked\":\"0\"\n" + - " }\n" + - " ]\n" + - " }\n" + - "}"; - - stubFor( - get(urlPathEqualTo("/spot/v1/account")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(walletBalanceDetails) - ) - ); - - BybitResult walletBalances = bybitAccountServiceRaw.getWalletBalances(); - - BybitBalances walletBalancesResult = walletBalances.getResult(); - List balances = walletBalancesResult.getBalances(); - - assertThat(balances.get(0).getTotal()).isEqualTo("66419.616666666666666666"); - assertThat(balances.get(0).getFree()).isEqualTo("56583.326666666666666666"); - assertThat(balances.get(0).getLocked()).isEqualTo("9836.29"); - assertThat(balances.get(0).getCoin()).isEqualTo("COIN"); - assertThat(balances.get(0).getCoinId()).isEqualTo("COIN"); - assertThat(balances.get(0).getCoinName()).isEqualTo("COIN"); - - assertThat(balances.get(1).getTotal()).isEqualTo("61.50059688096"); - assertThat(balances.get(1).getFree()).isEqualTo("61.50059688096"); - assertThat(balances.get(1).getLocked()).isEqualTo("0"); - assertThat(balances.get(1).getCoin()).isEqualTo("USDT"); - assertThat(balances.get(1).getCoinId()).isEqualTo("USDT"); - assertThat(balances.get(1).getCoinName()).isEqualTo("USDT"); + initGetStub("/v5/account/wallet-balance", "/getWalletBalance.json5"); + + BybitResult walletBalances = + bybitAccountServiceRaw.getWalletBalances(BybitAccountType.UNIFIED); + + BybitWalletBalance walletBalance = walletBalances.getResult(); + + assertThat(walletBalance.getList()).hasSize(1); + BybitAccountBalance accountBalance = walletBalance.getList().get(0); + + assertThat(accountBalance.getTotalEquity()).isEqualTo("3.31216591"); + assertThat(accountBalance.getAccountIMRate()).isEqualTo("0"); + assertThat(accountBalance.getTotalMarginBalance()).isEqualTo("3.00326056"); + assertThat(accountBalance.getTotalInitialMargin()).isEqualTo("0"); + assertThat(accountBalance.getAccountType()).isEqualTo(BybitAccountType.UNIFIED); + assertThat(accountBalance.getTotalAvailableBalance()).isEqualTo("3.00326056"); + assertThat(accountBalance.getAccountMMRate()).isEqualTo("0"); + assertThat(accountBalance.getTotalPerpUPL()).isEqualTo("0"); + assertThat(accountBalance.getTotalWalletBalance()).isEqualTo("3.00326056"); + assertThat(accountBalance.getAccountLTV()).isEqualTo("0"); + assertThat(accountBalance.getTotalMaintenanceMargin()).isEqualTo("0"); + + assertThat(accountBalance.getCoin()).hasSize(1); + List coins = accountBalance.getCoin(); + + assertThat(coins.get(0).getAvailableToBorrow()).isEqualTo("3"); + assertThat(coins.get(0).getBonus()).isEqualTo("0"); + assertThat(coins.get(0).getAccruedInterest()).isEqualTo("0"); + assertThat(coins.get(0).getAvailableToWithdraw()).isEqualTo("0"); + assertThat(coins.get(0).getTotalOrderIM()).isEqualTo("0"); + assertThat(coins.get(0).getEquity()).isEqualTo("0"); + assertThat(coins.get(0).getTotalPositionMM()).isEqualTo("0"); + assertThat(coins.get(0).getUsdValue()).isEqualTo("0"); + assertThat(coins.get(0).getUnrealisedPnl()).isEqualTo("0"); + assertThat(coins.get(0).isCollateralSwitch()).isTrue(); + assertThat(coins.get(0).getBorrowAmount()).isEqualTo("0.0"); + assertThat(coins.get(0).getTotalPositionIM()).isEqualTo("0"); + assertThat(coins.get(0).getWalletBalance()).isEqualTo("0"); + assertThat(coins.get(0).getFree()).isNull(); + assertThat(coins.get(0).getCumRealisedPnl()).isEqualTo("0"); + assertThat(coins.get(0).getLocked()).isEqualTo("0"); + assertThat(coins.get(0).isMarginCollateral()).isTrue(); + assertThat(coins.get(0).getCoin()).isEqualTo("BTC"); } -} \ No newline at end of file + @Test + public void testGetAllCoinsBalances() throws IOException { + initGetStub("/v5/asset/transfer/query-account-coins-balance", "/getAllCoinsBalance.json5"); + + BybitResult coinsBalanceBybitResult = + bybitAccountServiceRaw.getAllCoinsBalance(BybitAccountType.FUND, null, null, false); + + BybitAllCoinsBalance coinsBalance = coinsBalanceBybitResult.getResult(); + + assertThat(coinsBalance.getMemberId()).isEqualTo("XXXX"); + assertThat(coinsBalance.getAccountType()).isEqualTo(BybitAccountType.FUND); + + assertThat(coinsBalance.getBalance()).hasSize(1); + BybitCoinBalance coinBalance = coinsBalance.getBalance().get(0); + + assertThat(coinBalance.getCoin()).isEqualTo("USDC"); + assertThat(coinBalance.getTransferBalance()).isEqualTo("0"); + assertThat(coinBalance.getWalletBalance()).isEqualTo("0"); + assertThat(coinBalance.getBonus()).isNull(); + } + + @Test + public void testGetFeeRates() throws IOException { + initGetStub("/v5/account/fee-rate", "/getFeeRates.json5"); + + BybitResult bybitFeeRatesBybitResult = + bybitAccountServiceRaw.getFeeRates(BybitCategory.SPOT, "ETHUSDT"); + + BybitFeeRates feeRates = bybitFeeRatesBybitResult.getResult(); + + assertThat(feeRates.getList()).hasSize(1); + BybitFeeRate feeRate = feeRates.getList().get(0); + + assertThat(feeRate.getSymbol()).isEqualTo("ETHUSDT"); + assertThat(feeRate.getTakerFeeRate()).isEqualTo("0.0006"); + assertThat(feeRate.getMakerFeeRate()).isEqualTo("0.0001"); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceTest.java deleted file mode 100644 index edc48a9a5b3..00000000000 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitAccountServiceTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.knowm.xchange.bybit.service; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.math.BigDecimal; -import jakarta.ws.rs.core.Response.Status; -import org.junit.Test; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.currency.Currency; -import org.knowm.xchange.dto.account.AccountInfo; - -public class BybitAccountServiceTest extends BaseWiremockTest { - - @Test - public void testGetWalletBalances() throws IOException { - Exchange bybitExchange = createExchange(); - BybitAccountService bybitAccountService = new BybitAccountService(bybitExchange); - - String walletBalanceDetails = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"balances\":[\n" + - " {\n" + - " \"coin\":\"COIN\",\n" + - " \"coinId\":\"COIN\",\n" + - " \"coinName\":\"COIN\",\n" + - " \"total\":\"66419.616666666666666666\",\n" + - " \"free\":\"56583.326666666666666666\",\n" + - " \"locked\":\"9836.29\"\n" + - " },\n" + - " {\n" + - " \"coin\":\"USDT\",\n" + - " \"coinId\":\"USDT\",\n" + - " \"coinName\":\"USDT\",\n" + - " \"total\":\"61.50059688096\",\n" + - " \"free\":\"61.50059688096\",\n" + - " \"locked\":\"0\"\n" + - " }\n" + - " ]\n" + - " }\n" + - "}"; - - stubFor( - get(urlPathEqualTo("/spot/v1/account")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(walletBalanceDetails) - ) - ); - - AccountInfo accountInfo = bybitAccountService.getAccountInfo(); - assertThat(accountInfo.getWallet().getBalance(new Currency("COIN")).getTotal()).isEqualTo( - new BigDecimal("66419.616666666666666666")); - assertThat(accountInfo.getWallet().getBalance(new Currency("COIN")).getAvailable()).isEqualTo( - new BigDecimal("56583.326666666666666666")); - - assertThat(accountInfo.getWallet().getBalance(new Currency("USDT")).getTotal()).isEqualTo( - new BigDecimal("61.50059688096")); - assertThat(accountInfo.getWallet().getBalance(new Currency("USDT")).getAvailable()).isEqualTo( - new BigDecimal("61.50059688096")); - } - -} \ No newline at end of file diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRawTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRawTest.java index 8f7ff99b95c..24d94188692 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRawTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceRawTest.java @@ -1,62 +1,260 @@ package org.knowm.xchange.bybit.service; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.util.List; -import jakarta.ws.rs.core.Response.Status; -import org.apache.commons.io.IOUtils; +import java.util.Date; +import org.junit.Before; import org.junit.Test; import org.knowm.xchange.Exchange; -import org.knowm.xchange.bybit.dto.marketdata.BybitSymbol; +import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentInfo.InstrumentStatus; +import org.knowm.xchange.bybit.dto.marketdata.instruments.BybitInstrumentsInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.linear.BybitLinearInverseInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.linear.BybitLinearInverseInstrumentInfo.ContractType; +import org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.option.BybitOptionInstrumentInfo.BybitOptionType; +import org.knowm.xchange.bybit.dto.marketdata.instruments.spot.BybitSpotInstrumentInfo; +import org.knowm.xchange.bybit.dto.marketdata.instruments.spot.BybitSpotInstrumentInfo.MarginTrading; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.BybitTickers; +import org.knowm.xchange.bybit.dto.marketdata.tickers.linear.BybitLinearInverseTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.option.BybitOptionTicker; +import org.knowm.xchange.bybit.dto.marketdata.tickers.spot.BybitSpotTicker; public class BybitMarketDataServiceRawTest extends BaseWiremockTest { - @Test - public void testGetSymbols() throws Exception { + private BybitMarketDataServiceRaw marketDataServiceRaw; + + @Before + public void setUp() throws Exception { Exchange bybitExchange = createExchange(); - BybitMarketDataServiceRaw marketDataServiceRaw = (BybitMarketDataServiceRaw) bybitExchange.getMarketDataService(); - - stubFor( - get(urlPathEqualTo("/v2/public/symbols")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(IOUtils.resourceToString("/getSymbols.json5", StandardCharsets.UTF_8)) - ) - ); - - List symbols = marketDataServiceRaw.getSymbols().getResult(); - - assertThat(symbols).hasSize(2); - - BybitSymbol btcusdt = symbols.get(0); - assertThat(btcusdt.getName()).isEqualTo("BTCUSDT"); - assertThat(btcusdt.getAlias()).isEqualTo("BTCUSDT"); - assertThat(btcusdt.getStatus()).isEqualTo("Trading"); - assertThat(btcusdt.getBaseCurrency()).isEqualTo("BTC"); - assertThat(btcusdt.getQuoteCurrency()).isEqualTo("USDT"); - assertThat(btcusdt.getPriceScale()).isEqualTo(2); - assertThat(btcusdt.getTakerFee()).isEqualTo(new BigDecimal("0.0006")); - assertThat(btcusdt.getMakerFee()).isEqualTo(new BigDecimal("0.0001")); - assertThat(btcusdt.getFundingInterval()).isEqualTo(480); - assertThat(btcusdt.getLeverageFilter().getMinLeverage()).isEqualTo(1); - assertThat(btcusdt.getLeverageFilter().getMaxLeverage()).isEqualTo(100); - assertThat(btcusdt.getLeverageFilter().getLeverageStep()).isEqualTo(new BigDecimal("0.01")); - assertThat(btcusdt.getPriceFilter().getMinPrice()).isEqualTo(new BigDecimal("0.5")); - assertThat(btcusdt.getPriceFilter().getMaxPrice()).isEqualTo(new BigDecimal("999999")); - assertThat(btcusdt.getPriceFilter().getTickSize()).isEqualTo(new BigDecimal("0.5")); - assertThat(btcusdt.getLotSizeFilter().getMaxTradingQty()).isEqualTo(new BigDecimal("20")); - assertThat(btcusdt.getLotSizeFilter().getMinTradingQty()).isEqualTo(new BigDecimal("0.001")); - assertThat(btcusdt.getLotSizeFilter().getQtyStep()).isEqualTo(new BigDecimal("0.001")); - assertThat(btcusdt.getLotSizeFilter().getPostOnlyMaxTradingQty()).isEqualTo(new BigDecimal("100")); - - } - -} \ No newline at end of file + marketDataServiceRaw = (BybitMarketDataServiceRaw) bybitExchange.getMarketDataService(); + } + + private void initTickerStub(String responseBody) throws IOException { + initGetStub("/v5/market/tickers", responseBody); + } + + private void initInstrumentsInfoStub(String responseBody) throws IOException { + initGetStub("/v5/market/instruments-info", responseBody); + } + + @Test + public void testGetLinearInverseInstrumentsInfo() throws Exception { + initInstrumentsInfoStub("/getInstrumentLinear.json5"); + + BybitInstrumentsInfo instrumentsInfo = + marketDataServiceRaw.getInstrumentsInfo(BybitCategory.LINEAR, null, null, null, 1000, null).getResult(); + + assertThat(instrumentsInfo.getList()).hasSize(1); + + BybitLinearInverseInstrumentInfo actualInstrumentInfo = + (BybitLinearInverseInstrumentInfo) instrumentsInfo.getList().get(0); + + assertThat(actualInstrumentInfo.getSymbol()).isEqualTo("BTCUSDT"); + assertThat(actualInstrumentInfo.getContractType()).isEqualTo(ContractType.LINEAR_PERPETUAL); + assertThat(actualInstrumentInfo.getStatus()).isEqualTo(InstrumentStatus.TRADING); + assertThat(actualInstrumentInfo.getBaseCoin()).isEqualTo("BTC"); + assertThat(actualInstrumentInfo.getQuoteCoin()).isEqualTo("USDT"); + assertThat(actualInstrumentInfo.getLaunchTime()).isEqualTo(new Date(1585526400000L)); + assertThat(actualInstrumentInfo.getDeliveryTime()).isEqualTo(new Date(0L)); + assertThat(actualInstrumentInfo.getDeliveryFeeRate()).isNull(); + assertThat(actualInstrumentInfo.getPriceScale()).isEqualTo(2); + assertThat(actualInstrumentInfo.getLeverageFilter().getMinLeverage()).isEqualTo(1); + assertThat(actualInstrumentInfo.getLeverageFilter().getMaxLeverage()) + .isEqualTo(new BigDecimal("100.00")); + assertThat(actualInstrumentInfo.getLeverageFilter().getLeverageStep()) + .isEqualTo(new BigDecimal("0.01")); + assertThat(actualInstrumentInfo.getPriceFilter().getTickSize()) + .isEqualTo(new BigDecimal("0.50")); + assertThat(actualInstrumentInfo.getPriceFilter().getMinPrice()) + .isEqualTo(new BigDecimal("0.50")); + assertThat(actualInstrumentInfo.getPriceFilter().getMaxPrice()) + .isEqualTo(new BigDecimal("999999.00")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .isEqualTo(new BigDecimal("100.000")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .isEqualTo(new BigDecimal("0.001")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getQtyStep()) + .isEqualTo(new BigDecimal("0.001")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getPostOnlyMaxOrderQty()) + .isEqualTo(new BigDecimal("1000.000")); + assertThat(actualInstrumentInfo.isUnifiedMarginTrade()).isTrue(); + assertThat(actualInstrumentInfo.getFundingInterval()).isEqualTo(480); + assertThat(actualInstrumentInfo.getSettleCoin()).isEqualTo("USDT"); + assertThat(actualInstrumentInfo.getCopyTrading()).isNull(); + } + + @Test + public void testGetOptionInstrumentsInfo() throws Exception { + initInstrumentsInfoStub("/getInstrumentOption.json5"); + + BybitInstrumentsInfo instrumentsInfo = + marketDataServiceRaw.getInstrumentsInfo(BybitCategory.OPTION, null, null, null, 1000, null).getResult(); + + assertThat(instrumentsInfo.getList()).hasSize(1); + + BybitOptionInstrumentInfo actualInstrumentInfo = + (BybitOptionInstrumentInfo) instrumentsInfo.getList().get(0); + + assertThat(actualInstrumentInfo.getSymbol()).isEqualTo("ETH-3JAN23-1250-P"); + assertThat(actualInstrumentInfo.getOptionsType()).isEqualTo(BybitOptionType.PUT); + assertThat(actualInstrumentInfo.getStatus()).isEqualTo(InstrumentStatus.TRADING); + assertThat(actualInstrumentInfo.getBaseCoin()).isEqualTo("ETH"); + assertThat(actualInstrumentInfo.getQuoteCoin()).isEqualTo("USD"); + assertThat(actualInstrumentInfo.getLaunchTime()).isEqualTo(new Date(1672560000000L)); + assertThat(actualInstrumentInfo.getDeliveryTime()).isEqualTo(new Date(1672732800000L)); + assertThat(actualInstrumentInfo.getDeliveryFeeRate()).isEqualTo(new BigDecimal("0.00015")); + assertThat(actualInstrumentInfo.getPriceFilter().getTickSize()) + .isEqualTo(new BigDecimal("0.1")); + assertThat(actualInstrumentInfo.getPriceFilter().getMinPrice()) + .isEqualTo(new BigDecimal("0.1")); + assertThat(actualInstrumentInfo.getPriceFilter().getMaxPrice()) + .isEqualTo(new BigDecimal("10000000")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .isEqualTo(new BigDecimal("1500")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .isEqualTo(new BigDecimal("0.1")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getQtyStep()) + .isEqualTo(new BigDecimal("0.1")); + assertThat(actualInstrumentInfo.getSettleCoin()).isEqualTo("USDC"); + } + + @Test + public void testGetSpotInstrumentsInfo() throws Exception { + initInstrumentsInfoStub("/getInstrumentSpot.json5"); + + BybitInstrumentsInfo instrumentsInfo = + marketDataServiceRaw.getInstrumentsInfo(BybitCategory.SPOT, null, null, null, 1000, null).getResult(); + + assertThat(instrumentsInfo.getList()).hasSize(1); + + BybitSpotInstrumentInfo actualInstrumentInfo = + (BybitSpotInstrumentInfo) instrumentsInfo.getList().get(0); + + assertThat(actualInstrumentInfo.getSymbol()).isEqualTo("BTCUSDT"); + assertThat(actualInstrumentInfo.getStatus()).isEqualTo(InstrumentStatus.TRADING); + assertThat(actualInstrumentInfo.getMarginTrading()).isEqualTo(MarginTrading.BOTH); + assertThat(actualInstrumentInfo.getBaseCoin()).isEqualTo("BTC"); + assertThat(actualInstrumentInfo.getQuoteCoin()).isEqualTo("USDT"); + assertThat(actualInstrumentInfo.getPriceFilter().getTickSize()) + .isEqualTo(new BigDecimal("0.01")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMaxOrderQty()) + .isEqualTo(new BigDecimal("71.73956243")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMaxOrderAmt()) + .isEqualTo(new BigDecimal("2000000")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMinOrderQty()) + .isEqualTo(new BigDecimal("0.000048")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getMinOrderAmt()) + .isEqualTo(new BigDecimal("1")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getBasePrecision()) + .isEqualTo(new BigDecimal("0.000001")); + assertThat(actualInstrumentInfo.getLotSizeFilter().getQuotePrecision()) + .isEqualTo(new BigDecimal("0.00000001")); + } + + @Test + public void testGetLinearInverseTicker() throws Exception { + initTickerStub("/getTickerInverse.json5"); + + BybitTickers bybitTickers = + marketDataServiceRaw.getTicker24h(BybitCategory.INVERSE, "BTCUSD").getResult(); + + assertThat(bybitTickers.getList()).hasSize(1); + + BybitLinearInverseTicker actualTicker = + (BybitLinearInverseTicker) bybitTickers.getList().get(0); + + assertThat(actualTicker.getSymbol()).isEqualTo("BTCUSD"); + assertThat(actualTicker.getLastPrice()).isEqualTo(new BigDecimal("16597.00")); + assertThat(actualTicker.getIndexPrice()).isEqualTo(new BigDecimal("16598.54")); + assertThat(actualTicker.getMarkPrice()).isEqualTo(new BigDecimal("16596.00")); + assertThat(actualTicker.getPrevPrice24h()).isEqualTo(new BigDecimal("16464.50")); + assertThat(actualTicker.getPrice24hPcnt()).isEqualTo(new BigDecimal("0.008047")); + assertThat(actualTicker.getHighPrice24h()).isEqualTo(new BigDecimal("30912.50")); + assertThat(actualTicker.getLowPrice24h()).isEqualTo(new BigDecimal("15700.00")); + assertThat(actualTicker.getPrevPrice1h()).isEqualTo(new BigDecimal("16595.50")); + assertThat(actualTicker.getOpenInterest()).isEqualTo(new BigDecimal("373504107")); + assertThat(actualTicker.getOpenInterestValue()).isEqualTo(new BigDecimal("22505.67")); + assertThat(actualTicker.getTurnover24h()).isEqualTo(new BigDecimal("2352.94950046")); + assertThat(actualTicker.getVolume24h()).isEqualTo(new BigDecimal("49337318")); + assertThat(actualTicker.getFundingRate()).isEqualTo(new BigDecimal("-0.001034")); + assertThat(actualTicker.getNextFundingTime()).isEqualTo(new Date(1672387200000L)); + assertThat(actualTicker.getPredictedDeliveryPrice()).isNull(); + assertThat(actualTicker.getBasisRate()).isNull(); + assertThat(actualTicker.getDeliveryFeeRate()).isNull(); + assertThat(actualTicker.getDeliveryTime()).isEqualTo(new Date(0L)); + assertThat(actualTicker.getAsk1Size()).isEqualTo(new BigDecimal("1")); + assertThat(actualTicker.getBid1Price()).isEqualTo(new BigDecimal("16596.00")); + assertThat(actualTicker.getAsk1Price()).isEqualTo(new BigDecimal("16597.50")); + assertThat(actualTicker.getBid1Size()).isEqualTo(new BigDecimal("1")); + assertThat(actualTicker.getBasis()).isNull(); + } + + @Test + public void testGetOptionTicker() throws Exception { + initTickerStub("/getTickerOption.json5"); + + BybitTickers bybitTickers = + marketDataServiceRaw.getTicker24h(BybitCategory.OPTION, "BTC-30DEC22-18000-C").getResult(); + + assertThat(bybitTickers.getList()).hasSize(1); + + BybitOptionTicker actualTicker = (BybitOptionTicker) bybitTickers.getList().get(0); + + assertThat(actualTicker.getSymbol()).isEqualTo("BTC-30DEC22-18000-C"); + assertThat(actualTicker.getBid1Price()).isEqualTo(new BigDecimal("0")); + assertThat(actualTicker.getBid1Size()).isEqualTo(new BigDecimal("0")); + assertThat(actualTicker.getBid1Iv()).isEqualTo(new BigDecimal("0")); + assertThat(actualTicker.getAsk1Price()).isEqualTo(new BigDecimal("435")); + assertThat(actualTicker.getAsk1Size()).isEqualTo(new BigDecimal("0.66")); + assertThat(actualTicker.getAsk1Iv()).isEqualTo(new BigDecimal("5")); + assertThat(actualTicker.getLastPrice()).isEqualTo(new BigDecimal("435")); + assertThat(actualTicker.getHighPrice24h()).isEqualTo(new BigDecimal("435")); + assertThat(actualTicker.getLowPrice24h()).isEqualTo(new BigDecimal("165")); + assertThat(actualTicker.getMarkPrice()).isEqualTo(new BigDecimal("0.00000009")); + assertThat(actualTicker.getIndexPrice()).isEqualTo(new BigDecimal("16600.55")); + assertThat(actualTicker.getMarkIv()).isEqualTo(new BigDecimal("0.7567")); + assertThat(actualTicker.getUnderlyingPrice()).isEqualTo(new BigDecimal("16590.42")); + assertThat(actualTicker.getOpenInterest()).isEqualTo(new BigDecimal("6.3")); + assertThat(actualTicker.getTurnover24h()).isEqualTo(new BigDecimal("2482.73")); + assertThat(actualTicker.getVolume24h()).isEqualTo(new BigDecimal("0.15")); + assertThat(actualTicker.getTotalVolume()).isEqualTo(new BigDecimal("99")); + assertThat(actualTicker.getTotalTurnover()).isEqualTo(new BigDecimal("1967653")); + assertThat(actualTicker.getDelta()).isEqualTo(new BigDecimal("0.00000001")); + assertThat(actualTicker.getGamma()).isEqualTo(new BigDecimal("0.00000001")); + assertThat(actualTicker.getVega()).isEqualTo(new BigDecimal("0.00000004")); + assertThat(actualTicker.getTheta()).isEqualTo(new BigDecimal("-0.00000152")); + assertThat(actualTicker.getPredictedDeliveryPrice()).isEqualTo(new BigDecimal("0")); + assertThat(actualTicker.getChange24h()).isEqualTo(new BigDecimal("86")); + } + + @Test + public void testGetSpotTicker() throws Exception { + initTickerStub("/getTickerSpot.json5"); + + BybitTickers bybitTickers = + marketDataServiceRaw.getTicker24h(BybitCategory.SPOT, "BTCUSDT").getResult(); + + assertThat(bybitTickers.getList()).hasSize(1); + + BybitSpotTicker actualTicker = (BybitSpotTicker) bybitTickers.getList().get(0); + + assertThat(actualTicker.getSymbol()).isEqualTo("BTCUSDT"); + assertThat(actualTicker.getBid1Price()).isEqualTo(new BigDecimal("20517.96")); + assertThat(actualTicker.getBid1Size()).isEqualTo(new BigDecimal("2")); + assertThat(actualTicker.getAsk1Price()).isEqualTo(new BigDecimal("20527.77")); + assertThat(actualTicker.getAsk1Size()).isEqualTo(new BigDecimal("1.862172")); + assertThat(actualTicker.getLastPrice()).isEqualTo(new BigDecimal("20533.13")); + assertThat(actualTicker.getPrevPrice24h()).isEqualTo(new BigDecimal("20393.48")); + assertThat(actualTicker.getPrice24hPcnt()).isEqualTo(new BigDecimal("0.0068")); + assertThat(actualTicker.getHighPrice24h()).isEqualTo(new BigDecimal("21128.12")); + assertThat(actualTicker.getLowPrice24h()).isEqualTo(new BigDecimal("20318.89")); + assertThat(actualTicker.getTurnover24h()).isEqualTo(new BigDecimal("243765620.65899866")); + assertThat(actualTicker.getVolume24h()).isEqualTo(new BigDecimal("11801.27771")); + assertThat(actualTicker.getUsdIndexPrice()).isEqualTo(new BigDecimal("20784.12009279")); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceTest.java index 9dbe3d1283a..5abb57bfd72 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitMarketDataServiceTest.java @@ -1,56 +1,68 @@ package org.knowm.xchange.bybit.service; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import jakarta.ws.rs.core.Response.Status; -import org.apache.commons.io.IOUtils; +import java.util.Date; +import org.junit.Before; import org.junit.Test; import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.marketdata.Ticker; import org.knowm.xchange.service.marketdata.MarketDataService; public class BybitMarketDataServiceTest extends BaseWiremockTest { - @Test - public void testGetTicker() throws Exception { + private MarketDataService marketDataService; + + @Before + public void setUp() throws Exception { Exchange bybitExchange = createExchange(); - MarketDataService marketDataService = bybitExchange.getMarketDataService(); - - stubFor( - get(urlPathEqualTo("/v2/public/tickers")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(IOUtils.resourceToString("/getTicker.json5", StandardCharsets.UTF_8)) - ) - ); - - Ticker ticker = marketDataService.getTicker(CurrencyPair.BTC_USDT); - - assertThat(ticker.getInstrument().toString()).isEqualTo("BTC/USDT"); - assertThat(ticker.getOpen()).isEqualTo(new BigDecimal("21670.00")); - assertThat(ticker.getLast()).isEqualTo(new BigDecimal("21333.00")); - assertThat(ticker.getBid()).isEqualTo(new BigDecimal("21323")); - assertThat(ticker.getAsk()).isEqualTo(new BigDecimal("21334")); - assertThat(ticker.getHigh()).isEqualTo(new BigDecimal("22024.50")); - assertThat(ticker.getLow()).isEqualTo(new BigDecimal("21120.00")); - assertThat(ticker.getVwap()).isNull(); - assertThat(ticker.getVolume()).isEqualTo(new BigDecimal("10028.87")); - assertThat(ticker.getQuoteVolume()).isEqualTo(new BigDecimal("216158761.48")); - assertThat(ticker.getTimestamp()).isEqualTo(Instant.parse("2022-07-10T09:09:11.611Z")); - assertThat(ticker.getBidSize()).isNull(); - assertThat(ticker.getAskSize()).isNull(); - assertThat(ticker.getPercentageChange()).isEqualTo(new BigDecimal("-0.015551")); + marketDataService = bybitExchange.getMarketDataService(); + } + + @Test + public void testGetTickerWithInverseArg() throws Exception { + initGetStub("/v5/market/tickers", "/getTickerInverse.json5"); + Ticker ticker = marketDataService.getTicker(CurrencyPair.BTC_USD, BybitCategory.INVERSE); + + assertThat(ticker.getInstrument().toString()).isEqualTo("BTC/USD"); + assertThat(ticker.getOpen()).isEqualTo(new BigDecimal("16464.50")); + assertThat(ticker.getLast()).isEqualTo(new BigDecimal("16597.00")); + assertThat(ticker.getBid()).isEqualTo(new BigDecimal("16596.00")); + assertThat(ticker.getAsk()).isEqualTo(new BigDecimal("16597.50")); + assertThat(ticker.getHigh()).isEqualTo(new BigDecimal("30912.50")); + assertThat(ticker.getLow()).isEqualTo(new BigDecimal("15700.00")); + assertThat(ticker.getVwap()).isNull(); + assertThat(ticker.getVolume()).isEqualTo(new BigDecimal("49337318")); + assertThat(ticker.getQuoteVolume()).isEqualTo(new BigDecimal("2352.94950046")); + assertThat(ticker.getTimestamp()).isEqualTo(new Date(1672376496682L)); + assertThat(ticker.getBidSize()).isEqualTo(new BigDecimal("1")); + assertThat(ticker.getAskSize()).isEqualTo(new BigDecimal("1")); + assertThat(ticker.getPercentageChange()).isEqualTo(new BigDecimal("0.008047")); } -} \ No newline at end of file + @Test + public void testGetTickerWithSpotArg() throws Exception { + initGetStub("/v5/market/tickers", "/getTickerSpot.json5"); + + Ticker ticker = marketDataService.getTicker(CurrencyPair.BTC_USD, BybitCategory.SPOT); + + assertThat(ticker.getInstrument().toString()).isEqualTo("BTC/USD"); + assertThat(ticker.getOpen()).isEqualTo(new BigDecimal("20393.48")); + assertThat(ticker.getLast()).isEqualTo(new BigDecimal("20533.13")); + assertThat(ticker.getBid()).isEqualTo(new BigDecimal("20517.96")); + assertThat(ticker.getAsk()).isEqualTo(new BigDecimal("20527.77")); + assertThat(ticker.getHigh()).isEqualTo(new BigDecimal("21128.12")); + assertThat(ticker.getLow()).isEqualTo(new BigDecimal("20318.89")); + assertThat(ticker.getVwap()).isNull(); // If it's supposed to be null + assertThat(ticker.getVolume()).isEqualTo(new BigDecimal("11801.27771")); + assertThat(ticker.getQuoteVolume()).isEqualTo(new BigDecimal("243765620.65899866")); + assertThat(ticker.getTimestamp()).isEqualTo(new Date(1673859087947L)); + assertThat(ticker.getBidSize()).isEqualTo(new BigDecimal("2")); + assertThat(ticker.getAskSize()).isEqualTo(new BigDecimal("1.862172")); + assertThat(ticker.getPercentageChange()).isEqualTo(new BigDecimal("0.0068")); + } +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceRawTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceRawTest.java index 7cf4a5f6d58..a148538c227 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceRawTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceRawTest.java @@ -1,7 +1,6 @@ package org.knowm.xchange.bybit.service; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -9,165 +8,265 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; import jakarta.ws.rs.core.Response.Status; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; import org.junit.Test; import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; -import org.knowm.xchange.bybit.dto.trade.BybitOrderDetails; -import org.knowm.xchange.bybit.dto.trade.BybitOrderRequest; +import org.knowm.xchange.bybit.dto.trade.BybitOrderResponse; +import org.knowm.xchange.bybit.dto.trade.BybitOrderStatus; +import org.knowm.xchange.bybit.dto.trade.BybitOrderType; +import org.knowm.xchange.bybit.dto.trade.BybitSide; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.BybitOrderDetails; +import org.knowm.xchange.bybit.dto.trade.details.linear.BybitLinearOrderDetail; +import org.knowm.xchange.bybit.dto.trade.details.spot.BybitSpotOrderDetail; public class BybitTradeServiceRawTest extends BaseWiremockTest { @Test - public void testGetBybitOrder() throws IOException { + public void testGetBybitLinearDetailOrder() throws IOException { Exchange bybitExchange = createExchange(); BybitTradeServiceRaw bybitAccountServiceRaw = new BybitTradeServiceRaw(bybitExchange); - String orderDetails = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"accountId\":\"123456789\",\n" + - " \"exchangeId\":\"301\",\n" + - " \"symbol\":\"COINUSDT\",\n" + - " \"symbolName\":\"COINUSDT\",\n" + - " \"orderLinkId\":\"1234567891011121\",\n" + - " \"orderId\":\"1234567891011121314\",\n" + - " \"price\":\"0\",\n" + - " \"origQty\":\"352\",\n" + - " \"executedQty\":\"352\",\n" + - " \"cummulativeQuoteQty\":\"0.569888\",\n" + - " \"avgPrice\":\"0.001619\",\n" + - " \"status\":\"FILLED\",\n" + - " \"timeInForce\":\"GTC\",\n" + - " \"type\":\"MARKET\",\n" + - " \"side\":\"SELL\",\n" + - " \"stopPrice\":\"0.0\",\n" + - " \"icebergQty\":\"0.0\",\n" + - " \"time\":\"1655997749601\",\n" + - " \"updateTime\":\"1655997749662\",\n" + - " \"isWorking\":true,\n" + - " \"locked\":\"0\"\n" + - " }\n" + - "}"; + String responseFilePath = "/getOrderDetailsLinear.json5"; + initGetStub("/v5/order/realtime", responseFilePath); + String expectedOrderDetails = + IOUtils.resourceToString(responseFilePath, StandardCharsets.UTF_8); - stubFor( - get(urlPathEqualTo("/spot/v1/order")) - .willReturn( - aResponse() - .withStatus(Status.OK.getStatusCode()) - .withHeader("Content-Type", "application/json") - .withBody(orderDetails) - ) - ); - BybitResult order = bybitAccountServiceRaw.getBybitOrder("1234567891011121314"); + BybitResult> actualOrderDetails = + bybitAccountServiceRaw.getBybitOrder( + BybitCategory.LINEAR, "fd4300ae-7847-404e-b947-b46980a4d140"); + + assertThat(actualOrderDetails.getResult().getList()).hasSize(1); ObjectMapper mapper = new ObjectMapper(); - JsonNode responseObject = mapper.readTree(orderDetails); + JsonNode responseObject = mapper.readTree(expectedOrderDetails); - BybitOrderDetails orderResult = order.getResult(); + BybitLinearOrderDetail actualOrderDetail = + (BybitLinearOrderDetail) actualOrderDetails.getResult().getList().get(0); JsonNode responseObjectResult = responseObject.get("result"); + JsonNode listNode = responseObjectResult.get("list"); + JsonNode expectedOrderDetail = listNode.get(0); - assertThat(responseObjectResult.get("accountId").textValue()).isEqualTo(orderResult.getAccountId()); - assertThat(responseObjectResult.get("exchangeId").textValue()).isEqualTo(orderResult.getExchangeId()); - assertThat(responseObjectResult.get("symbol").textValue()).isEqualTo(orderResult.getSymbol()); - assertThat(responseObjectResult.get("symbolName").textValue()).isEqualTo(orderResult.getSymbolName()); - assertThat(responseObjectResult.get("orderLinkId").textValue()).isEqualTo(orderResult.getOrderLinkId()); - assertThat(responseObjectResult.get("orderId").textValue()).isEqualTo(orderResult.getOrderId()); - assertThat(responseObjectResult.get("price").textValue()).isEqualTo(orderResult.getPrice()); - assertThat(responseObjectResult.get("origQty").textValue()).isEqualTo(orderResult.getOrigQty()); - assertThat(responseObjectResult.get("executedQty").textValue()).isEqualTo(orderResult.getExecutedQty()); - assertThat(responseObjectResult.get("cummulativeQuoteQty").textValue()).isEqualTo( - orderResult.getCummulativeQuoteQty()); - assertThat(responseObjectResult.get("avgPrice").textValue()).isEqualTo(orderResult.getAvgPrice()); - assertThat(responseObjectResult.get("status").textValue()).isEqualTo(orderResult.getStatus()); - assertThat(responseObjectResult.get("timeInForce").textValue()).isEqualTo(orderResult.getTimeInForce()); - assertThat(responseObjectResult.get("type").textValue()).isEqualTo(orderResult.getType()); - assertThat(responseObjectResult.get("side").textValue()).isEqualTo(orderResult.getSide()); - assertThat(responseObjectResult.get("stopPrice").textValue()).isEqualTo(orderResult.getStopPrice()); - assertThat(responseObjectResult.get("icebergQty").textValue()).isEqualTo(orderResult.getIcebergQty()); - assertThat(responseObjectResult.get("time").textValue()).isEqualTo(orderResult.getTime()); - assertThat(responseObjectResult.get("updateTime").textValue()).isEqualTo(orderResult.getUpdateTime()); - assertThat(responseObjectResult.get("isWorking").booleanValue()).isEqualTo(orderResult.isWorking()); - assertThat(responseObjectResult.get("locked").textValue()).isEqualTo(orderResult.getLocked()); + assertThat(actualOrderDetail.getSymbol()) + .isEqualTo(expectedOrderDetail.get("symbol").textValue()); + assertThat(actualOrderDetail.getPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("price").asDouble()); + assertThat(actualOrderDetail.getQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("qty").asDouble()); + assertThat(actualOrderDetail.getSide().name()) + .isEqualToIgnoringCase(expectedOrderDetail.get("side").textValue()); + assertThat(actualOrderDetail.getIsLeverage()) + .isEqualTo(expectedOrderDetail.get("isLeverage").textValue()); + assertThat(actualOrderDetail.getPositionIdx()) + .isEqualTo(expectedOrderDetail.get("positionIdx").intValue()); + assertThat(actualOrderDetail.getOrderStatus().name()) + .isEqualToIgnoringCase(expectedOrderDetail.get("orderStatus").textValue()); + assertThat(actualOrderDetail.getCancelType()) + .isEqualTo(expectedOrderDetail.get("cancelType").textValue()); + assertThat(actualOrderDetail.getRejectReason()) + .isEqualTo(expectedOrderDetail.get("rejectReason").textValue()); + assertThat(actualOrderDetail.getAvgPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("avgPrice").asDouble()); + assertThat(actualOrderDetail.getLeavesQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("leavesQty").asDouble()); + assertThat(actualOrderDetail.getLeavesValue().doubleValue()) + .isEqualTo(expectedOrderDetail.get("leavesValue").asDouble()); + assertThat(actualOrderDetail.getCumExecQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecQty").asDouble()); + assertThat(actualOrderDetail.getCumExecValue().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecValue").asDouble()); + assertThat(actualOrderDetail.getCumExecFee().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecFee").asDouble()); + assertThat(actualOrderDetail.getTimeInForce()) + .isEqualTo(expectedOrderDetail.get("timeInForce").textValue()); + assertThat(actualOrderDetail.getOrderType().name()) + .isEqualToIgnoringCase(expectedOrderDetail.get("orderType").textValue()); + assertThat(actualOrderDetail.getStopOrderType()) + .isEqualTo(expectedOrderDetail.get("stopOrderType").textValue()); + assertThat(actualOrderDetail.getOrderIv()) + .isEqualTo(expectedOrderDetail.get("orderIv").textValue()); + assertThat(actualOrderDetail.getTriggerPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("triggerPrice").asDouble()); + assertThat(actualOrderDetail.getTakeProfit().doubleValue()) + .isEqualTo(expectedOrderDetail.get("takeProfit").asDouble()); + assertThat(actualOrderDetail.getStopLoss().doubleValue()) + .isEqualTo(expectedOrderDetail.get("stopLoss").asDouble()); + assertThat(actualOrderDetail.getTpTriggerBy()) + .isEqualTo(expectedOrderDetail.get("tpTriggerBy").textValue()); + assertThat(actualOrderDetail.getSlTriggerBy()) + .isEqualTo(expectedOrderDetail.get("slTriggerBy").textValue()); + assertThat(actualOrderDetail.getTriggerDirection()) + .isEqualTo(expectedOrderDetail.get("triggerDirection").intValue()); + assertThat(actualOrderDetail.getTriggerBy()) + .isEqualTo(expectedOrderDetail.get("triggerBy").textValue()); + assertThat(actualOrderDetail.getLastPriceOnCreated()) + .isEqualTo(expectedOrderDetail.get("lastPriceOnCreated").textValue()); + assertThat(actualOrderDetail.isReduceOnly()) + .isEqualTo(expectedOrderDetail.get("reduceOnly").booleanValue()); + assertThat(actualOrderDetail.isCloseOnTrigger()) + .isEqualTo(expectedOrderDetail.get("closeOnTrigger").booleanValue()); + assertThat(actualOrderDetail.getSmpType()) + .isEqualTo(expectedOrderDetail.get("smpType").textValue()); + assertThat(actualOrderDetail.getSmpGroup()) + .isEqualTo(expectedOrderDetail.get("smpGroup").intValue()); + assertThat(actualOrderDetail.getSmpOrderId()) + .isEqualTo(expectedOrderDetail.get("smpOrderId").textValue()); + assertThat(actualOrderDetail.getTpslMode()) + .isEqualTo(expectedOrderDetail.get("tpslMode").textValue()); + assertThat(actualOrderDetail.getTpLimitPrice()) + .isEqualTo(expectedOrderDetail.get("tpLimitPrice").textValue()); + assertThat(actualOrderDetail.getSlLimitPrice()) + .isEqualTo(expectedOrderDetail.get("slLimitPrice").textValue()); + assertThat(actualOrderDetail.getPlaceType()) + .isEqualTo(expectedOrderDetail.get("placeType").textValue()); + assertThat(actualOrderDetail.getCreatedTime().getTime()) + .isEqualTo(expectedOrderDetail.get("createdTime").asLong()); + assertThat(actualOrderDetail.getUpdatedTime().getTime()) + .isEqualTo(expectedOrderDetail.get("updatedTime").asLong()); } + @Test + public void testGetBybitSpotDetailOrder() throws IOException { + Exchange bybitExchange = createExchange(); + BybitTradeServiceRaw bybitAccountServiceRaw = new BybitTradeServiceRaw(bybitExchange); + + String responseFilePath = "/getOrderDetailsSpot.json5"; + initGetStub("/v5/order/realtime", responseFilePath); + String expectedOrderDetails = + IOUtils.resourceToString(responseFilePath, StandardCharsets.UTF_8); + + BybitResult> actualOrderDetails = + bybitAccountServiceRaw.getBybitOrder( + BybitCategory.SPOT, "fd4300ae-7847-404e-b947-b46980a4d140"); + + assertThat(actualOrderDetails.getResult().getList()).hasSize(1); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode responseObject = mapper.readTree(expectedOrderDetails); + + BybitSpotOrderDetail actualOrderDetail = + (BybitSpotOrderDetail) actualOrderDetails.getResult().getList().get(0); + JsonNode responseObjectResult = responseObject.get("result"); + JsonNode listNode = responseObjectResult.get("list"); + JsonNode expectedOrderDetail = listNode.get(0); + + assertThat(actualOrderDetail.getSymbol()) + .isEqualTo(expectedOrderDetail.get("symbol").textValue()); + assertThat(actualOrderDetail.getPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("price").asDouble()); + assertThat(actualOrderDetail.getQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("qty").asDouble()); + assertThat(actualOrderDetail.getSide().name()) + .isEqualToIgnoringCase(expectedOrderDetail.get("side").textValue()); + assertThat(actualOrderDetail.getIsLeverage()) + .isEqualTo(expectedOrderDetail.get("isLeverage").textValue()); + assertThat(actualOrderDetail.getPositionIdx()) + .isEqualTo(expectedOrderDetail.get("positionIdx").intValue()); + assertThat(actualOrderDetail.getOrderStatus()) + .isEqualTo(BybitOrderStatus.PARTIALLY_FILLED_CANCELED); + assertThat(actualOrderDetail.getCancelType()) + .isEqualTo(expectedOrderDetail.get("cancelType").textValue()); + assertThat(actualOrderDetail.getRejectReason()) + .isEqualTo(expectedOrderDetail.get("rejectReason").textValue()); + assertThat(actualOrderDetail.getAvgPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("avgPrice").asDouble()); + assertThat(actualOrderDetail.getLeavesQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("leavesQty").asDouble()); + assertThat(actualOrderDetail.getLeavesValue().doubleValue()) + .isEqualTo(expectedOrderDetail.get("leavesValue").asDouble()); + assertThat(actualOrderDetail.getCumExecQty().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecQty").asDouble()); + assertThat(actualOrderDetail.getCumExecValue().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecValue").asDouble()); + assertThat(actualOrderDetail.getCumExecFee().doubleValue()) + .isEqualTo(expectedOrderDetail.get("cumExecFee").asDouble()); + assertThat(actualOrderDetail.getTimeInForce()) + .isEqualTo(expectedOrderDetail.get("timeInForce").textValue()); + assertThat(actualOrderDetail.getOrderType().name()) + .isEqualToIgnoringCase(expectedOrderDetail.get("orderType").textValue()); + assertThat(actualOrderDetail.getStopOrderType()) + .isEqualTo(expectedOrderDetail.get("stopOrderType").textValue()); + assertThat(actualOrderDetail.getOrderIv()) + .isEqualTo(expectedOrderDetail.get("orderIv").textValue()); + assertThat(actualOrderDetail.getTriggerPrice().doubleValue()) + .isEqualTo(expectedOrderDetail.get("triggerPrice").asDouble()); + assertThat(actualOrderDetail.getTpTriggerBy()) + .isEqualTo(expectedOrderDetail.get("tpTriggerBy").textValue()); + assertThat(actualOrderDetail.getSlTriggerBy()) + .isEqualTo(expectedOrderDetail.get("slTriggerBy").textValue()); + assertThat(actualOrderDetail.getTriggerDirection()) + .isEqualTo(expectedOrderDetail.get("triggerDirection").intValue()); + assertThat(actualOrderDetail.getTriggerBy()) + .isEqualTo(expectedOrderDetail.get("triggerBy").textValue()); + assertThat(actualOrderDetail.getLastPriceOnCreated()).isNull(); + assertThat(actualOrderDetail.isReduceOnly()) + .isEqualTo(expectedOrderDetail.get("reduceOnly").booleanValue()); + assertThat(actualOrderDetail.isCloseOnTrigger()) + .isEqualTo(expectedOrderDetail.get("closeOnTrigger").booleanValue()); + assertThat(actualOrderDetail.getSmpType()) + .isEqualTo(expectedOrderDetail.get("smpType").textValue()); + assertThat(actualOrderDetail.getSmpGroup()) + .isEqualTo(expectedOrderDetail.get("smpGroup").intValue()); + assertThat(actualOrderDetail.getSmpOrderId()) + .isEqualTo(expectedOrderDetail.get("smpOrderId").textValue()); + assertThat(actualOrderDetail.getPlaceType()) + .isEqualTo(expectedOrderDetail.get("placeType").textValue()); + assertThat(actualOrderDetail.getCreatedTime().getTime()) + .isEqualTo(expectedOrderDetail.get("createdTime").asLong()); + assertThat(actualOrderDetail.getUpdatedTime().getTime()) + .isEqualTo(expectedOrderDetail.get("updatedTime").asLong()); + } @Test public void testPlaceBybitOrder() throws IOException { Exchange bybitExchange = createExchange(); BybitTradeServiceRaw bybitAccountServiceRaw = new BybitTradeServiceRaw(bybitExchange); - String orderPlacementResponse = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"accountId\":\"28649557\",\n" + - " \"exchangeId\":\"301\",\n" + - " \"symbol\":\"COINUSDT\",\n" + - " \"symbolName\":\"COINUSDT\",\n" + - " \"orderLinkId\":\"1655997749596563\",\n" + - " \"orderId\":\"1184989442799045889\",\n" + - " \"price\":\"0\",\n" + - " \"origQty\":\"352\",\n" + - " \"executedQty\":\"352\",\n" + - " \"cummulativeQuoteQty\":\"0.569888\",\n" + - " \"avgPrice\":\"0.001619\",\n" + - " \"status\":\"FILLED\",\n" + - " \"timeInForce\":\"GTC\",\n" + - " \"type\":\"MARKET\",\n" + - " \"side\":\"SELL\",\n" + - " \"stopPrice\":\"0.0\",\n" + - " \"icebergQty\":\"0.0\",\n" + - " \"time\":\"1655997749601\",\n" + - " \"updateTime\":\"1655997749662\",\n" + - " \"isWorking\":true,\n" + - " \"locked\":\"0\"\n" + - " }\n" + - "}"; + String orderPlacementResponse = + "{\n" + + " \"retCode\": 0,\n" + + " \"retMsg\": \"OK\",\n" + + " \"result\": {\n" + + " \"orderId\": \"1321003749386327552\",\n" + + " \"orderLinkId\": \"spot-test-postonly\"\n" + + " },\n" + + " \"retExtInfo\": {},\n" + + " \"time\": 1672211918471\n" + + "}"; stubFor( - post(urlPathEqualTo("/spot/v1/order")) + post(urlPathEqualTo("/v5/order/create")) .willReturn( aResponse() .withStatus(Status.OK.getStatusCode()) .withHeader("Content-Type", "application/json") - .withBody(orderPlacementResponse) - ) - ); + .withBody(orderPlacementResponse))); - BybitResult order = bybitAccountServiceRaw.placeOrder( - "COINUSDT", - 300, - "SELL", - "MARKET" - ); + BybitResult order = + bybitAccountServiceRaw.placeOrder( + BybitCategory.SPOT, + "BTCUSDT", + BybitSide.BUY, + BybitOrderType.LIMIT, + BigDecimal.valueOf(0.1)); ObjectMapper mapper = new ObjectMapper(); JsonNode responseObject = mapper.readTree(orderPlacementResponse); - BybitOrderRequest orderRequestResult = order.getResult(); + BybitOrderResponse orderRequestResult = order.getResult(); JsonNode responseObjectResult = responseObject.get("result"); - assertThat(responseObjectResult.get("accountId").textValue()).isEqualTo(orderRequestResult.getAccountId()); - assertThat(responseObjectResult.get("symbol").textValue()).isEqualTo(orderRequestResult.getSymbol()); - assertThat(responseObjectResult.get("symbolName").textValue()).isEqualTo(orderRequestResult.getSymbolName()); - assertThat(responseObjectResult.get("orderLinkId").textValue()).isEqualTo(orderRequestResult.getOrderLinkId()); - assertThat(responseObjectResult.get("orderId").textValue()).isEqualTo(orderRequestResult.getOrderId()); - assertThat(responseObjectResult.get("price").textValue()).isEqualTo(orderRequestResult.getPrice()); - assertThat(responseObjectResult.get("origQty").textValue()).isEqualTo(orderRequestResult.getOrigQty()); - assertThat(responseObjectResult.get("executedQty").textValue()).isEqualTo(orderRequestResult.getExecutedQty()); - assertThat(responseObjectResult.get("status").textValue()).isEqualTo(orderRequestResult.getStatus()); - assertThat(responseObjectResult.get("timeInForce").textValue()).isEqualTo(orderRequestResult.getTimeInForce()); - assertThat(responseObjectResult.get("type").textValue()).isEqualTo(orderRequestResult.getType()); - assertThat(responseObjectResult.get("side").textValue()).isEqualTo(orderRequestResult.getSide()); + assertThat(responseObjectResult.get("orderLinkId").textValue()) + .isEqualTo(orderRequestResult.getOrderLinkId()); + assertThat(responseObjectResult.get("orderId").textValue()) + .isEqualTo(orderRequestResult.getOrderId()); System.out.println(order); } - -} \ No newline at end of file +} diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceTest.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceTest.java index 72255344a0c..8c036deb505 100644 --- a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceTest.java +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/service/BybitTradeServiceTest.java @@ -7,14 +7,16 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; +import jakarta.ws.rs.core.Response.Status; import java.io.IOException; import java.math.BigDecimal; import java.util.Collection; -import jakarta.ws.rs.core.Response.Status; import org.junit.Test; import org.knowm.xchange.Exchange; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.trade.MarketOrder; public class BybitTradeServiceTest extends BaseWiremockTest { @@ -24,110 +26,111 @@ public void testGetBybitOrder() throws IOException { Exchange bybitExchange = createExchange(); BybitTradeService bybitAccountService = new BybitTradeService(bybitExchange); - String orderDetails = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"accountId\":\"123456789\",\n" + - " \"exchangeId\":\"301\",\n" + - " \"symbol\":\"COINUSDT\",\n" + - " \"symbolName\":\"COINUSDT\",\n" + - " \"orderLinkId\":\"1234567891011121\",\n" + - " \"orderId\":\"1234567891011121314\",\n" + - " \"price\":\"0\",\n" + - " \"origQty\":\"352\",\n" + - " \"executedQty\":\"352\",\n" + - " \"cummulativeQuoteQty\":\"0.569888\",\n" + - " \"avgPrice\":\"0.001619\",\n" + - " \"status\":\"FILLED\",\n" + - " \"timeInForce\":\"GTC\",\n" + - " \"type\":\"MARKET\",\n" + - " \"side\":\"SELL\",\n" + - " \"stopPrice\":\"0.0\",\n" + - " \"icebergQty\":\"0.0\",\n" + - " \"time\":\"1655997749601\",\n" + - " \"updateTime\":\"1655997749662\",\n" + - " \"isWorking\":true,\n" + - " \"locked\":\"0\"\n" + - " }\n" + - "}"; + String orderDetails = + "{\n" + + " \"retCode\": 0,\n" + + " \"retMsg\": \"OK\",\n" + + " \"result\": {\n" + + " \"list\": [\n" + + " {\n" + + " \"orderId\": \"fd4300ae-7847-404e-b947-b46980a4d140\",\n" + + " \"orderLinkId\": \"test-000005\",\n" + + " \"blockTradeId\": \"\",\n" + + " \"symbol\": \"ETHUSDT\",\n" + + " \"price\": \"1600.00\",\n" + + " \"qty\": \"0.10\",\n" + + " \"side\": \"Buy\",\n" + + " \"isLeverage\": \"\",\n" + + " \"positionIdx\": 1,\n" + + " \"orderStatus\": \"New\",\n" + + " \"cancelType\": \"UNKNOWN\",\n" + + " \"rejectReason\": \"EC_NoError\",\n" + + " \"avgPrice\": \"0\",\n" + + " \"leavesQty\": \"0.10\",\n" + + " \"leavesValue\": \"160\",\n" + + " \"cumExecQty\": \"0.00\",\n" + + " \"cumExecValue\": \"0\",\n" + + " \"cumExecFee\": \"0\",\n" + + " \"timeInForce\": \"GTC\",\n" + + " \"orderType\": \"Limit\",\n" + + " \"stopOrderType\": \"UNKNOWN\",\n" + + " \"orderIv\": \"\",\n" + + " \"triggerPrice\": \"0.00\",\n" + + " \"takeProfit\": \"2500.00\",\n" + + " \"stopLoss\": \"1500.00\",\n" + + " \"tpTriggerBy\": \"LastPrice\",\n" + + " \"slTriggerBy\": \"LastPrice\",\n" + + " \"triggerDirection\": 0,\n" + + " \"triggerBy\": \"UNKNOWN\",\n" + + " \"lastPriceOnCreated\": \"\",\n" + + " \"reduceOnly\": false,\n" + + " \"closeOnTrigger\": false,\n" + + " \"smpType\": \"None\",\n" + + " \"smpGroup\": 0,\n" + + " \"smpOrderId\": \"\",\n" + + " \"tpslMode\": \"Full\",\n" + + " \"tpLimitPrice\": \"\",\n" + + " \"slLimitPrice\": \"\",\n" + + " \"placeType\": \"\",\n" + + " \"createdTime\": \"1684738540559\",\n" + + " \"updatedTime\": \"1684738540561\"\n" + + " }\n" + + " ],\n" + + " \"nextPageCursor\": \"page_args%3Dfd4300ae-7847-404e-b947-b46980a4d140%26symbol%3D6%26\",\n" + + " \"category\": \"linear\"\n" + + " },\n" + + " \"retExtInfo\": {},\n" + + " \"time\": 1684765770483\n" + + "}"; stubFor( - get(urlPathEqualTo("/spot/v1/order")) + get(urlPathEqualTo("/v5/order/realtime")) .willReturn( aResponse() .withStatus(Status.OK.getStatusCode()) .withHeader("Content-Type", "application/json") - .withBody(orderDetails) - ) - ); + .withBody(orderDetails))); - Collection orders = bybitAccountService.getOrder("1234567891011121314"); + Collection orders = bybitAccountService.getOrder("fd4300ae-7847-404e-b947-b46980a4d140"); assertThat(orders.size()).isEqualTo(1); Order order = (Order) orders.toArray()[0]; - assertThat(order.getType()).isEqualTo(Order.OrderType.ASK); - assertThat(order.getInstrument()).isEqualTo(new CurrencyPair("COIN", "USDT")); - assertThat(order.getAveragePrice()).isEqualTo(new BigDecimal("0.001619")); - assertThat(order.getStatus()).isEqualTo(Order.OrderStatus.FILLED); - assertThat(order.getOriginalAmount()).isEqualTo(new BigDecimal("352")); - + assertThat(order.getType()).isEqualTo(OrderType.BID); + assertThat(order.getInstrument()).isEqualTo(new CurrencyPair("ETH", "USDT")); + assertThat(order.getAveragePrice()).isEqualTo(new BigDecimal("0")); + assertThat(order.getStatus()).isEqualTo(OrderStatus.NEW); + assertThat(order.getOriginalAmount()).isEqualTo(new BigDecimal("0.10")); } - @Test public void testPlaceBybitOrder() throws IOException { Exchange bybitExchange = createExchange(); BybitTradeService bybitAccountService = new BybitTradeService(bybitExchange); - String orderPlacementResponse = "{\n" + - " \"ret_code\":0,\n" + - " \"ret_msg\":\"\",\n" + - " \"ext_code\":null,\n" + - " \"ext_info\":null,\n" + - " \"result\":{\n" + - " \"accountId\":\"28649557\",\n" + - " \"exchangeId\":\"301\",\n" + - " \"symbol\":\"COINUSDT\",\n" + - " \"symbolName\":\"COINUSDT\",\n" + - " \"orderLinkId\":\"1655997749596563\",\n" + - " \"orderId\":\"1184989442799045889\",\n" + - " \"price\":\"0\",\n" + - " \"origQty\":\"352\",\n" + - " \"executedQty\":\"352\",\n" + - " \"cummulativeQuoteQty\":\"0.569888\",\n" + - " \"avgPrice\":\"0.001619\",\n" + - " \"status\":\"FILLED\",\n" + - " \"timeInForce\":\"GTC\",\n" + - " \"type\":\"MARKET\",\n" + - " \"side\":\"SELL\",\n" + - " \"stopPrice\":\"0.0\",\n" + - " \"icebergQty\":\"0.0\",\n" + - " \"time\":\"1655997749601\",\n" + - " \"updateTime\":\"1655997749662\",\n" + - " \"isWorking\":true,\n" + - " \"locked\":\"0\"\n" + - " }\n" + - "}"; + String orderPlacementResponse = + "{\n" + + " \"retCode\": 0,\n" + + " \"retMsg\": \"OK\",\n" + + " \"result\": {\n" + + " \"orderId\": \"1321003749386327552\",\n" + + " \"orderLinkId\": \"spot-test-postonly\"\n" + + " },\n" + + " \"retExtInfo\": {},\n" + + " \"time\": 1672211918471\n" + + "}"; stubFor( - post(urlPathEqualTo("/spot/v1/order")) + post(urlPathEqualTo("/v5/order/create")) .willReturn( aResponse() .withStatus(Status.OK.getStatusCode()) .withHeader("Content-Type", "application/json") - .withBody(orderPlacementResponse) - ) - ); - - String orderId = bybitAccountService.placeMarketOrder( - new MarketOrder(Order.OrderType.ASK, new BigDecimal("300"), new CurrencyPair("COIN", "USDT")) - ); + .withBody(orderPlacementResponse))); - assertThat(orderId).isEqualTo("1184989442799045889"); + String orderId = + bybitAccountService.placeMarketOrder( + new MarketOrder(OrderType.ASK, new BigDecimal("0.1"), new CurrencyPair("BTC", "USDT"))); + assertThat(orderId).isEqualTo("1321003749386327552"); } - -} \ No newline at end of file +} diff --git a/xchange-bybit/src/test/resources/getAllCoinsBalance.json5 b/xchange-bybit/src/test/resources/getAllCoinsBalance.json5 new file mode 100644 index 00000000000..2733ac94e8d --- /dev/null +++ b/xchange-bybit/src/test/resources/getAllCoinsBalance.json5 @@ -0,0 +1,18 @@ +{ + "retCode": 0, + "retMsg": "success", + "result": { + "memberId": "XXXX", + "accountType": "FUND", + "balance": [ + { + "coin": "USDC", + "transferBalance": "0", + "walletBalance": "0", + "bonus": "" + } + ] + }, + "retExtInfo": {}, + "time": 1675866354913 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getFeeRates.json5 b/xchange-bybit/src/test/resources/getFeeRates.json5 new file mode 100644 index 00000000000..3f400105c5e --- /dev/null +++ b/xchange-bybit/src/test/resources/getFeeRates.json5 @@ -0,0 +1,15 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "list": [ + { + "symbol": "ETHUSDT", + "takerFeeRate": "0.0006", + "makerFeeRate": "0.0001" + } + ] + }, + "retExtInfo": {}, + "time": 1676360412576 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getInstrumentLinear.json5 b/xchange-bybit/src/test/resources/getInstrumentLinear.json5 new file mode 100644 index 00000000000..047fc7c8745 --- /dev/null +++ b/xchange-bybit/src/test/resources/getInstrumentLinear.json5 @@ -0,0 +1,42 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "linear", + "list": [ + { + "symbol": "BTCUSDT", + "contractType": "LinearPerpetual", + "status": "Trading", + "baseCoin": "BTC", + "quoteCoin": "USDT", + "launchTime": "1585526400000", + "deliveryTime": "0", + "deliveryFeeRate": "", + "priceScale": "2", + "leverageFilter": { + "minLeverage": "1", + "maxLeverage": "100.00", + "leverageStep": "0.01" + }, + "priceFilter": { + "minPrice": "0.50", + "maxPrice": "999999.00", + "tickSize": "0.50" + }, + "lotSizeFilter": { + "maxOrderQty": "100.000", + "minOrderQty": "0.001", + "qtyStep": "0.001", + "postOnlyMaxOrderQty": "1000.000" + }, + "unifiedMarginTrade": true, + "fundingInterval": 480, + "settleCoin": "USDT" + } + ], + "nextPageCursor": "" + }, + "retExtInfo": {}, + "time": 1672712495660 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getInstrumentOption.json5 b/xchange-bybit/src/test/resources/getInstrumentOption.json5 new file mode 100644 index 00000000000..c5be1d3bd16 --- /dev/null +++ b/xchange-bybit/src/test/resources/getInstrumentOption.json5 @@ -0,0 +1,33 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "option", + "nextPageCursor": "", + "list": [ + { + "symbol": "ETH-3JAN23-1250-P", + "status": "Trading", + "baseCoin": "ETH", + "quoteCoin": "USD", + "settleCoin": "USDC", + "optionsType": "Put", + "launchTime": "1672560000000", + "deliveryTime": "1672732800000", + "deliveryFeeRate": "0.00015", + "priceFilter": { + "minPrice": "0.1", + "maxPrice": "10000000", + "tickSize": "0.1" + }, + "lotSizeFilter": { + "maxOrderQty": "1500", + "minOrderQty": "0.1", + "qtyStep": "0.1" + } + } + ] + }, + "retExtInfo": {}, + "time": 1672712537130 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getInstrumentSpot.json5 b/xchange-bybit/src/test/resources/getInstrumentSpot.json5 new file mode 100644 index 00000000000..04c53443ab0 --- /dev/null +++ b/xchange-bybit/src/test/resources/getInstrumentSpot.json5 @@ -0,0 +1,30 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "spot", + "list": [ + { + "symbol": "BTCUSDT", + "baseCoin": "BTC", + "quoteCoin": "USDT", + "innovation": "0", + "status": "Trading", + "marginTrading": "both", + "lotSizeFilter": { + "basePrecision": "0.000001", + "quotePrecision": "0.00000001", + "minOrderQty": "0.000048", + "maxOrderQty": "71.73956243", + "minOrderAmt": "1", + "maxOrderAmt": "2000000" + }, + "priceFilter": { + "tickSize": "0.01" + } + } + ] + }, + "retExtInfo": {}, + "time": 1672712468011 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getOrderDetailsLinear.json5 b/xchange-bybit/src/test/resources/getOrderDetailsLinear.json5 new file mode 100644 index 00000000000..f6078291770 --- /dev/null +++ b/xchange-bybit/src/test/resources/getOrderDetailsLinear.json5 @@ -0,0 +1,55 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "list": [ + { + "orderId": "fd4300ae-7847-404e-b947-b46980a4d140", + "orderLinkId": "test-000005", + "blockTradeId": "", + "symbol": "ETHUSDT", + "price": "1600.00", + "qty": "0.10", + "side": "Buy", + "isLeverage": "", + "positionIdx": 1, + "orderStatus": "New", + "cancelType": "UNKNOWN", + "rejectReason": "EC_NoError", + "avgPrice": "0", + "leavesQty": "0.10", + "leavesValue": "160", + "cumExecQty": "0.00", + "cumExecValue": "0", + "cumExecFee": "0", + "timeInForce": "GTC", + "orderType": "Limit", + "stopOrderType": "UNKNOWN", + "orderIv": "", + "triggerPrice": "0.00", + "takeProfit": "2500.00", + "stopLoss": "1500.00", + "tpTriggerBy": "LastPrice", + "slTriggerBy": "LastPrice", + "triggerDirection": 0, + "triggerBy": "UNKNOWN", + "lastPriceOnCreated": "", + "reduceOnly": false, + "closeOnTrigger": false, + "smpType": "None", + "smpGroup": 0, + "smpOrderId": "", + "tpslMode": "Full", + "tpLimitPrice": "", + "slLimitPrice": "", + "placeType": "", + "createdTime": "1684738540559", + "updatedTime": "1684738540561" + } + ], + "nextPageCursor": "page_args%3Dfd4300ae-7847-404e-b947-b46980a4d140%26symbol%3D6%26", + "category": "linear" + }, + "retExtInfo": {}, + "time": 1684765770483 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getOrderDetailsSpot.json5 b/xchange-bybit/src/test/resources/getOrderDetailsSpot.json5 new file mode 100644 index 00000000000..a03f5092c78 --- /dev/null +++ b/xchange-bybit/src/test/resources/getOrderDetailsSpot.json5 @@ -0,0 +1,52 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "nextPageCursor": "page_args%3Dfd4300ae-7847-404e-b947-b46980a4d140%26symbol%3D6%26", + "category": "spot", + "list": [ + { + "symbol": "ETHUSDT", + "orderType": "Market", + "orderLinkId": "test-000005", + "orderId": "fd4300ae-7847-404e-b947-b46980a4d140", + "cancelType": "UNKNOWN", + "avgPrice": "25905.97", + "stopOrderType": "", + "lastPriceOnCreated": "", + "orderStatus": "PartiallyFilledCanceled", + "takeProfit": "", + "cumExecValue": "19.99940884", + "smpType": "None", + "triggerDirection": 0, + "blockTradeId": "", + "isLeverage": "0", + "rejectReason": "EC_CancelForNoFullFill", + "price": "0", + "orderIv": "", + "createdTime": "1684738540559", + "tpTriggerBy": "", + "positionIdx": 0, + "timeInForce": "IOC", + "leavesValue": "0.00059116", + "updatedTime": "1684738540561", + "side": "Buy", + "smpGroup": 0, + "triggerPrice": "0.00", + "cumExecFee": "0.000000772", + "leavesQty": "0.000000", + "slTriggerBy": "", + "closeOnTrigger": false, + "placeType": "", + "cumExecQty": "0.000772", + "reduceOnly": false, + "qty": "20.000000", + "stopLoss": "", + "smpOrderId": "", + "triggerBy": "" + } + ] + }, + "retExtInfo": {}, + "time": 1694634580983 +} diff --git a/xchange-bybit/src/test/resources/getSymbols.json5 b/xchange-bybit/src/test/resources/getSymbols.json5 deleted file mode 100644 index 1a442c1b100..00000000000 --- a/xchange-bybit/src/test/resources/getSymbols.json5 +++ /dev/null @@ -1,63 +0,0 @@ -{ - 'ret_code': 0, - 'ret_msg': 'OK', - 'ext_code': '', - 'ext_info': '', - 'result': [ - { - 'name': 'BTCUSDT', - 'alias': 'BTCUSDT', - 'status': 'Trading', - 'base_currency': 'BTC', - 'quote_currency': 'USDT', - 'price_scale': 2, - 'taker_fee': '0.0006', - 'maker_fee': '0.0001', - 'funding_interval': 480, - 'leverage_filter': { - 'min_leverage': 1, - 'max_leverage': 100, - 'leverage_step': '0.01' - }, - 'price_filter': { - 'min_price': '0.5', - 'max_price': '999999', - 'tick_size': '0.5' - }, - 'lot_size_filter': { - 'max_trading_qty': 20, - 'min_trading_qty': 0.001, - 'qty_step': 0.001, - 'post_only_max_trading_qty': '100' - } - }, - { - 'name': 'ETHUSDT', - 'alias': 'ETHUSDT', - 'status': 'Trading', - 'base_currency': 'ETH', - 'quote_currency': 'USDT', - 'price_scale': 2, - 'taker_fee': '0.0006', - 'maker_fee': '0.0001', - 'funding_interval': 480, - 'leverage_filter': { - 'min_leverage': 1, - 'max_leverage': 100, - 'leverage_step': '0.01' - }, - 'price_filter': { - 'min_price': '0.05', - 'max_price': '99999.9', - 'tick_size': '0.05' - }, - 'lot_size_filter': { - 'max_trading_qty': 1000, - 'min_trading_qty': 0.01, - 'qty_step': 0.01, - 'post_only_max_trading_qty': '5000' - } - } - ], - 'time_now': '1657475395.487439' -} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getTicker.json5 b/xchange-bybit/src/test/resources/getTicker.json5 deleted file mode 100644 index 57d24add04c..00000000000 --- a/xchange-bybit/src/test/resources/getTicker.json5 +++ /dev/null @@ -1,37 +0,0 @@ -{ - 'ret_code': 0, - 'ret_msg': 'OK', - 'ext_code': '', - 'ext_info': '', - 'result': [ - { - 'symbol': 'BTCUSDT', - 'bid_price': '21323', - 'ask_price': '21334', - 'last_price': '21333.00', - 'last_tick_direction': 'PlusTick', - 'prev_price_24h': '21670.00', - 'price_24h_pcnt': '-0.015551', - 'high_price_24h': '22024.50', - 'low_price_24h': '21120.00', - 'prev_price_1h': '21307.00', - 'price_1h_pcnt': '0.00122', - 'mark_price': '21331.00', - 'index_price': '21334.53', - 'open_interest': 16028.75, - 'open_value': '0.00', - 'total_turnover': '38884574628.30', - 'turnover_24h': '216158761.48', - 'total_volume': 9588193.5, - 'volume_24h': 10028.87, - 'funding_rate': '0.0001', - 'predicted_funding_rate': '0.0001', - 'next_funding_time': '2022-07-10T16:00:00Z', - 'countdown_hour': 7, - 'delivery_fee_rate': '', - 'predicted_delivery_price': '', - 'delivery_time': '' - } - ], - 'time_now': '1657444151.611671' -} diff --git a/xchange-bybit/src/test/resources/getTickerInverse.json5 b/xchange-bybit/src/test/resources/getTickerInverse.json5 new file mode 100644 index 00000000000..69684c5ffd8 --- /dev/null +++ b/xchange-bybit/src/test/resources/getTickerInverse.json5 @@ -0,0 +1,37 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "inverse", + "list": [ + { + "symbol": "BTCUSD", + "lastPrice": "16597.00", + "indexPrice": "16598.54", + "markPrice": "16596.00", + "prevPrice24h": "16464.50", + "price24hPcnt": "0.008047", + "highPrice24h": "30912.50", + "lowPrice24h": "15700.00", + "prevPrice1h": "16595.50", + "openInterest": "373504107", + "openInterestValue": "22505.67", + "turnover24h": "2352.94950046", + "volume24h": "49337318", + "fundingRate": "-0.001034", + "nextFundingTime": "1672387200000", + "predictedDeliveryPrice": "", + "basisRate": "", + "deliveryFeeRate": "", + "deliveryTime": "0", + "ask1Size": "1", + "bid1Price": "16596.00", + "ask1Price": "16597.50", + "bid1Size": "1", + "basis": "" + } + ] + }, + "retExtInfo": {}, + "time": 1672376496682 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getTickerOption.json5 b/xchange-bybit/src/test/resources/getTickerOption.json5 new file mode 100644 index 00000000000..33250ff6cb8 --- /dev/null +++ b/xchange-bybit/src/test/resources/getTickerOption.json5 @@ -0,0 +1,38 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "option", + "list": [ + { + "symbol": "BTC-30DEC22-18000-C", + "bid1Price": "0", + "bid1Size": "0", + "bid1Iv": "0", + "ask1Price": "435", + "ask1Size": "0.66", + "ask1Iv": "5", + "lastPrice": "435", + "highPrice24h": "435", + "lowPrice24h": "165", + "markPrice": "0.00000009", + "indexPrice": "16600.55", + "markIv": "0.7567", + "underlyingPrice": "16590.42", + "openInterest": "6.3", + "turnover24h": "2482.73", + "volume24h": "0.15", + "totalVolume": "99", + "totalTurnover": "1967653", + "delta": "0.00000001", + "gamma": "0.00000001", + "vega": "0.00000004", + "theta": "-0.00000152", + "predictedDeliveryPrice": "0", + "change24h": "86" + } + ] + }, + "retExtInfo": {}, + "time": 1672376592395 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getTickerSpot.json5 b/xchange-bybit/src/test/resources/getTickerSpot.json5 new file mode 100644 index 00000000000..d7870658e7e --- /dev/null +++ b/xchange-bybit/src/test/resources/getTickerSpot.json5 @@ -0,0 +1,26 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "spot", + "list": [ + { + "symbol": "BTCUSDT", + "bid1Price": "20517.96", + "bid1Size": "2", + "ask1Price": "20527.77", + "ask1Size": "1.862172", + "lastPrice": "20533.13", + "prevPrice24h": "20393.48", + "price24hPcnt": "0.0068", + "highPrice24h": "21128.12", + "lowPrice24h": "20318.89", + "turnover24h": "243765620.65899866", + "volume24h": "11801.27771", + "usdIndexPrice": "20784.12009279" + } + ] + }, + "retExtInfo": {}, + "time": 1673859087947 +} \ No newline at end of file diff --git a/xchange-bybit/src/test/resources/getWalletBalance.json5 b/xchange-bybit/src/test/resources/getWalletBalance.json5 new file mode 100644 index 00000000000..7da7b5e1300 --- /dev/null +++ b/xchange-bybit/src/test/resources/getWalletBalance.json5 @@ -0,0 +1,44 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "list": [ + { + "totalEquity": "3.31216591", + "accountIMRate": "0", + "totalMarginBalance": "3.00326056", + "totalInitialMargin": "0", + "accountType": "UNIFIED", + "totalAvailableBalance": "3.00326056", + "accountMMRate": "0", + "totalPerpUPL": "0", + "totalWalletBalance": "3.00326056", + "accountLTV": "0", + "totalMaintenanceMargin": "0", + "coin": [ + { + "availableToBorrow": "3", + "bonus": "0", + "accruedInterest": "0", + "availableToWithdraw": "0", + "totalOrderIM": "0", + "equity": "0", + "totalPositionMM": "0", + "usdValue": "0", + "unrealisedPnl": "0", + "collateralSwitch": true, + "borrowAmount": "0.0", + "totalPositionIM": "0", + "walletBalance": "0", + "cumRealisedPnl": "0", + "locked": "0", + "marginCollateral": true, + "coin": "BTC" + } + ] + } + ] + }, + "retExtInfo": {}, + "time": 1690872862481 +} \ No newline at end of file diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbasePro.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbasePro.java index a6b4b430e97..b93a212b0b0 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbasePro.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbasePro.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.util.Date; -import java.util.List; import java.util.Map; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; @@ -20,7 +19,9 @@ import org.knowm.xchange.coinbasepro.dto.CoinbaseProException; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTrades; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfers; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFee; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProLedger; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProSendMoneyRequest; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWithdrawCryptoResponse; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWithdrawFundsRequest; @@ -32,15 +33,14 @@ import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductTicker; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProStats; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProTrade; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccount; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccountAddress; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWallet; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWalletAddress; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProIdResponse; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProOrder; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProPlaceOrder; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProSendMoneyResponse; import org.knowm.xchange.utils.DateUtils; -import si.mazi.rescu.HttpStatusIOException; import si.mazi.rescu.ParamsDigest; @Path("/") @@ -110,13 +110,23 @@ CoinbaseProCandle[] getHistoricalCandles( /** Authenticated calls */ @GET @Path("accounts") - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount[] getAccounts( + CoinbaseProAccount[] getAccounts( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase) throws CoinbaseProException, IOException; + @GET + @Path("accounts/{account_id}") + CoinbaseProAccount getAccountById( + @HeaderParam("CB-ACCESS-KEY") String apiKey, + @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, + @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, + @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, + @PathParam("account_id") String accountId) + throws CoinbaseProException, IOException; + @GET @Path("fees") CoinbaseProFee getFees( @@ -192,17 +202,22 @@ CoinbaseProOrder getOrder( throws CoinbaseProException, IOException; /** - * @param apiKey for account - * @param signer for account - * @param timestamp of message - * @param passphrase for account - * @param tradeIdAfter Return trades before this tradeId. - * @param tradeIdBefore Return trades after this tradeId. - * @param orderId to get fills for - * @param productId to get fills for - * @return fill array - * @throws CoinbaseProException when exchange throws exception - * @throws IOException when connection issue arises + * Get a list of fills. A fill is a partial or complete match on a specific order. + * @param apiKey + * @param signer + * @param timestamp + * @param passphrase + * @param orderId + * @param productId + * @param limit + * @param tradeIdBefore + * @param tradeIdAfter + * @param marketType + * @param startDate + * @param endDate + * @return CoinbasePagedResponse + * @throws CoinbaseProException + * @throws IOException */ @GET @Path("fills") @@ -211,12 +226,15 @@ CoinbasePagedResponse getFills( @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, - @QueryParam("after") Integer tradeIdAfter, - @QueryParam("before") Integer tradeIdBefore, - @QueryParam("limit") Integer limit, @QueryParam("order_id") String orderId, - @QueryParam("product_id") String productId) - throws CoinbaseProException, IOException; + @QueryParam("product_id") String productId, + @QueryParam("limit") Integer limit, + @QueryParam("before") Integer tradeIdBefore, + @QueryParam("after") Integer tradeIdAfter, + @QueryParam("market_type") String marketType, + @QueryParam("start_date") String startDate, + @QueryParam("end_date") String endDate + ) throws CoinbaseProException, IOException; @POST @Path("accounts/{account_id}/transactions") @@ -233,41 +251,55 @@ CoinbaseProSendMoneyResponse sendMoney( @GET @Path("accounts/{account_id}/ledger") @Consumes(MediaType.APPLICATION_JSON) - List> ledger( + CoinbaseProLedger ledger( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, @PathParam("account_id") String accountId, - @QueryParam("after") String startingOrderId) + @QueryParam("start_date") String startDate, + @QueryParam("end_date") String endDate, + @QueryParam("before") String beforeId, + @QueryParam("after") String afterId, + @QueryParam("limit") Integer limit, + @QueryParam("profile_id") String profileId) throws CoinbaseProException, IOException; + /** + * Lists past withdrawals and deposits for an account. + */ @GET @Path("accounts/{account_id}/transfers") @Consumes(MediaType.APPLICATION_JSON) - CoinbaseProTransfers transfers( + CoinbaseProTransfers getTransfersByAccountId( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, @PathParam("account_id") String accountId, - @QueryParam("profile_id") String profileId, + @QueryParam("before") String before, + @QueryParam("after") String after, @QueryParam("limit") Integer limit, - @QueryParam("after") String createdAtDate); + @QueryParam("type") String type) // Possible types [deposit, withdraw, internal_deposit, internal_withdraw] + throws CoinbaseProException, IOException; + /** + * Gets a list of in-progress and completed transfers of funds in/out of any of the user's accounts. + */ @GET @Path("transfers") @Consumes(MediaType.APPLICATION_JSON) - CoinbaseProTransfers transfers( + CoinbaseProTransfers getTransfers( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, - @QueryParam("type") String type, + @QueryParam("type") String type, // Possible types [deposit, withdraw, internal_deposit, internal_withdraw] @QueryParam("profile_id") String profileId, @QueryParam("before") String beforeDate, @QueryParam("after") String afterDate, - @QueryParam("limit") Integer limit); + @QueryParam("limit") Integer limit) + throws CoinbaseProException, IOException; @POST @Path("reports") @@ -301,25 +333,26 @@ CoinbaseProWithdrawCryptoResponse withdrawCrypto( @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, CoinbaseProWithdrawFundsRequest request) - throws HttpStatusIOException; + throws CoinbaseProException, IOException; @GET @Path("coinbase-accounts") - CoinbaseProAccount[] getCoinbaseProAccounts( + CoinbaseProWallet[] getCoinbaseProWallets( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase) - throws HttpStatusIOException; + throws CoinbaseProException, IOException; @POST @Path("coinbase-accounts/{account_id}/addresses") - CoinbaseProAccountAddress getCoinbaseProAccountAddress( + CoinbaseProWalletAddress getCoinbaseProWalletAddress( @HeaderParam("CB-ACCESS-KEY") String apiKey, @HeaderParam("CB-ACCESS-SIGN") ParamsDigest signer, @HeaderParam("CB-ACCESS-TIMESTAMP") long timestamp, @HeaderParam("CB-ACCESS-PASSPHRASE") String passphrase, - @PathParam("account_id") String accountId); + @PathParam("account_id") String accountId) + throws CoinbaseProException, IOException; @GET @Path("/users/self/verify") diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProAdapters.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProAdapters.java index 786ca280da8..497d6edc745 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProAdapters.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProAdapters.java @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfer; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProLedger; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProCurrency; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProduct; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductBook; @@ -24,6 +25,7 @@ import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProStats; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProTrade; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill.Side; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProOrder; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProOrderFlags; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProPlaceLimitOrder; @@ -36,6 +38,7 @@ import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -43,7 +46,6 @@ import org.knowm.xchange.dto.marketdata.Trades; import org.knowm.xchange.dto.marketdata.Trades.TradeSortType; import org.knowm.xchange.dto.meta.CurrencyMetaData; -import org.knowm.xchange.dto.meta.ExchangeMetaData; import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.dto.meta.WalletHealth; import org.knowm.xchange.dto.trade.LimitOrder; @@ -174,7 +176,7 @@ private static List toLimitOrderList( return allLevels; } - public static Wallet adaptAccountInfo(CoinbaseProAccount[] coinbaseProAccounts) { + public static Wallet adaptWallet(CoinbaseProAccount[] coinbaseProAccounts) { List balances = new ArrayList<>(coinbaseProAccounts.length); for (CoinbaseProAccount coinbaseProAccount : coinbaseProAccounts) { @@ -186,7 +188,20 @@ public static Wallet adaptAccountInfo(CoinbaseProAccount[] coinbaseProAccounts) coinbaseProAccount.getHold())); } - return Wallet.Builder.from(balances).id(coinbaseProAccounts[0].getProfile_id()).build(); + return Wallet.Builder.from(balances).id(coinbaseProAccounts[0].getProfileId()).build(); + } + + public static Wallet adaptWallet(CoinbaseProAccount coinbaseProAccount) { + List balances = new ArrayList<>(); + + balances.add( + new Balance( + Currency.getInstance(coinbaseProAccount.getCurrency()), + coinbaseProAccount.getBalance(), + coinbaseProAccount.getAvailable(), + coinbaseProAccount.getHold())); + + return Wallet.Builder.from(balances).id(coinbaseProAccount.getProfileId()).build(); } @SuppressWarnings("unchecked") @@ -194,7 +209,7 @@ public static OpenOrders adaptOpenOrders(CoinbaseProOrder[] coinbaseExOpenOrders final Map> twoTypes = Arrays.stream(coinbaseExOpenOrders) .map(CoinbaseProAdapters::adaptOrder) - .collect(Collectors.partitioningBy(t -> t instanceof LimitOrder)); + .collect(Collectors.partitioningBy(LimitOrder.class::isInstance)); @SuppressWarnings("rawtypes") List limitOrders = twoTypes.get(true); return new OpenOrders(limitOrders, twoTypes.get(false)); @@ -310,7 +325,7 @@ public static UserTrades adaptTradeHistory(List coinbaseExFills trades.add( new UserTrade.Builder() - .type("buy".equals(fill.getSide()) ? OrderType.BID : OrderType.ASK) + .type(fill.getSide().equals(Side.buy) ? OrderType.BID : OrderType.ASK) .originalAmount(fill.getSize()) .currencyPair(currencyPair) .price(fill.getPrice()) @@ -348,10 +363,10 @@ public static Trades adaptTrades(CoinbaseProTrade[] coinbaseExTrades, CurrencyPa } public static CurrencyPair adaptCurrencyPair(CoinbaseProProduct product) { - return new CurrencyPair(product.getBaseCurrency(), product.getTargetCurrency()); + return new CurrencyPair(product.getBaseCurrency(), product.getQuoteCurrency()); } - private static Currency adaptCurrency(CoinbaseProCurrency currency) { + public static Currency adaptCurrency(CoinbaseProCurrency currency) { return new Currency(currency.getId()); } @@ -360,71 +375,18 @@ private static int numberOfDecimals(BigDecimal value) { return -(int) Math.round(Math.log10(d)); } - public static ExchangeMetaData adaptToExchangeMetaData( - ExchangeMetaData exchangeMetaData, - CoinbaseProProduct[] products, - CoinbaseProCurrency[] cbCurrencies) { - - Map currencyPairs = - exchangeMetaData == null ? new HashMap<>() : exchangeMetaData.getInstruments(); - - Map currencies = - exchangeMetaData == null ? new HashMap<>() : exchangeMetaData.getCurrencies(); - - for (CoinbaseProProduct product : products) { - if (!"online".equals(product.getStatus())) { - continue; - } - CurrencyPair pair = adaptCurrencyPair(product); - - InstrumentMetaData staticMetaData = currencyPairs.get(pair); - int baseScale = numberOfDecimals(product.getBaseIncrement()); - int priceScale = numberOfDecimals(product.getQuoteIncrement()); - boolean marketOrderAllowed = !product.isLimitOnly(); - - currencyPairs.put( - pair, - new InstrumentMetaData.Builder() - .tradingFee(new BigDecimal("0.50")) - .minimumAmount(product.getBaseMinSize()) - .maximumAmount(product.getBaseMaxSize()) - .volumeScale(baseScale) - .priceScale(priceScale) - .counterMinimumAmount(product.getMinMarketFunds()) - .counterMaximumAmount(product.getMaxMarketFunds()) - .feeTiers(staticMetaData != null ? staticMetaData.getFeeTiers() : null) - .tradingFeeCurrency(pair.counter) - .marketOrderEnabled(marketOrderAllowed) - .build()); - } - - Arrays.stream(cbCurrencies) - .forEach( - currency -> - currencies.put( - adaptCurrency(currency), - new CurrencyMetaData( - numberOfDecimals(currency.getMaxPrecision()), - BigDecimal.ZERO, - currency.getDetails().getMinWithdrawalAmount(), - "online".equals(currency.getStatus()) - ? WalletHealth.ONLINE - : WalletHealth.OFFLINE))); - - return new ExchangeMetaData( - currencyPairs, - currencies, - exchangeMetaData == null ? null : exchangeMetaData.getPublicRateLimits(), - exchangeMetaData == null ? null : exchangeMetaData.getPrivateRateLimits(), - true); - } - public static String adaptProductID(CurrencyPair currencyPair) { return currencyPair == null ? null : currencyPair.base.getCurrencyCode() + "-" + currencyPair.counter.getCurrencyCode(); } + public static String adaptProductID(Instrument instrument) { + return instrument == null + ? null + : instrument.getBase().getCurrencyCode() + "-" + instrument.getCounter().getCurrencyCode(); + } + public static CoinbaseProPlaceOrder.Side adaptSide(OrderType orderType) { return orderType == OrderType.ASK ? CoinbaseProPlaceOrder.Side.sell @@ -504,12 +466,11 @@ public static CoinbaseProPlaceOrder adaptCoinbaseProStopOrder(StopOrder stopOrde .build(); } - public static FundingRecord adaptFundingRecord( - Currency currency, CoinbaseProTransfer coinbaseProTransfer) { + public static FundingRecord adaptFundingRecord(CoinbaseProTransfer coinbaseProTransfer) { FundingRecord.Status status = FundingRecord.Status.PROCESSING; - Date processedAt = coinbaseProTransfer.processedAt(); - Date canceledAt = coinbaseProTransfer.canceledAt(); + Date processedAt = coinbaseProTransfer.getProcessedAt(); + Date canceledAt = coinbaseProTransfer.getCanceledAt(); if (canceledAt != null) status = FundingRecord.Status.CANCELLED; else if (processedAt != null) status = FundingRecord.Status.COMPLETE; @@ -518,21 +479,19 @@ public static FundingRecord adaptFundingRecord( if (address == null) address = coinbaseProTransfer.getDetails().getSentToAddress(); String cryptoTransactionHash = coinbaseProTransfer.getDetails().getCryptoTransactionHash(); - String transactionHash = adaptTransactionHash(currency.getSymbol(), cryptoTransactionHash); - - return new FundingRecord( - address, - coinbaseProTransfer.getDetails().getDestinationTag(), - coinbaseProTransfer.createdAt(), - currency, - coinbaseProTransfer.amount(), - coinbaseProTransfer.getId(), - transactionHash, - coinbaseProTransfer.type(), - status, - null, - null, - null); + String transactionHash = adaptTransactionHash(coinbaseProTransfer.getCurrency(), cryptoTransactionHash); + + return FundingRecord.builder() + .address(address) + .addressTag(coinbaseProTransfer.getDetails().getDestinationTag()) + .date(coinbaseProTransfer.getCreatedAt()) + .currency(Currency.getInstance(coinbaseProTransfer.getCurrency())) + .amount(coinbaseProTransfer.getAmount()) + .internalId(coinbaseProTransfer.getId()) + .blockchainTransactionHash(transactionHash) + .type(coinbaseProTransfer.getType()) + .status(status) + .build(); } // crypto_transaction_link: "https://etherscan.io/tx/0x{{txId}}" @@ -558,4 +517,58 @@ private static String adaptTransactionHash(String currency, String transactionHa } return transactionHash; } + + public static List adaptCoinbaseProLedger(CoinbaseProLedger ledger) { + List records = new ArrayList<>(); + + ledger.forEach( + coinbaseProLedgerDto -> records.add(FundingRecord.builder() + .internalId(coinbaseProLedgerDto.getId()) + .amount(coinbaseProLedgerDto.getAmount()) + .balance(coinbaseProLedgerDto.getBalance()) + .date(coinbaseProLedgerDto.getCreatedAt()) + .status(Status.COMPLETE) + .description(coinbaseProLedgerDto.getType().name()) +// .type(convertCoinbaseProLedgerTxType(coinbaseProLedgerDto.getType())) + .build())); + return records; + } + + + public static Map adaptCoinbaseProCurrencies(CoinbaseProCurrency[] coinbaseProCurrencies) { + Map map = new HashMap<>(); + + Arrays.stream(coinbaseProCurrencies).forEach(coinbaseProCurrency -> map.put(adaptCurrency(coinbaseProCurrency), CurrencyMetaData.builder() + .scale(coinbaseProCurrency.getMinSize().scale()) + .walletHealth("online".equals(coinbaseProCurrency.getStatus()) + ? WalletHealth.ONLINE + : WalletHealth.OFFLINE) + .minWithdrawalAmount((coinbaseProCurrency.getDetails().getMinWithdrawalAmount() != null) + ? coinbaseProCurrency.getDetails().getMinWithdrawalAmount() + : BigDecimal.ZERO) + .build())); + + return map; + } + + public static Map adaptCoinbaseProCurrencyPairs(CoinbaseProProduct[] coinbaseProProducts) { + Map map = new HashMap<>(); + + Arrays.stream(coinbaseProProducts).forEach(coinbaseProProduct -> { + Instrument instrument = adaptCurrencyPair(coinbaseProProduct); + + map.put(instrument, new InstrumentMetaData.Builder() + .tradingFee(new BigDecimal("0.50")) + .volumeScale(numberOfDecimals(coinbaseProProduct.getBaseIncrement())) + .priceScale(numberOfDecimals(coinbaseProProduct.getQuoteIncrement())) + .counterMinimumAmount(coinbaseProProduct.getMinMarketFunds()) + .amountStepSize(coinbaseProProduct.getBaseIncrement()) + .priceStepSize(coinbaseProProduct.getQuoteIncrement()) + .tradingFeeCurrency(instrument.getCounter()) + .marketOrderEnabled(!coinbaseProProduct.isLimitOnly()) + .build()); + }); + + return map; + } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProExchange.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProExchange.java index f457c39c33a..54a9f7fffa9 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProExchange.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/CoinbaseProExchange.java @@ -13,12 +13,10 @@ import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.client.ResilienceRegistries; -import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProCurrency; -import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProduct; import org.knowm.xchange.coinbasepro.service.CoinbaseProAccountService; import org.knowm.xchange.coinbasepro.service.CoinbaseProMarketDataService; -import org.knowm.xchange.coinbasepro.service.CoinbaseProMarketDataServiceRaw; import org.knowm.xchange.coinbasepro.service.CoinbaseProTradeService; +import org.knowm.xchange.dto.meta.ExchangeMetaData; import si.mazi.rescu.SynchronizedValueFactory; public class CoinbaseProExchange extends BaseExchange { @@ -121,12 +119,12 @@ public ResilienceRegistries getResilienceRegistries() { @Override public void remoteInit() throws IOException { - CoinbaseProProduct[] products = - ((CoinbaseProMarketDataServiceRaw) marketDataService).getCoinbaseProProducts(); - CoinbaseProCurrency[] currencies = - ((CoinbaseProMarketDataServiceRaw) marketDataService).getCoinbaseProCurrencies(); - exchangeMetaData = - CoinbaseProAdapters.adaptToExchangeMetaData(exchangeMetaData, products, currencies); + exchangeMetaData = new ExchangeMetaData( + marketDataService.getInstruments(), + marketDataService.getCurrencies(), + exchangeMetaData == null ? null : exchangeMetaData.getPublicRateLimits(), + exchangeMetaData == null ? null : exchangeMetaData.getPrivateRateLimits(), + true); } // @NoArgsConstructor(access = AccessLevel.PRIVATE) diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProTransfer.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProTransfer.java index 37179a25cf6..e8107952bba 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProTransfer.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProTransfer.java @@ -5,7 +5,9 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import org.knowm.xchange.dto.account.FundingRecord; +import lombok.Getter; +import lombok.ToString; +import org.knowm.xchange.dto.account.FundingRecord.Type; /* examples: @@ -47,16 +49,63 @@ } */ +@ToString +@Getter public class CoinbaseProTransfer { + private final String id; + private final Type type; + private final Date createdAt; + private final Date completedAt; + private final Date canceledAt; + private final Date processedAt; + private final String accountId; + private final String userId; + private final String userNonce; + private final BigDecimal amount; + private final String idem; + private final String currency; + private final Detail details; + + public CoinbaseProTransfer( + @JsonProperty("id") String id, + @JsonProperty("type") String type, + @JsonProperty("created_at") String createdAt, + @JsonProperty("completed_at") String completedAt, + @JsonProperty("canceled_at") String canceledAt, + @JsonProperty("processed_at") String processedAt, + @JsonProperty("account_id") String accountId, + @JsonProperty("user_id") String userId, + @JsonProperty("user_nonce") String userNonce, + @JsonProperty("amount") String amount, + @JsonProperty("idem") String idem, + @JsonProperty("currency") String currency, + @JsonProperty("details") Detail details) { + this.id = id; + this.type = Type.valueOf(type.toUpperCase()); + this.createdAt = parse(createdAt); + this.completedAt = parse(completedAt); + this.canceledAt = parse(canceledAt); + this.processedAt = parse(processedAt); + this.accountId = accountId; + this.userId = userId; + this.userNonce = userNonce; + this.amount = (amount == null) ? BigDecimal.ZERO : new BigDecimal(amount); + this.idem = idem; + this.currency = currency; + this.details = details; + } + + @ToString + @Getter public static class Detail { - public final String cryptoAddress; - public final String coinbaseAccountId; - public final String cryptoTransactionId; - public final String coinbaseTransactionId; - public final String cryptoTransactionHash; - public final String sentToAddress; - public final String coinbaseWithdrawalId; + private final String cryptoAddress; + private final String coinbaseAccountId; + private final String cryptoTransactionId; + private final String coinbaseTransactionId; + private final String cryptoTransactionHash; + private final String sentToAddress; + private final String coinbaseWithdrawalId; private final String destinationTag; private final String destinationTagName; @@ -80,178 +129,6 @@ public Detail( this.destinationTag = destinationTag; this.destinationTagName = destinationTagName; } - - public String getCryptoAddress() { - return cryptoAddress; - } - - public String getCoinbaseAccountId() { - return coinbaseAccountId; - } - - public String getCryptoTransactionId() { - return cryptoTransactionId; - } - - public String getCoinbaseTransactionId() { - return coinbaseTransactionId; - } - - public String getCryptoTransactionHash() { - return cryptoTransactionHash; - } - - public String getSentToAddress() { - return sentToAddress; - } - - public String getCoinbaseWithdrawalId() { - return coinbaseWithdrawalId; - } - - public String getDestinationTag() { - return destinationTag; - } - - @Override - public String toString() { - return "Detail{" - + "cryptoAddress='" - + cryptoAddress - + '\'' - + ", coinbaseAccountId='" - + coinbaseAccountId - + '\'' - + ", cryptoTransactionId='" - + cryptoTransactionId - + '\'' - + ", coinbaseTransactionId='" - + coinbaseTransactionId - + '\'' - + ", cryptoTransactionHash='" - + cryptoTransactionHash - + '\'' - + ", sentToAddress='" - + sentToAddress - + '\'' - + ", coinbaseWithdrawalId='" - + coinbaseWithdrawalId - + '\'' - + ", destinationTag='" - + destinationTag - + '\'' - + ", destinationTagName='" - + destinationTagName - + '\'' - + '}'; - } - } - - public final String id; - public final String type; - public final String createdAt; - public final String completedAt; - public final String canceledAt; - public final String processedAt; - public final String accountId; - public final String userId; - public final String userNonce; - public final String amount; - public final Detail details; - - public CoinbaseProTransfer( - @JsonProperty("id") String id, - @JsonProperty("type") String type, - @JsonProperty("created_at") String createdAt, - @JsonProperty("completed_at") String completedAt, - @JsonProperty("canceled_at") String canceledAt, - @JsonProperty("processed_at") String processedAt, - @JsonProperty("account_id") String accountId, - @JsonProperty("user_id") String userId, - @JsonProperty("user_nonce") String userNonce, - @JsonProperty("amount") String amount, - @JsonProperty("details") Detail details) { - this.id = id; - this.type = type; - this.createdAt = createdAt; - this.completedAt = completedAt; - this.canceledAt = canceledAt; - this.processedAt = processedAt; - this.accountId = accountId; - this.userId = userId; - this.userNonce = userNonce; - this.amount = amount; - this.details = details; - } - - public String getId() { - return id; - } - - public String getType() { - return type; - } - - public FundingRecord.Type type() { - return type.equalsIgnoreCase("withdraw") - ? FundingRecord.Type.WITHDRAWAL - : FundingRecord.Type.DEPOSIT; - } - - public String getCreatedAt() { - return createdAt; - } - - public Date createdAt() { - return parse(createdAt); - } - - public String getCompletedAt() { - return completedAt; - } - - public Date completedAt() { - return parse(completedAt); - } - - public String getCanceledAt() { - return canceledAt; - } - - public Date canceledAt() { - return parse(canceledAt); - } - - public String getProcessedAt() { - return processedAt; - } - - public Date processedAt() { - return parse(processedAt); - } - - public String getAccountId() { - return accountId; - } - - public String getUserId() { - return userId; - } - - public String getUserNonce() { - return userNonce; - } - - public String getAmount() { - return amount; - } - - public BigDecimal amount() { - return new BigDecimal(amount); - } - - public Detail getDetails() { - return details; } private static Date parse(String time) { @@ -263,42 +140,4 @@ private static Date parse(String time) { throw new IllegalStateException("Cannot parse '" + time + "'", e); } } - - @Override - public String toString() { - return "CoinbaseProTransfer{" - + "id='" - + id - + '\'' - + ", type='" - + type - + '\'' - + ", createdAt='" - + createdAt - + '\'' - + ", completedAt='" - + completedAt - + '\'' - + ", canceledAt='" - + canceledAt - + '\'' - + ", processedAt='" - + processedAt - + '\'' - + ", accountId='" - + accountId - + '\'' - + ", userId='" - + userId - + '\'' - + ", userNonce='" - + userNonce - + '\'' - + ", amount='" - + amount - + '\'' - + ", details=" - + details - + '}'; - } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProAccount.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProAccount.java index 7846ac7ee5c..e64725298fa 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProAccount.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProAccount.java @@ -2,70 +2,34 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import lombok.Getter; +import lombok.ToString; +@ToString +@Getter public class CoinbaseProAccount { private final String id; private final String currency; - private final String profile_id; + private final String profileId; private final BigDecimal balance; private final BigDecimal hold; private final BigDecimal available; + private final boolean tradingEnabled; public CoinbaseProAccount( @JsonProperty("id") String id, @JsonProperty("currency") String currency, - @JsonProperty("profile_id") String profile_id, + @JsonProperty("profile_id") String profileId, @JsonProperty("balance") BigDecimal balance, @JsonProperty("hold") BigDecimal hold, - @JsonProperty("available") BigDecimal available) { + @JsonProperty("available") BigDecimal available, + @JsonProperty("trading_enabled") boolean tradingEnabled) { this.id = id; this.currency = currency; - this.profile_id = profile_id; + this.profileId = profileId; this.balance = balance; this.hold = hold; this.available = available; - } - - public String getId() { - return id; - } - - public String getCurrency() { - return currency; - } - - public String getProfile_id() { - return profile_id; - } - - public BigDecimal getBalance() { - return balance; - } - - public BigDecimal getHold() { - return hold; - } - - public BigDecimal getAvailable() { - return available; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("CoinbaseExAccount [id="); - builder.append(id); - builder.append(", currency="); - builder.append(currency); - builder.append(", profile_id="); - builder.append(profile_id); - builder.append(", balance="); - builder.append(balance); - builder.append(", hold="); - builder.append(hold); - builder.append(", available="); - builder.append(available); - builder.append("]"); - return builder.toString(); + this.tradingEnabled = tradingEnabled; } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProFundingHistoryParams.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProFundingHistoryParams.java new file mode 100644 index 00000000000..4b2aeb1649e --- /dev/null +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProFundingHistoryParams.java @@ -0,0 +1,73 @@ +package org.knowm.xchange.coinbasepro.dto.account; + +import java.util.Date; +import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.service.trade.params.HistoryParamsFundingType; +import org.knowm.xchange.service.trade.params.TradeHistoryParamAccountId; +import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit; +import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsIdSpan; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; + +public class CoinbaseProFundingHistoryParams implements + TradeHistoryParams, + HistoryParamsFundingType, + TradeHistoryParamLimit, TradeHistoryParamsTimeSpan, TradeHistoryParamAccountId { + + private Type type; + private Integer limit; + private Date beforeDate; + private Date afterDate; + private String accountId; + + @Override + public Type getType() { + return type; + } + + @Override + public void setType(Type type) { + this.type = type; + } + + @Override + public Integer getLimit() { + return limit; + } + + @Override + public void setLimit(Integer limit) { + this.limit = limit; + } + + @Override + public Date getStartTime() { + return beforeDate; + } + + @Override + public void setStartTime(Date startTime) { + this.beforeDate = startTime; + } + + @Override + public Date getEndTime() { + return afterDate; + } + + @Override + public void setEndTime(Date endTime) { + this.afterDate = endTime; + } + + @Override + public String getAccountId() { + return accountId; + } + + @Override + public void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedger.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedger.java new file mode 100644 index 00000000000..d697369aa25 --- /dev/null +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedger.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.coinbasepro.dto.account; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import si.mazi.rescu.HttpResponseAware; + +public class CoinbaseProLedger extends ArrayList + implements HttpResponseAware { + + private Map> headers; + + @Override + public void setResponseHeaders(Map> headers) { + this.headers = headers; + } + + @Override + public Map> getResponseHeaders() { + return headers; + } + + public String getHeader(String key) { + return getResponseHeaders().get(key).get(0); + } +} diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedgerDto.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedgerDto.java new file mode 100644 index 00000000000..d3487e2a7cb --- /dev/null +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProLedgerDto.java @@ -0,0 +1,77 @@ +package org.knowm.xchange.coinbasepro.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Getter; +import lombok.ToString; +import org.knowm.xchange.coinbasepro.CoinbaseProAdapters; +import org.knowm.xchange.instrument.Instrument; + +@ToString +@Getter +public class CoinbaseProLedgerDto { + + private final String id; + + private final BigDecimal amount; + + private final Date createdAt; + + private final BigDecimal balance; + + private final CoinbaseProLedgerTxType type; + + private final CoinbaseProLedgerDetails details; + + public CoinbaseProLedgerDto( + @JsonProperty("id") String id, + @JsonProperty("amount") BigDecimal amount, + @JsonProperty("created_at") Date createdAt, + @JsonProperty("balance") BigDecimal balance, + @JsonProperty("type") CoinbaseProLedgerTxType type, + @JsonProperty("details") CoinbaseProLedgerDetails details) { + this.id = id; + this.amount = amount; + this.createdAt = createdAt; + this.balance = balance; + this.type = type; + this.details = details; + } + + @Getter + @ToString + public static class CoinbaseProLedgerDetails{ + + private final String orderId; + + private final Instrument productId; + + private final String tradeId; + + public CoinbaseProLedgerDetails( + @JsonProperty("order_id") String orderId, + @JsonProperty("product_id") String productId, + @JsonProperty("trade_id") String tradeId) { + this.orderId = orderId; + this.productId = (productId == null) ? null : CoinbaseProAdapters.toCurrencyPair(productId); + this.tradeId = tradeId; + } + } + public enum CoinbaseProLedgerTxType{ + transfer, + match, + fee, + conversion, + margin_interest, + rebate, + otc_fee, + otc_match, + tax_credit, + rfq_match, + rfq_fee, + match_conversion, + stake_wrap + + } +} diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProTransfersWithHeader.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProTransfersWithHeader.java index 7a73e68d3f6..e6b088520b2 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProTransfersWithHeader.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/account/CoinbaseProTransfersWithHeader.java @@ -1,35 +1,26 @@ package org.knowm.xchange.coinbasepro.dto.account; -import java.util.ArrayList; import java.util.List; +import lombok.Getter; +import lombok.ToString; import org.knowm.xchange.dto.account.FundingRecord; +@Getter +@ToString public class CoinbaseProTransfersWithHeader { - List fundingRecords = new ArrayList<>(); - String cbAfter; - String cbBefore; - public List getFundingRecords() { - return fundingRecords; - } + private final List fundingRecords; + private final String cbAfter; + private final String cbBefore; - public void setFundingRecords(List fundingRecords) { + public CoinbaseProTransfersWithHeader( + List fundingRecords, + String cbAfter, + String cbBefore + ) { this.fundingRecords = fundingRecords; - } - - public String getCbAfter() { - return cbAfter; - } - - public void setCbAfter(String cbAfter) { this.cbAfter = cbAfter; - } - - public String getCbBefore() { - return cbBefore; - } - - public void setCbBefore(String cbBefore) { this.cbBefore = cbBefore; } + } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrency.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrency.java index 89c92467a1c..189863080b1 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrency.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrency.java @@ -2,86 +2,134 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import java.util.List; +import lombok.Getter; +import lombok.ToString; +@ToString +@Getter public class CoinbaseProCurrency { - private final BigDecimal maxPrecision; - private final String name; - private final String minSize; - private final CoinbaseProCurrencyDetails details; private final String id; - private final Object message; + private final String name; + private final BigDecimal minSize; private final String status; + private final String message; + private final BigDecimal maxPrecision; + private final List convertibleTo; + private final CoinbaseProCurrencyDetails details; + private final String defaultNetwork; + private final List supportedNetworks; public CoinbaseProCurrency( - @JsonProperty("max_precision") BigDecimal maxPrecision, + @JsonProperty("id") String id, @JsonProperty("name") String name, - @JsonProperty("min_size") String minSize, + @JsonProperty("min_size") BigDecimal minSize, + @JsonProperty("status") String status, + @JsonProperty("message") String message, + @JsonProperty("max_precision") BigDecimal maxPrecision, + @JsonProperty("convertible_to") List convertibleTo, @JsonProperty("details") CoinbaseProCurrencyDetails details, - @JsonProperty("id") String id, - @JsonProperty("message") Object message, - @JsonProperty("status") String status) { - this.maxPrecision = maxPrecision; + @JsonProperty("default_network") String defaultNetwork, + @JsonProperty("supported_networks") List supportedNetworks + ) { + this.id = id; this.name = name; this.minSize = minSize; - this.details = details; - this.id = id; - this.message = message; this.status = status; + this.message = message; + this.maxPrecision = maxPrecision; + this.convertibleTo = convertibleTo; + this.details = details; + this.defaultNetwork = defaultNetwork; + this.supportedNetworks = supportedNetworks; } - public BigDecimal getMaxPrecision() { - return maxPrecision; - } - - public String getName() { - return name; - } - - public String getMinSize() { - return minSize; - } + @Getter + @ToString + public static class CoinbaseProCurrencyDetails { - public CoinbaseProCurrencyDetails getDetails() { - return details; - } + private final CoinbaseProCurrencyDetailsType type; + private final String symbol; + private final int networkConfirmations; + private final int sortOrder; + private final String cryptoAddressLink; + private final String cryptoTransactionLink; + private final List pushPaymentMethods; + private final List groupTypes; + private final String displayName; + private final BigDecimal processingTimeSeconds; + private final BigDecimal minWithdrawalAmount; + private final BigDecimal maxWithdrawalAmount; - public String getId() { - return id; - } + public CoinbaseProCurrencyDetails( + @JsonProperty("type") CoinbaseProCurrencyDetailsType type, + @JsonProperty("symbol") String symbol, + @JsonProperty("network_confirmations") int networkConfirmations, + @JsonProperty("sort_order") int sortOrder, + @JsonProperty("crypto_address_link") String cryptoAddressLink, + @JsonProperty("crypto_transaction_link") String cryptoTransactionLink, + @JsonProperty("push_payment_methods") List pushPaymentMethods, + @JsonProperty("group_types") List groupTypes, + @JsonProperty("display_name") String displayName, + @JsonProperty("processing_time_seconds") BigDecimal processingTimeSeconds, + @JsonProperty("min_withdrawal_amount") BigDecimal minWithdrawalAmount, + @JsonProperty("max_withdrawal_amount") BigDecimal maxWithdrawalAmount) { + this.type = type; + this.symbol = symbol; + this.networkConfirmations = networkConfirmations; + this.sortOrder = sortOrder; + this.cryptoAddressLink = cryptoAddressLink; + this.cryptoTransactionLink = cryptoTransactionLink; + this.pushPaymentMethods = pushPaymentMethods; + this.groupTypes = groupTypes; + this.displayName = displayName; + this.processingTimeSeconds = processingTimeSeconds; + this.minWithdrawalAmount = minWithdrawalAmount; + this.maxWithdrawalAmount = maxWithdrawalAmount; + } - public Object getMessage() { - return message; + public enum CoinbaseProCurrencyDetailsType { + crypto, + fiat + } } - public String getStatus() { - return status; - } + @Getter + @ToString + public static class CoinbaseCurrencySupportedNetwork { + private final String id; + private final String name; + private final String status; + private final String contractAddress; + private final String cryptoAddressLink; + private final String cryptoTransactionLink; + private final BigDecimal minWithdrawalAmount; + private final BigDecimal maxWithdrawalAmount; + private final Integer networkConfirmations; + private final BigDecimal processingTimeSeconds; - @Override - public String toString() { - return "CoinbaseProCurrency{" - + "max_precision = '" - + maxPrecision - + '\'' - + ",name = '" - + name - + '\'' - + ",min_size = '" - + minSize - + '\'' - + ",details = '" - + details - + '\'' - + ",id = '" - + id - + '\'' - + ",message = '" - + message - + '\'' - + ",status = '" - + status - + '\'' - + "}"; + public CoinbaseCurrencySupportedNetwork( + @JsonProperty("id") String id, + @JsonProperty("name") String name, + @JsonProperty("status") String status, + @JsonProperty("contract_address") String contractAddress, + @JsonProperty("crypto_address_link") String cryptoAddressLink, + @JsonProperty("crypto_transaction_link") String cryptoTransactionLink, + @JsonProperty("min_withdrawal_amount") BigDecimal minWithdrawalAmount, + @JsonProperty("max_withdrawal_amount") BigDecimal maxWithdrawalAmount, + @JsonProperty("network_confirmations") Integer networkConfirmations, + @JsonProperty("processing_time_seconds") BigDecimal processingTimeSeconds) { + this.id = id; + this.name = name; + this.status = status; + this.contractAddress = contractAddress; + this.cryptoAddressLink = cryptoAddressLink; + this.cryptoTransactionLink = cryptoTransactionLink; + this.minWithdrawalAmount = minWithdrawalAmount; + this.maxWithdrawalAmount = maxWithdrawalAmount; + this.networkConfirmations = networkConfirmations; + this.processingTimeSeconds = processingTimeSeconds; + } } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrencyDetails.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrencyDetails.java deleted file mode 100644 index 5d10a4218c3..00000000000 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProCurrencyDetails.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.knowm.xchange.coinbasepro.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.List; - -public class CoinbaseProCurrencyDetails { - - private final String symbol; - private final int networkConfirmations; - private final List pushPaymentMethods; - private final List groupTypes; - private final String cryptoAddressLink; - private final String type; - private final int sortOrder; - private final String cryptoTransactionLink; - private final BigDecimal minWithdrawalAmount; - - public CoinbaseProCurrencyDetails( - @JsonProperty("symbol") String symbol, - @JsonProperty("network_confirmations") int networkConfirmations, - @JsonProperty("push_payment_methods") List pushPaymentMethods, - @JsonProperty("group_types") List groupTypes, - @JsonProperty("crypto_address_link") String cryptoAddressLink, - @JsonProperty("type") String type, - @JsonProperty("sort_order") int sortOrder, - @JsonProperty("crypto_transaction_link") String cryptoTransactionLink, - @JsonProperty("min_withdrawal_amount") BigDecimal minWithdrawalAmount) { - this.symbol = symbol; - this.networkConfirmations = networkConfirmations; - this.pushPaymentMethods = pushPaymentMethods; - this.groupTypes = groupTypes; - this.cryptoAddressLink = cryptoAddressLink; - this.type = type; - this.sortOrder = sortOrder; - this.cryptoTransactionLink = cryptoTransactionLink; - this.minWithdrawalAmount = minWithdrawalAmount; - } - - public String getSymbol() { - return symbol; - } - - public int getNetworkConfirmations() { - return networkConfirmations; - } - - public List getPushPaymentMethods() { - return pushPaymentMethods; - } - - public List getGroupTypes() { - return groupTypes; - } - - public String getCryptoAddressLink() { - return cryptoAddressLink; - } - - public String getType() { - return type; - } - - public int getSortOrder() { - return sortOrder; - } - - public String getCryptoTransactionLink() { - return cryptoTransactionLink; - } - - public BigDecimal getMinWithdrawalAmount() { - return minWithdrawalAmount; - } - - @Override - public String toString() { - return "CoinbaseProCurrencyDetails{" - + "symbol = '" - + symbol - + '\'' - + ",network_confirmations = '" - + networkConfirmations - + '\'' - + ",push_payment_methods = '" - + pushPaymentMethods - + '\'' - + ",group_types = '" - + groupTypes - + '\'' - + ",crypto_address_link = '" - + cryptoAddressLink - + '\'' - + ",type = '" - + type - + '\'' - + ",sort_order = '" - + sortOrder - + '\'' - + ",crypto_transaction_link = '" - + cryptoTransactionLink - + '\'' - + ",min_withdrawal_amount = '" - + minWithdrawalAmount - + '\'' - + "}"; - } -} diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProProduct.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProProduct.java index d5b31de3739..44bb8f5b4da 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProProduct.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/marketdata/CoinbaseProProduct.java @@ -2,122 +2,75 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import lombok.Getter; +import lombok.ToString; +@ToString +@Getter public class CoinbaseProProduct { private final String id; private final String baseCurrency; - private final String targetCurrency; - private final BigDecimal baseMinSize; - private final BigDecimal baseMaxSize; - private final BigDecimal minMarketFunds; - private final BigDecimal maxMarketFunds; - private final BigDecimal baseIncrement; + private final String quoteCurrency; private final BigDecimal quoteIncrement; - private final boolean cancelOnly; - private final boolean limitOnly; + private final BigDecimal baseIncrement; + private final String displayName; + private final BigDecimal minMarketFunds; + private final boolean marginEnabled; private final boolean postOnly; + private final boolean limitOnly; + private final boolean cancelOnly; + private final CoinbaseProProductStatus status; + private final String statusMessage; private final boolean tradingDisabled; private final boolean fxStablecoin; - private final String status; + private final BigDecimal maxSlippagePercentage; + private final boolean auctionMode; + private final BigDecimal highBidLimitPercentage; public CoinbaseProProduct( @JsonProperty("id") String id, @JsonProperty("base_currency") String baseCurrency, - @JsonProperty("quote_currency") String targetCurrency, - @JsonProperty("base_min_size") BigDecimal baseMinSize, - @JsonProperty("base_max_size") BigDecimal baseMaxSize, - @JsonProperty("min_market_funds") BigDecimal minMarketFunds, - @JsonProperty("max_market_funds") BigDecimal maxMarketFunds, - @JsonProperty("base_increment") BigDecimal baseIncrement, + @JsonProperty("quote_currency") String quoteCurrency, @JsonProperty("quote_increment") BigDecimal quoteIncrement, - @JsonProperty("cancel_only") boolean cancelOnly, - @JsonProperty("limit_only") boolean limitOnly, + @JsonProperty("base_increment") BigDecimal baseIncrement, + @JsonProperty("display_name") String displayName, + @JsonProperty("min_market_funds") BigDecimal minMarketFunds, + @JsonProperty("margin_enabled") boolean marginEnabled, @JsonProperty("post_only") boolean postOnly, + @JsonProperty("limit_only") boolean limitOnly, + @JsonProperty("cancel_only") boolean cancelOnly, + @JsonProperty("status") CoinbaseProProductStatus status, + @JsonProperty("status_message") String statusMessage, @JsonProperty("trading_disabled") boolean tradingDisabled, @JsonProperty("fx_stablecoin") boolean fxStablecoin, - @JsonProperty("status") String status) { - + @JsonProperty("max_slippage_percentage") BigDecimal maxSlippagePercentage, + @JsonProperty("auction_mode") boolean auctionMode, + @JsonProperty("high_bid_limit_percentage") BigDecimal highBidLimitPercentage) { this.id = id; this.baseCurrency = baseCurrency; - this.targetCurrency = targetCurrency; - this.baseMinSize = baseMinSize; - this.baseMaxSize = baseMaxSize; - this.minMarketFunds = minMarketFunds; - this.maxMarketFunds = maxMarketFunds; - this.baseIncrement = baseIncrement; + this.quoteCurrency = quoteCurrency; this.quoteIncrement = quoteIncrement; - this.cancelOnly = cancelOnly; - this.limitOnly = limitOnly; + this.baseIncrement = baseIncrement; + this.displayName = displayName; + this.minMarketFunds = minMarketFunds; + this.marginEnabled = marginEnabled; this.postOnly = postOnly; + this.limitOnly = limitOnly; + this.cancelOnly = cancelOnly; + this.status = status; + this.statusMessage = statusMessage; this.tradingDisabled = tradingDisabled; this.fxStablecoin = fxStablecoin; - this.status = status; - } - - public String getId() { - - return id; - } - - public String getBaseCurrency() { - - return baseCurrency; - } - - public String getTargetCurrency() { - - return targetCurrency; - } - - public BigDecimal getBaseMinSize() { - - return baseMinSize; - } - - public BigDecimal getBaseMaxSize() { - - return baseMaxSize; - } - - public BigDecimal getBaseIncrement() { - return baseIncrement; - } - - public BigDecimal getQuoteIncrement() { - - return quoteIncrement; - } - - public BigDecimal getMinMarketFunds() { - return minMarketFunds; - } - - public BigDecimal getMaxMarketFunds() { - return maxMarketFunds; - } - - public boolean isCancelOnly() { - return cancelOnly; - } - - public boolean isLimitOnly() { - return limitOnly; - } - - public boolean isPostOnly() { - return postOnly; - } - - public boolean isTradingDisabled() { - return tradingDisabled; - } - - public boolean isFxStablecoin() { - return fxStablecoin; + this.maxSlippagePercentage = maxSlippagePercentage; + this.auctionMode = auctionMode; + this.highBidLimitPercentage = highBidLimitPercentage; } - public String getStatus() { - return status; + public enum CoinbaseProProductStatus { + online, + offline, + internal, + delisted } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFill.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFill.java index ce5f55584aa..9eb2bda1a15 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFill.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFill.java @@ -2,111 +2,72 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +@Getter +@ToString +@Builder public class CoinbaseProFill { private final String tradeId; private final String productId; + private final String orderId; + private final String userId; + private final String profileId; + private final Liquidity liquidity; private final BigDecimal price; private final BigDecimal size; - private final String orderId; - private final String createdAt; - private final String liquidity; private final BigDecimal fee; + private final String createdAt; + private final Side side; private final boolean settled; - private final String side; + private final String usdVolume; + private final String marketType; + private final String fundingCurrency; public CoinbaseProFill( @JsonProperty("trade_id") String tradeId, @JsonProperty("product_id") String productId, + @JsonProperty("order_id") String orderId, + @JsonProperty("user_id") String userId, + @JsonProperty("profile_id") String profileId, + @JsonProperty("liquidity") Liquidity liquidity, @JsonProperty("price") BigDecimal price, @JsonProperty("size") BigDecimal size, - @JsonProperty("order_id") String orderId, - @JsonProperty("created_at") String createdAt, - @JsonProperty("liquidity") String liquidity, @JsonProperty("fee") BigDecimal fee, + @JsonProperty("created_at") String createdAt, + @JsonProperty("side") Side side, @JsonProperty("settled") boolean settled, - @JsonProperty("side") String side) { + @JsonProperty("usd_volume") String usdVolume, + @JsonProperty("market_type") String marketType, + @JsonProperty("funding_currency") String fundingCurrency + ) { this.tradeId = tradeId; this.productId = productId; - this.price = price; - this.size = size; this.orderId = orderId; - this.createdAt = createdAt; + this.userId = userId; + this.profileId = profileId; this.liquidity = liquidity; + this.price = price; + this.size = size; this.fee = fee; - this.settled = settled; + this.createdAt = createdAt; this.side = side; + this.settled = settled; + this.usdVolume = usdVolume; + this.marketType = marketType; + this.fundingCurrency = fundingCurrency; } - public String getTradeId() { - return tradeId; - } - - public String getProductId() { - return productId; - } - - public BigDecimal getPrice() { - return price; - } - - public BigDecimal getSize() { - return size; - } - - public String getOrderId() { - return orderId; - } - - public String getCreatedAt() { - return createdAt; - } - - public String getLiquidity() { - return liquidity; - } - - public BigDecimal getFee() { - return fee; - } - - public boolean isSettled() { - return settled; - } - - public String getSide() { - return side; + public enum Liquidity { + M, + T, + O } - @Override - public String toString() { - return "CoinbaseExFill{" - + "tradeId='" - + tradeId - + '\'' - + ", productId='" - + productId - + '\'' - + ", price=" - + price - + ", size=" - + size - + ", orderId='" - + orderId - + '\'' - + ", createdAt='" - + createdAt - + '\'' - + ", liquidity='" - + liquidity - + '\'' - + ", fee=" - + fee - + ", settled=" - + settled - + ", side='" - + side - + '\'' - + '}'; + public enum Side { + buy, + sell } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProTradeHistoryParams.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProTradeHistoryParams.java index a53d5c2c122..99beaa34c3f 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProTradeHistoryParams.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProTradeHistoryParams.java @@ -1,19 +1,27 @@ package org.knowm.xchange.coinbasepro.dto.trade; +import lombok.Getter; +import lombok.Setter; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.trade.params.HistoryParamsFundingType; import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamInstrument; import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit; import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; +@Getter +@Setter public class CoinbaseProTradeHistoryParams implements TradeHistoryParamTransactionId, TradeHistoryParamCurrencyPair, + TradeHistoryParamInstrument, TradeHistoryParamLimit, HistoryParamsFundingType { private CurrencyPair currencyPair; + private Instrument instrument; private String txId; private Integer afterTradeId; private Integer beforeTradeId; @@ -22,38 +30,6 @@ public class CoinbaseProTradeHistoryParams private Integer limit; private Type type; - public Integer getAfterTradeId() { - return afterTradeId; - } - - public String getAfterTransferId() { - return afterTransferId; - } - - public void setAfterTransferId(String afterTransferId) { - this.afterTransferId = afterTransferId; - } - - public String getBeforeTransferId() { - return beforeTransferId; - } - - public void setBeforeTransferId(String beforeTransferId) { - this.beforeTransferId = beforeTransferId; - } - - public void setAfterTradeId(Integer startingOrderId) { - this.afterTradeId = startingOrderId; - } - - public Integer getBeforeTradeId() { - return beforeTradeId; - } - - public void setBeforeTradeId(Integer beforeTradeId) { - this.beforeTradeId = beforeTradeId; - } - @Override public CurrencyPair getCurrencyPair() { return currencyPair; @@ -93,4 +69,14 @@ public Type getType() { public void setType(Type type) { this.type = type; } + + @Override + public Instrument getInstrument() { + return instrument; + } + + @Override + public void setInstrument(Instrument instrument) { + this.instrument = instrument; + } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccount.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWallet.java similarity index 66% rename from xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccount.java rename to xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWallet.java index b1cb31f79ef..1e68706f048 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccount.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWallet.java @@ -2,8 +2,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import lombok.Getter; +import lombok.ToString; -public class CoinbaseProAccount { +@ToString +@Getter +public class CoinbaseProWallet { private final String name; private final boolean primaryAccount; @@ -13,7 +17,7 @@ public class CoinbaseProAccount { private final String type; private final String id; - public CoinbaseProAccount( + public CoinbaseProWallet( @JsonProperty("name") String name, @JsonProperty("primary") boolean primaryAccount, @JsonProperty("currency") String currency, @@ -29,32 +33,4 @@ public CoinbaseProAccount( this.type = type; this.id = id; } - - public String getName() { - return name; - } - - public boolean isPrimaryAccount() { - return primaryAccount; - } - - public String getCurrency() { - return currency; - } - - public boolean isActive() { - return active; - } - - public BigDecimal getBalance() { - return balance; - } - - public String getType() { - return type; - } - - public String getId() { - return id; - } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccountAddress.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWalletAddress.java similarity index 96% rename from xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccountAddress.java rename to xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWalletAddress.java index b504879748e..9d97dbedb1b 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProAccountAddress.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProWalletAddress.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public class CoinbaseProAccountAddress { +public class CoinbaseProWalletAddress { private final String resource; private final String network; @@ -16,7 +16,7 @@ public class CoinbaseProAccountAddress { private final String name; private final String destinationTag; - public CoinbaseProAccountAddress( + public CoinbaseProWalletAddress( @JsonProperty("resource") String resource, @JsonProperty("network") String network, @JsonProperty("exchange_deposit_address") boolean exchangeDepositAddress, diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountService.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountService.java index decd75831bd..f031227ace4 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountService.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountService.java @@ -7,23 +7,24 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.coinbasepro.CoinbaseProAdapters; import org.knowm.xchange.coinbasepro.CoinbaseProExchange; -import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfer; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfers; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFee; -import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProTransfersWithHeader; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccount; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccountAddress; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFundingHistoryParams; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProLedger; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWallet; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWalletAddress; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProSendMoneyResponse; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProTradeHistoryParams; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AccountInfo; import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.dto.account.params.FundingRecordParamAll; import org.knowm.xchange.exceptions.ExchangeException; import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.account.AccountService; @@ -35,6 +36,9 @@ public class CoinbaseProAccountService extends CoinbaseProAccountServiceRaw implements AccountService { + private static final String CB_AFTER_HEADER = "Cb-After"; + private static final String CB_BEFORE_HEADER = "Cb-Before"; + public CoinbaseProAccountService( CoinbaseProExchange exchange, ResilienceRegistries resilienceRegistries) { @@ -43,7 +47,7 @@ public CoinbaseProAccountService( @Override public AccountInfo getAccountInfo() throws IOException { - return new AccountInfo(CoinbaseProAdapters.adaptAccountInfo(getCoinbaseProAccountInfo())); + return new AccountInfo(CoinbaseProAdapters.adaptWallet(getCoinbaseProAccountInfo())); } @Override @@ -86,10 +90,10 @@ public String withdrawFunds(WithdrawFundsParams params) throws IOException { } public String moveFunds(Currency currency, String address, BigDecimal amount) throws IOException { - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount[] accounts = + CoinbaseProAccount[] accounts = getCoinbaseProAccountInfo(); String accountId = null; - for (org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount account : accounts) { + for (CoinbaseProAccount account : accounts) { if (currency.getCurrencyCode().equals(account.getCurrency())) { accountId = account.getId(); } @@ -108,12 +112,12 @@ public String moveFunds(Currency currency, String address, BigDecimal amount) th return null; } - private CoinbaseProAccountAddress accountAddress(Currency currency, String... args) + private CoinbaseProWalletAddress accountAddress(Currency currency) throws IOException { - CoinbaseProAccount[] coinbaseAccounts = getCoinbaseAccounts(); - CoinbaseProAccount depositAccount = null; + CoinbaseProWallet[] coinbaseAccounts = getCoinbaseAccounts(); + CoinbaseProWallet depositAccount = null; - for (CoinbaseProAccount account : coinbaseAccounts) { + for (CoinbaseProWallet account : coinbaseAccounts) { Currency accountCurrency = Currency.getInstance(account.getCurrency()); if (account.isActive() && "wallet".equals(account.getType()) @@ -123,69 +127,136 @@ private CoinbaseProAccountAddress accountAddress(Currency currency, String... ar } } + assert depositAccount != null; return getCoinbaseAccountAddress(depositAccount.getId()); } @Deprecated @Override public String requestDepositAddress(Currency currency, String... args) throws IOException { - return accountAddress(currency, args).getAddress(); + return accountAddress(currency).getAddress(); } @Override public AddressWithTag requestDepositAddressData(Currency currency, String... args) throws IOException { - CoinbaseProAccountAddress depositAddress = accountAddress(currency, args); + CoinbaseProWalletAddress depositAddress = accountAddress(currency); return new AddressWithTag(depositAddress.getAddress(), depositAddress.getDestinationTag()); } @Override public TradeHistoryParams createFundingHistoryParams() { - return new CoinbaseProTradeHistoryParams(); + return new CoinbaseProFundingHistoryParams(); } - public CoinbaseProTransfersWithHeader getTransfersWithPagination(TradeHistoryParams params) - throws IOException { + @Override + /* + * Warning - this method makes several API calls. The reason is that the paging functionality + * isn't implemented properly yet. + * + *

It honours TradeHistoryParamCurrency for filtering to a single ccy. + */ + public List getFundingHistory(TradeHistoryParams params) throws IOException { - String fundingRecordType; + String fundingRecordType = null; if (params instanceof HistoryParamsFundingType && ((HistoryParamsFundingType) params).getType() != null) { - FundingRecord.Type type = ((HistoryParamsFundingType) params).getType(); - fundingRecordType = type == FundingRecord.Type.WITHDRAWAL ? "withdraw" : "deposit"; - } else { - throw new ExchangeException( - "Type 'deposit' or 'withdraw' must be supplied using FundingRecord.Type"); + fundingRecordType = ((HistoryParamsFundingType) params).getType().toString().toLowerCase(); } + + int maxPageSize = 100; + + List fundingHistory = new ArrayList<>(); + + String createdAt = null; // use to get next page + while (true) { + String createdAtFinal = createdAt; + CoinbaseProTransfers transfers = + getTransfers(fundingRecordType, null, null, createdAtFinal, maxPageSize); + + fundingHistory.addAll( + transfers.stream() + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (transfers.size() < maxPageSize) { + break; + } + + createdAt = transfers.getHeader(CB_AFTER_HEADER); + } + + return fundingHistory; + } + + @Override + public AccountInfo getSubAccountInfo(String subAccountId) throws IOException { + return new AccountInfo(CoinbaseProAdapters.adaptWallet(getCoinbaseProAccountById(subAccountId))); + } + + @Override + public List getInternalTransferHistory(FundingRecordParamAll params) + throws IOException { + String beforeItem = ""; String afterItem = ""; int maxPageSize = 100; - if (params instanceof CoinbaseProTradeHistoryParams) { - maxPageSize = ((CoinbaseProTradeHistoryParams) params).getLimit(); - afterItem = ((CoinbaseProTradeHistoryParams) params).getAfterTransferId(); - beforeItem = ((CoinbaseProTradeHistoryParams) params).getBeforeTransferId(); - } List fundingHistory = new ArrayList<>(); - Map accountToCurrencyMap = - Stream.of(getCoinbaseProAccountInfo()) - .collect( - Collectors.toMap( - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount::getId, - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount::getCurrency)); - while (true) { CoinbaseProTransfers transfers = - transfers(fundingRecordType, null, beforeItem, afterItem, maxPageSize); + getTransfers( + null, + null, + beforeItem, + afterItem, + maxPageSize); + + fundingHistory.addAll( + transfers.stream() + .filter(transfer -> transfer.getType().equals(Type.INTERNAL_DEPOSIT) || transfer.getType().equals(Type.INTERNAL_WITHDRAW)) + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (!transfers.isEmpty()) { + afterItem = transfers.getHeader(CB_AFTER_HEADER); + beforeItem = transfers.getHeader(CB_BEFORE_HEADER); + } - for (CoinbaseProTransfer coinbaseProTransfer : transfers) { - Currency currency = - Currency.getInstance(accountToCurrencyMap.get(coinbaseProTransfer.getAccountId())); - fundingHistory.add(CoinbaseProAdapters.adaptFundingRecord(currency, coinbaseProTransfer)); + if (transfers.size() < maxPageSize) { + break; } - if (transfers.size() > 0) { - afterItem = transfers.getHeader("Cb-After"); - beforeItem = transfers.getHeader("Cb-Before"); + } + + return fundingHistory; + } + + @Override + public List getWithdrawHistory(FundingRecordParamAll params) throws IOException { + String beforeItem = ""; + String afterItem = ""; + int maxPageSize = 100; + + List fundingHistory = new ArrayList<>(); + + while (true) { + CoinbaseProTransfers transfers = + getTransfers( + Type.WITHDRAW.name().toLowerCase(), + null, + beforeItem, + afterItem, + maxPageSize); + + fundingHistory.addAll( + transfers.stream() + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (!transfers.isEmpty()) { + afterItem = transfers.getHeader(CB_AFTER_HEADER); + beforeItem = transfers.getHeader(CB_BEFORE_HEADER); } if (transfers.size() < maxPageSize) { @@ -193,65 +264,161 @@ public CoinbaseProTransfersWithHeader getTransfersWithPagination(TradeHistoryPar } } - CoinbaseProTransfersWithHeader allTransfers = new CoinbaseProTransfersWithHeader(); - allTransfers.setCbAfter(afterItem); - allTransfers.setCbBefore(beforeItem); - allTransfers.setFundingRecords(fundingHistory); - return allTransfers; + return fundingHistory; } @Override - /* - * Warning - this method makes several API calls. The reason is that the paging functionality - * isn't implemented properly yet. - * - *

It honours TradeHistoryParamCurrency for filtering to a single ccy. - */ - public List getFundingHistory(TradeHistoryParams params) throws IOException { + public List getSubAccountDepositHistory(FundingRecordParamAll params) + throws IOException { + String beforeItem = ""; + String afterItem = ""; + int maxPageSize = 100; - String fundingRecordType; - if (params instanceof HistoryParamsFundingType - && ((HistoryParamsFundingType) params).getType() != null) { - FundingRecord.Type type = ((HistoryParamsFundingType) params).getType(); - fundingRecordType = type == FundingRecord.Type.WITHDRAWAL ? "withdraw" : "deposit"; - } else { - throw new ExchangeException( - "Type 'deposit' or 'withdraw' must be supplied using FundingRecord.Type"); + if(params.getSubAccountId() == null || params.getSubAccountId().isEmpty()){ + throw new IllegalArgumentException("You must provide subAccountId for this call."); } + List fundingHistory = new ArrayList<>(); + + while (true) { + CoinbaseProTransfers transfers = + getTransfersByAccountId( + params.getSubAccountId(), + beforeItem, + afterItem, + maxPageSize, + Type.DEPOSIT.name().toLowerCase()); + + fundingHistory.addAll( + transfers.stream() + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (!transfers.isEmpty()) { + afterItem = transfers.getHeader(CB_AFTER_HEADER); + beforeItem = transfers.getHeader(CB_BEFORE_HEADER); + } + + if (transfers.size() < maxPageSize) { + break; + } + } + + return fundingHistory; + } + + @Override + public List getDepositHistory(FundingRecordParamAll params) throws IOException { + String beforeItem = ""; + String afterItem = ""; int maxPageSize = 100; List fundingHistory = new ArrayList<>(); - Map accountToCurrencyMap = - Stream.of(getCoinbaseProAccountInfo()) - .collect( - Collectors.toMap( - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount::getId, - org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount::getCurrency)); - - String createdAt = null; // use to get next page while (true) { - String createdAtFinal = createdAt; CoinbaseProTransfers transfers = - transfers(fundingRecordType, null, null, createdAtFinal, maxPageSize); - - for (CoinbaseProTransfer coinbaseProTransfer : transfers) { - Currency currency = - Currency.getInstance(accountToCurrencyMap.get(coinbaseProTransfer.getAccountId())); - fundingHistory.add(CoinbaseProAdapters.adaptFundingRecord(currency, coinbaseProTransfer)); + getTransfers( + Type.DEPOSIT.name().toLowerCase(), + null, + beforeItem, + afterItem, + maxPageSize); + + fundingHistory.addAll( + transfers.stream() + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (!transfers.isEmpty()) { + afterItem = transfers.getHeader(CB_AFTER_HEADER); + beforeItem = transfers.getHeader(CB_BEFORE_HEADER); } if (transfers.size() < maxPageSize) { break; } + } + + return fundingHistory; + } - createdAt = transfers.getHeader("Cb-After"); + @Override + public List getWalletTransferHistory(FundingRecordParamAll params) + throws IOException { + String beforeItem = ""; + String afterItem = ""; + int maxPageSize = 100; + + if(params.getSubAccountId() == null || params.getSubAccountId().isEmpty()){ + throw new IllegalArgumentException("You must provide subAccountId for this call."); + } + + List fundingHistory = new ArrayList<>(); + + while (true) { + CoinbaseProTransfers transfers = + getTransfersByAccountId( + params.getSubAccountId(), + beforeItem, + afterItem, + maxPageSize, + null); + + fundingHistory.addAll( + transfers.stream() + .map(CoinbaseProAdapters::adaptFundingRecord) + .collect(Collectors.toList())); + + if (!transfers.isEmpty()) { + afterItem = transfers.getHeader(CB_AFTER_HEADER); + beforeItem = transfers.getHeader(CB_BEFORE_HEADER); + } + + if (transfers.size() < maxPageSize) { + break; + } } return fundingHistory; } + @Override + public List getLedger(FundingRecordParamAll params) throws IOException { + int maxPageSize = 100; + + if(params.getSubAccountId() == null || params.getSubAccountId().isEmpty()) { + throw new IOException("You must provide subAccountId for this call."); + } + + List ledgerList = new ArrayList<>(); + + String createdAt = null; + while (true) { + String createdAtFinal = createdAt; + + CoinbaseProLedger ledger = + getLedger( + params.getSubAccountId(), + params.getStartTime(), + params.getEndTime(), + null, + createdAtFinal, + params.getLimit(), + null + ); + + ledgerList.addAll(CoinbaseProAdapters.adaptCoinbaseProLedger(ledger)); + + if (ledger.size() < maxPageSize) { + break; + } + + createdAt = ledger.getHeader(CB_AFTER_HEADER); + } + + return ledgerList; + } + public static class CoinbaseProMoveFundsParams implements WithdrawFundsParams { public final Currency currency; public final BigDecimal amount; diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountServiceRaw.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountServiceRaw.java index 39d0831ef92..94f7e18bfb8 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountServiceRaw.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProAccountServiceRaw.java @@ -2,28 +2,27 @@ import static org.knowm.xchange.coinbasepro.CoinbaseProResilience.PRIVATE_REST_ENDPOINT_RATE_LIMITER; -import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.math.BigDecimal; -import java.util.List; +import java.util.Date; import java.util.Map; import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.coinbasepro.CoinbasePro; import org.knowm.xchange.coinbasepro.CoinbaseProExchange; import org.knowm.xchange.coinbasepro.dto.CoinbaseProException; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfers; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFee; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProLedger; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProSendMoneyRequest; -import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWebsocketAuthData; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWithdrawCryptoResponse; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWithdrawFundsRequest; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccount; -import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProAccountAddress; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWallet; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProWalletAddress; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProSendMoneyResponse; import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.utils.DateUtils; import org.knowm.xchange.utils.timestamp.UnixTimestampFactory; -import si.mazi.rescu.ParamsDigest; -import si.mazi.rescu.RestInvocation; public class CoinbaseProAccountServiceRaw extends CoinbaseProBaseService { @@ -32,7 +31,7 @@ public CoinbaseProAccountServiceRaw( super(exchange, resilienceRegistries); } - public org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount[] getCoinbaseProAccountInfo() + public CoinbaseProAccount[] getCoinbaseProAccountInfo() throws CoinbaseProException, IOException { return decorateApiCall( () -> @@ -42,6 +41,16 @@ public org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount[] getCoinbas .call(); } + public CoinbaseProAccount getCoinbaseProAccountById(String accountId) + throws CoinbaseProException, IOException { + return decorateApiCall( + () -> + coinbasePro.getAccountById( + apiKey, digest, UnixTimestampFactory.INSTANCE.createValue(), passphrase, accountId)) + .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) + .call(); + } + /** https://docs.pro.coinbase.com/#fees */ public CoinbaseProFee getCoinbaseProFees() throws CoinbaseProException, IOException { return decorateApiCall( @@ -94,7 +103,7 @@ public CoinbaseProWithdrawCryptoResponse withdrawCrypto( } /** https://docs.pro.coinbase.com/#get-an-account */ - public List> ledger(String accountId, String startingOrderId) throws IOException { + public CoinbaseProLedger getLedger(String accountId, Date startDate, Date endDate, String beforeId, String afterId, Integer limit, String profileId) throws CoinbaseProException, IOException { return decorateApiCall( () -> coinbasePro.ledger( @@ -103,7 +112,12 @@ public CoinbaseProWithdrawCryptoResponse withdrawCrypto( UnixTimestampFactory.INSTANCE.createValue(), passphrase, accountId, - startingOrderId)) + (startDate == null) ? null : DateUtils.toISODateString(startDate), + (endDate == null) ? null : DateUtils.toISODateString(endDate), + beforeId, + afterId, + limit, + profileId)) .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) .call(); } @@ -141,56 +155,57 @@ public String requestNewReport(CoinbasePro.CoinbaseProReportRequest reportReques } /** https://docs.pro.coinbase.com/#get-current-exchange-limits */ - public CoinbaseProTransfers transfers(String accountId, String profileId, int limit, String after) - throws IOException { + public CoinbaseProTransfers getTransfersByAccountId(String accountId, String before, String after, int limit, String type) + throws CoinbaseProException, IOException { return decorateApiCall( () -> - coinbasePro.transfers( + coinbasePro.getTransfersByAccountId( apiKey, digest, UnixTimestampFactory.INSTANCE.createValue(), passphrase, accountId, - profileId, + before, + after, limit, - after)) + type)) .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) .call(); } /** https://docs.pro.coinbase.com/#get-current-exchange-limits */ - public CoinbaseProTransfers transfers( - String type, String profileId, String before, String after, int limit) throws IOException { - return decorateApiCall( - () -> - coinbasePro.transfers( - apiKey, - digest, - UnixTimestampFactory.INSTANCE.createValue(), - passphrase, - type, - profileId, - before, - after, - limit)) - .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) - .call(); + public CoinbaseProTransfers getTransfers(String type, String profileId, String before, String after, int limit) throws CoinbaseProException, IOException { + + return decorateApiCall( + () -> + coinbasePro.getTransfers( + apiKey, + digest, + UnixTimestampFactory.INSTANCE.createValue(), + passphrase, + type, + profileId, + before, + after, + limit)) + .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) + .call(); } /** https://docs.pro.coinbase.com/#coinbase-accounts */ - public CoinbaseProAccount[] getCoinbaseAccounts() throws IOException { + public CoinbaseProWallet[] getCoinbaseAccounts() throws IOException { return decorateApiCall( () -> - coinbasePro.getCoinbaseProAccounts( + coinbasePro.getCoinbaseProWallets( apiKey, digest, UnixTimestampFactory.INSTANCE.createValue(), passphrase)) .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) .call(); } - public CoinbaseProAccountAddress getCoinbaseAccountAddress(String accountId) throws IOException { + public CoinbaseProWalletAddress getCoinbaseAccountAddress(String accountId) throws IOException { return decorateApiCall( () -> - coinbasePro.getCoinbaseProAccountAddress( + coinbasePro.getCoinbaseProWalletAddress( apiKey, digest, UnixTimestampFactory.INSTANCE.createValue(), @@ -199,31 +214,4 @@ public CoinbaseProAccountAddress getCoinbaseAccountAddress(String accountId) thr .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) .call(); } - - public CoinbaseProWebsocketAuthData getWebsocketAuthData() - throws CoinbaseProException, IOException { - long timestamp = UnixTimestampFactory.INSTANCE.createValue(); - WebhookAuthDataParamsDigestProxy digestProxy = new WebhookAuthDataParamsDigestProxy(); - JsonNode json = - decorateApiCall(() -> coinbasePro.getVerifyId(apiKey, digestProxy, timestamp, passphrase)) - .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) - .call(); - String userId = json.get("id").asText(); - return new CoinbaseProWebsocketAuthData( - userId, apiKey, passphrase, digestProxy.getSignature(), timestamp); - } - - private class WebhookAuthDataParamsDigestProxy implements ParamsDigest { - private String signature; - - @Override - public String digestParams(RestInvocation restInvocation) { - signature = digest.digestParams(restInvocation); - return signature; - } - - public String getSignature() { - return signature; - } - } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProBaseService.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProBaseService.java index 66ba5c6364e..2ab9d1209da 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProBaseService.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProBaseService.java @@ -1,16 +1,26 @@ package org.knowm.xchange.coinbasepro.service; +import static org.knowm.xchange.coinbasepro.CoinbaseProResilience.PRIVATE_REST_ENDPOINT_RATE_LIMITER; + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import lombok.SneakyThrows; import org.knowm.xchange.client.ExchangeRestProxyBuilder; +import org.knowm.xchange.client.ProxyConfig; import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.coinbasepro.CoinbasePro; import org.knowm.xchange.coinbasepro.CoinbaseProExchange; import org.knowm.xchange.coinbasepro.dto.CoinbaseProException; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWebsocketAuthData; import org.knowm.xchange.exceptions.ExchangeException; import org.knowm.xchange.exceptions.FundsExceededException; import org.knowm.xchange.exceptions.InternalServerException; import org.knowm.xchange.exceptions.RateLimitExceededException; import org.knowm.xchange.service.BaseResilientExchangeService; +import org.knowm.xchange.utils.timestamp.UnixTimestampFactory; import si.mazi.rescu.ParamsDigest; +import si.mazi.rescu.RestInvocation; public class CoinbaseProBaseService extends BaseResilientExchangeService { @@ -20,21 +30,24 @@ public class CoinbaseProBaseService extends BaseResilientExchangeService coinbasePro.getVerifyId(apiKey, digestProxy, timestamp, passphrase)) + .withRateLimiter(rateLimiter(PRIVATE_REST_ENDPOINT_RATE_LIMITER)) + .call(); + String userId = json.get("id").asText(); + return new CoinbaseProWebsocketAuthData( + userId, apiKey, passphrase, digestProxy.getSignature(), timestamp); + } + + private class WebhookAuthDataParamsDigestProxy implements ParamsDigest { + private String signature; + + @Override + public String digestParams(RestInvocation restInvocation) { + signature = digest.digestParams(restInvocation); + return signature; + } + + public String getSignature() { + return signature; + } + } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataService.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataService.java index 4e2f3db8c6b..bcf2c03d780 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataService.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataService.java @@ -2,17 +2,22 @@ import java.io.IOException; import java.util.List; +import java.util.Map; import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.coinbasepro.CoinbaseProAdapters; import org.knowm.xchange.coinbasepro.CoinbaseProExchange; import org.knowm.xchange.coinbasepro.dto.CoinbaseProTrades; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductStats; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductTicker; +import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; import org.knowm.xchange.dto.marketdata.Trades; +import org.knowm.xchange.dto.meta.CurrencyMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.exceptions.RateLimitExceededException; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.marketdata.MarketDataService; import org.knowm.xchange.service.marketdata.params.Params; import org.slf4j.Logger; @@ -93,38 +98,48 @@ public Trades getTrades(CurrencyPair currencyPair, Object... args) log.debug("fromTradeId: {}, toTradeId: {}", fromTradeId, toTradeId); Long latestTradeId = toTradeId; - CoinbaseProTrades CoinbaseProTrades = new CoinbaseProTrades(); + CoinbaseProTrades coinbaseProTrades = new CoinbaseProTrades(); for (; ; ) { - CoinbaseProTrades CoinbaseProTradesNew = + CoinbaseProTrades coinbaseProTradesNew = getCoinbaseProTradesExtended(currencyPair, latestTradeId, 100); - CoinbaseProTrades.addAll(CoinbaseProTradesNew); + coinbaseProTrades.addAll(coinbaseProTradesNew); log.debug( "latestTradeId: {}, earliest-latest: {}-{}, trades: {}", latestTradeId, - CoinbaseProTrades.getEarliestTradeId(), - CoinbaseProTrades.getLatestTradeId(), - CoinbaseProTrades); - latestTradeId = CoinbaseProTrades.getEarliestTradeId(); + coinbaseProTrades.getEarliestTradeId(), + coinbaseProTrades.getLatestTradeId(), + coinbaseProTrades); + latestTradeId = coinbaseProTrades.getEarliestTradeId(); - if (CoinbaseProTradesNew.getEarliestTradeId() == null) { + if (coinbaseProTradesNew.getEarliestTradeId() == null) { break; } - if (CoinbaseProTrades.getEarliestTradeId() <= fromTradeId) { + if (coinbaseProTrades.getEarliestTradeId() <= fromTradeId) { break; } } log.debug( "earliest-latest: {}-{}", - CoinbaseProTrades.getEarliestTradeId(), - CoinbaseProTrades.getLatestTradeId()); + coinbaseProTrades.getEarliestTradeId(), + coinbaseProTrades.getLatestTradeId()); - CoinbaseProTrades.forEach(s -> log.debug(s.toString())); + coinbaseProTrades.forEach(s -> log.debug(s.toString())); - return CoinbaseProAdapters.adaptTrades(CoinbaseProTrades, currencyPair); + return CoinbaseProAdapters.adaptTrades(coinbaseProTrades, currencyPair); } throw new IllegalArgumentException("Invalid arguments passed to getTrades"); } + + @Override + public Map getCurrencies() throws IOException { + return CoinbaseProAdapters.adaptCoinbaseProCurrencies(getCoinbaseProCurrencies()); + } + + @Override + public Map getInstruments() throws IOException { + return CoinbaseProAdapters.adaptCoinbaseProCurrencyPairs(getCoinbaseProProducts()); + } } diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataServiceRaw.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataServiceRaw.java index 578e2984366..713563b73f4 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataServiceRaw.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProMarketDataServiceRaw.java @@ -29,7 +29,7 @@ public CoinbaseProMarketDataServiceRaw( /** https://docs.pro.coinbase.com/#get-product-ticker */ public CoinbaseProProductTicker getCoinbaseProProductTicker(CurrencyPair currencyPair) - throws IOException { + throws CoinbaseProException, IOException { if (!checkProductExists(currencyPair)) { throw new InstrumentNotValidException("Pair does not exist on CoinbasePro"); @@ -48,7 +48,7 @@ public CoinbaseProProductTicker getCoinbaseProProductTicker(CurrencyPair currenc /** https://docs.pro.coinbase.com/#get-24hr-stats */ public CoinbaseProProductStats getCoinbaseProProductStats(CurrencyPair currencyPair) - throws IOException { + throws CoinbaseProException, IOException { if (!checkProductExists(currencyPair)) { throw new InstrumentNotValidException("Pair does not exist on CoinbasePro"); @@ -65,7 +65,7 @@ public CoinbaseProProductStats getCoinbaseProProductStats(CurrencyPair currencyP } } - public Map getCoinbaseProStats() throws IOException { + public Map getCoinbaseProStats() throws CoinbaseProException, IOException { try { return decorateApiCall(coinbasePro::getStats) .withRateLimiter(rateLimiter(PUBLIC_REST_ENDPOINT_RATE_LIMITER)) @@ -77,7 +77,7 @@ public Map getCoinbaseProStats() throws IOException { /** https://docs.pro.coinbase.com/#get-product-order-book */ public CoinbaseProProductBook getCoinbaseProProductOrderBook(CurrencyPair currencyPair, int level) - throws IOException { + throws CoinbaseProException, IOException { try { return decorateApiCall( @@ -94,7 +94,7 @@ public CoinbaseProProductBook getCoinbaseProProductOrderBook(CurrencyPair curren } /** https://docs.pro.coinbase.com/#get-trades */ - public CoinbaseProTrade[] getCoinbaseProTrades(CurrencyPair currencyPair) throws IOException { + public CoinbaseProTrade[] getCoinbaseProTrades(CurrencyPair currencyPair) throws CoinbaseProException, IOException { try { return decorateApiCall( () -> @@ -109,7 +109,7 @@ public CoinbaseProTrade[] getCoinbaseProTrades(CurrencyPair currencyPair) throws /** https://docs.pro.coinbase.com/#get-historic-rates */ public CoinbaseProCandle[] getCoinbaseProHistoricalCandles( - CurrencyPair currencyPair, String start, String end, String granularity) throws IOException { + CurrencyPair currencyPair, String start, String end, String granularity) throws CoinbaseProException, IOException { try { return decorateApiCall( @@ -128,7 +128,7 @@ public CoinbaseProCandle[] getCoinbaseProHistoricalCandles( } /** https://docs.pro.coinbase.com/#get-products */ - public CoinbaseProProduct[] getCoinbaseProProducts() throws IOException { + public CoinbaseProProduct[] getCoinbaseProProducts() throws CoinbaseProException, IOException { try { return decorateApiCall(coinbasePro::getProducts) .withRateLimiter(rateLimiter(PUBLIC_REST_ENDPOINT_RATE_LIMITER)) @@ -139,7 +139,7 @@ public CoinbaseProProduct[] getCoinbaseProProducts() throws IOException { } /** https://docs.pro.coinbase.com/#get-currencies */ - public CoinbaseProCurrency[] getCoinbaseProCurrencies() throws IOException { + public CoinbaseProCurrency[] getCoinbaseProCurrencies() throws CoinbaseProException, IOException { try { return decorateApiCall(coinbasePro::getCurrencies) .withRateLimiter(rateLimiter(PUBLIC_REST_ENDPOINT_RATE_LIMITER)) @@ -151,7 +151,7 @@ public CoinbaseProCurrency[] getCoinbaseProCurrencies() throws IOException { /** https://docs.pro.coinbase.com/#get-trades */ public CoinbaseProTrades getCoinbaseProTradesExtended( - CurrencyPair currencyPair, Long after, Integer limit) throws IOException { + CurrencyPair currencyPair, Long after, Integer limit) throws CoinbaseProException, IOException { return decorateApiCall( () -> coinbasePro.getTradesPageable( diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeService.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeService.java index f2dcee20e0c..c0212bf2720 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeService.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeService.java @@ -7,7 +7,9 @@ import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.coinbasepro.CoinbaseProAdapters; import org.knowm.xchange.coinbasepro.CoinbaseProExchange; +import org.knowm.xchange.coinbasepro.dto.CoinbaseProException; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProTradeHistoryParams; +import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order; import org.knowm.xchange.dto.trade.LimitOrder; import org.knowm.xchange.dto.trade.MarketOrder; @@ -15,14 +17,21 @@ import org.knowm.xchange.dto.trade.StopOrder; import org.knowm.xchange.dto.trade.UserTrades; import org.knowm.xchange.exceptions.FundsExceededException; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.trade.TradeService; import org.knowm.xchange.service.trade.params.CancelOrderByIdParams; import org.knowm.xchange.service.trade.params.CancelOrderParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamInstrument; +import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit; +import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; import org.knowm.xchange.service.trade.params.orders.DefaultOpenOrdersParamCurrencyPair; import org.knowm.xchange.service.trade.params.orders.OpenOrdersParamCurrencyPair; import org.knowm.xchange.service.trade.params.orders.OpenOrdersParams; import org.knowm.xchange.service.trade.params.orders.OrderQueryParams; +import org.knowm.xchange.utils.DateUtils; public class CoinbaseProTradeService extends CoinbaseProTradeServiceRaw implements TradeService { @@ -84,7 +93,62 @@ public boolean cancelOrder(CancelOrderParams orderParams) throws IOException { @Override public UserTrades getTradeHistory(TradeHistoryParams params) throws IOException { - return CoinbaseProAdapters.adaptTradeHistory(getCoinbaseProFills(params)); + + String orderId = null; + String productId = null; + Integer afterTradeId = null; + Integer beforeTradeId = null; + Integer limit = null; + String startDate = null; + String endDate = null; + + if (params instanceof CoinbaseProTradeHistoryParams) { + CoinbaseProTradeHistoryParams historyParams = + (CoinbaseProTradeHistoryParams) params; + afterTradeId = historyParams.getAfterTradeId(); + beforeTradeId = historyParams.getBeforeTradeId(); + } + + if (params instanceof TradeHistoryParamTransactionId) { + TradeHistoryParamTransactionId tnxIdParams = + (TradeHistoryParamTransactionId) params; + orderId = tnxIdParams.getTransactionId(); + } + + if (params instanceof TradeHistoryParamCurrencyPair) { + TradeHistoryParamCurrencyPair ccyPairParams = + (TradeHistoryParamCurrencyPair) params; + CurrencyPair currencyPair = ccyPairParams.getCurrencyPair(); + if (currencyPair != null) { + productId = CoinbaseProAdapters.adaptProductID(currencyPair); + } + } + + if (params instanceof TradeHistoryParamInstrument) { + TradeHistoryParamInstrument ccyPairParams = + (TradeHistoryParamInstrument) params; + Instrument instrument = ccyPairParams.getInstrument(); + if (instrument != null) { + productId = CoinbaseProAdapters.adaptProductID(instrument); + } + } + + if (params instanceof TradeHistoryParamLimit) { + TradeHistoryParamLimit limitParams = (TradeHistoryParamLimit) params; + limit = limitParams.getLimit(); + } + + if(params instanceof TradeHistoryParamsTimeSpan){ + TradeHistoryParamsTimeSpan timeSpanParams = (TradeHistoryParamsTimeSpan) params; + startDate = (timeSpanParams.getStartTime() == null) ? null : DateUtils.toISODateString(timeSpanParams.getStartTime()); + endDate = (timeSpanParams.getEndTime() == null) ? null : DateUtils.toISODateString(timeSpanParams.getEndTime()); + } + + if(orderId == null && productId == null){ + throw new CoinbaseProException("Either orderId or productId must be provided"); + } + + return CoinbaseProAdapters.adaptTradeHistory(getCoinbaseProFills(orderId, productId, limit, beforeTradeId, afterTradeId, null, startDate, endDate)); } @Override diff --git a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeServiceRaw.java b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeServiceRaw.java index 056f59ab27d..b08093040c3 100644 --- a/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeServiceRaw.java +++ b/xchange-coinbasepro/src/main/java/org/knowm/xchange/coinbasepro/service/CoinbaseProTradeServiceRaw.java @@ -14,7 +14,9 @@ import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProPlaceOrder; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProTradeHistoryParams; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamInstrument; import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit; import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; import org.knowm.xchange.service.trade.params.TradeHistoryParams; @@ -62,40 +64,7 @@ public CoinbaseProOrder[] getCoinbaseProOpenOrders(String productId) throws IOEx /** https://docs.pro.coinbase.com/#fills */ public CoinbasePagedResponse getCoinbaseProFills( - TradeHistoryParams tradeHistoryParams) throws IOException { - - String orderId = null; - String productId = null; - Integer afterTradeId = null; - Integer beforeTradeId = null; - Integer limit = null; - - if (tradeHistoryParams instanceof CoinbaseProTradeHistoryParams) { - CoinbaseProTradeHistoryParams historyParams = - (CoinbaseProTradeHistoryParams) tradeHistoryParams; - afterTradeId = historyParams.getAfterTradeId(); - beforeTradeId = historyParams.getBeforeTradeId(); - } - - if (tradeHistoryParams instanceof TradeHistoryParamTransactionId) { - TradeHistoryParamTransactionId tnxIdParams = - (TradeHistoryParamTransactionId) tradeHistoryParams; - orderId = tnxIdParams.getTransactionId(); - } - - if (tradeHistoryParams instanceof TradeHistoryParamCurrencyPair) { - TradeHistoryParamCurrencyPair ccyPairParams = - (TradeHistoryParamCurrencyPair) tradeHistoryParams; - CurrencyPair currencyPair = ccyPairParams.getCurrencyPair(); - if (currencyPair != null) { - productId = CoinbaseProAdapters.adaptProductID(currencyPair); - } - } - - if (tradeHistoryParams instanceof TradeHistoryParamLimit) { - TradeHistoryParamLimit limitParams = (TradeHistoryParamLimit) tradeHistoryParams; - limit = limitParams.getLimit(); - } + String orderId, String productId, Integer limit, Integer beforeTradeId, Integer afterTradeId, String marketType, String startDate, String endDate) throws CoinbaseProException, IOException { try { return coinbasePro.getFills( @@ -103,11 +72,14 @@ public CoinbasePagedResponse getCoinbaseProFills( digest, UnixTimestampFactory.INSTANCE.createValue(), passphrase, - afterTradeId, - beforeTradeId, - limit, orderId, - productId); + productId, + limit, + beforeTradeId, + afterTradeId, + marketType, + startDate, + endDate); } catch (CoinbaseProException e) { throw handleError(e); } diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeIntegration.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeIntegration.java index f9cdc9b8e54..0f6a7edba01 100644 --- a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeIntegration.java +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeIntegration.java @@ -30,7 +30,7 @@ public void testCreateExchangeShouldApplyDefaultSpecification() { @Test public void coinbaseShouldBeInstantiatedWithoutAnExceptionWhenUsingDefaultSpecification() { - ExchangeFactory.INSTANCE.createExchange(CoinbaseProExchange.class.getCanonicalName()); + assertThat(ExchangeFactory.INSTANCE.createExchange(CoinbaseProExchange.class.getCanonicalName())).isInstanceOf(Exchange.class); } @Test @@ -61,17 +61,17 @@ public void testExtendedGetTrades() throws IOException { // get latest trades CoinbaseProTrades trades1 = marketDataServiceRaw.getCoinbaseProTradesExtended( - currencyPair, new Long(Integer.MAX_VALUE), null); + currencyPair, (long )Integer.MAX_VALUE, null); assertEquals("Unexpected trades list length (1000)", 1000, trades1.size()); // get latest 10 trades CoinbaseProTrades trades2 = marketDataServiceRaw.getCoinbaseProTradesExtended( - currencyPair, new Long(Integer.MAX_VALUE), 10); + currencyPair, (long) Integer.MAX_VALUE, 10); assertEquals("Unexpected trades list length (10)", 10, trades2.size()); - Trades trades3 = marketDataService.getTrades(currencyPair, new Long(0), new Long(1005)); - assertEquals("Unexpected trades list length (100)", 1004, trades3.getTrades().size()); + Trades trades3 = marketDataService.getTrades(currencyPair); + assertEquals("Unexpected trades list length (1000)", 1000, trades3.getTrades().size()); } @Test @@ -80,8 +80,17 @@ public void testExchangeMetaData() { ExchangeMetaData exchangeMetaData = exchange.getExchangeMetaData(); - Assert.assertNotNull(exchangeMetaData); - Assert.assertNotNull(exchangeMetaData.getCurrencies()); + assertThat(exchangeMetaData).isNotNull(); + assertThat(exchangeMetaData.getInstruments()).isNotEmpty(); + assertThat(exchangeMetaData.getCurrencies()).isNotEmpty(); + exchangeMetaData.getInstruments().forEach((instrument,instrumentMetaData) -> { + assertThat(instrument).isInstanceOf(CurrencyPair.class); + assertThat(instrumentMetaData).isNotNull(); + assertThat(instrumentMetaData.getTradingFee()).isNotNull(); + assertThat(instrumentMetaData.getCounterMinimumAmount()).isNotNull(); + assertThat(instrumentMetaData.getPriceScale()).isNotNull(); + assertThat(instrumentMetaData.getVolumeScale()).isNotNull(); + }); Assert.assertNotNull( "USDC is not defined", exchangeMetaData.getCurrencies().get(new Currency("USDC"))); } diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeWiremock.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeWiremock.java new file mode 100644 index 00000000000..13d613315b6 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProExchangeWiremock.java @@ -0,0 +1,48 @@ +package org.knowm.xchange.coinbasepro; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import com.github.tomakehurst.wiremock.recording.RecordSpecBuilder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; + +public abstract class CoinbaseProExchangeWiremock { + + protected static CoinbaseProExchange exchange; + private static final boolean IS_RECORDING = false; + + @ClassRule + public static WireMockClassRule wireMockRule = new WireMockClassRule(options().dynamicPort()); + + @BeforeClass + public static void initExchange() { + ExchangeSpecification exSpec = new ExchangeSpecification(CoinbaseProExchange.class); + + if (IS_RECORDING) { + + wireMockRule.startRecording( + new RecordSpecBuilder() + .forTarget("https://api.pro.coinbase.com") + .matchRequestBodyWithEqualToJson() + .extractTextBodiesOver(1L) + .chooseBodyMatchTypeAutomatically() + ); + + } + + exchange = (CoinbaseProExchange) ExchangeFactory.INSTANCE.createExchange(exSpec); + + } + + + @AfterClass + public static void stop() { + if (IS_RECORDING) { + wireMockRule.stopRecording(); + } + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateInit.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateInit.java new file mode 100644 index 00000000000..dc0beca9dbc --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateInit.java @@ -0,0 +1,29 @@ +package org.knowm.xchange.coinbasepro; + +import java.io.IOException; +import java.util.Properties; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; + +public class CoinbaseProPrivateInit { + + public static Exchange getCoinbasePrivateInstance() { + Properties properties = new Properties(); + + try { + properties.load(CoinbaseProPrivateInit.class.getResourceAsStream("/secret.keys")); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ExchangeSpecification spec = new CoinbaseProExchange().getDefaultExchangeSpecification(); + + spec.setApiKey(properties.getProperty("coinbaseApi")); + spec.setSecretKey(properties.getProperty("coinbaseSecret")); + spec.setExchangeSpecificParametersItem("passphrase", properties.getProperty("coinbasePassphrase")); + spec.setExchangeSpecificParametersItem(Exchange.USE_SANDBOX, true); + + return ExchangeFactory.INSTANCE.createExchange(spec); + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateIntegration.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateIntegration.java new file mode 100644 index 00000000000..b6524964fe5 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateIntegration.java @@ -0,0 +1,245 @@ +package org.knowm.xchange.coinbasepro; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import org.junit.Ignore; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFundingHistoryParams; +import org.knowm.xchange.coinbasepro.service.CoinbaseProAccountService; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.dto.account.Wallet.WalletFeature; +import org.knowm.xchange.dto.account.params.FundingRecordParamAll; +import org.knowm.xchange.dto.trade.UserTrades; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.service.trade.params.DefaultTradeHistoryParamInstrument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Ignore +public class CoinbaseProPrivateIntegration { + + private final Exchange exchange = CoinbaseProPrivateInit.getCoinbasePrivateInstance(); + private static final Logger LOG = LoggerFactory.getLogger(CoinbaseProPrivateIntegration.class); + Instrument instrument = new CurrencyPair("BTC/USD"); + + private final String SUB_ACCOUNT_ID = ""; + + /** + * AccountService tests + */ + @Test + public void testAccountInfo() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getAccountInfo(); + + LOG.info(accountInfo.toString()); + assertThat(accountInfo.getWallet()).isNotNull(); + assertThat(accountInfo.getWallet(WalletFeature.FUNDING)).isNotNull(); + assertThat(accountInfo.getWallets().size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testSubAccountInfo() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getSubAccountInfo(SUB_ACCOUNT_ID); + + LOG.info(accountInfo.toString()); + assertThat(accountInfo.getWallet()).isNotNull(); + assertThat(accountInfo.getWallet(WalletFeature.FUNDING)).isNotNull(); + assertThat(accountInfo.getWallets().size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testInternalTransfer() throws IOException { + + List internalTransfers = exchange + .getAccountService() + .getInternalTransferHistory(FundingRecordParamAll.builder().build()); + + internalTransfers.forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isInstanceOf(FundingRecord.Type.class); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + assertThat(internalTransfers.size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testDepositHistory() throws IOException { + + List internalTransfers = exchange + .getAccountService() + .getDepositHistory(FundingRecordParamAll.builder().build()); + + internalTransfers.forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isEqualTo(Type.DEPOSIT); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + assertThat(internalTransfers.size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testWithdrawHistory() throws IOException { + + List internalTransfers = exchange + .getAccountService() + .getWithdrawHistory(FundingRecordParamAll.builder().build()); + + internalTransfers.forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isEqualTo(Type.WITHDRAW); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + assertThat(internalTransfers.size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testWalletTransferHistory() throws IOException { + + List internalTransfers = exchange + .getAccountService() + .getWalletTransferHistory(FundingRecordParamAll.builder().subAccountId(SUB_ACCOUNT_ID).build()); + + internalTransfers.forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isInstanceOf(FundingRecord.Type.class); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + assertThat(internalTransfers.size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testSubAccountDepositHistory() throws IOException { + + List internalTransfers = exchange + .getAccountService() + .getSubAccountDepositHistory(FundingRecordParamAll.builder().subAccountId(SUB_ACCOUNT_ID).build()); + + internalTransfers.forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isInstanceOf(FundingRecord.Type.class); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + assertThat(internalTransfers.size()).isGreaterThanOrEqualTo(1); + } + + @Test + public void testFundingHistory() throws IOException { + + CoinbaseProFundingHistoryParams coinbaseFundingHistoryParams = (CoinbaseProFundingHistoryParams) exchange.getAccountService().createFundingHistoryParams(); + + coinbaseFundingHistoryParams.setLimit(50); + coinbaseFundingHistoryParams.setType(Type.WITHDRAW); + + exchange + .getAccountService() + .getFundingHistory(coinbaseFundingHistoryParams) + .forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isInstanceOf(FundingRecord.Type.class); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + + exchange + .getAccountService() + .getFundingHistory(new DefaultTradeHistoryParamInstrument()) + .forEach( + fundingRecord -> { + LOG.info(fundingRecord.toString()); + assertThat(fundingRecord).isNotNull(); + assertThat(fundingRecord.getDate()).isNotNull(); + assertThat(fundingRecord.getAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(fundingRecord.getType()).isInstanceOf(FundingRecord.Type.class); + assertThat(fundingRecord.getStatus()).isInstanceOf(FundingRecord.Status.class); + assertThat(fundingRecord.getCurrency()).isNotNull(); + }); + } + + /** + * TradeService tests + */ + @Test + public void testTradeHistory() throws IOException { + UserTrades userTrades = exchange + .getTradeService() + .getTradeHistory(new DefaultTradeHistoryParamInstrument(instrument)); + + userTrades.getUserTrades().forEach(userTrade -> { + assertThat(userTrade).isNotNull(); + assertThat(userTrade.getId()).isNotNull(); + assertThat(userTrade.getInstrument()).isEqualTo(instrument); + assertThat(userTrade.getPrice()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getOriginalAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getFeeAmount()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(userTrade.getTimestamp()).isNotNull(); + assertThat(userTrade.getType()).isNotNull(); + assertThat(userTrade.getOrderId()).isNotNull(); + assertThat(userTrade.getFeeCurrency()).isNotNull(); + }); + } + + @Test + public void testLedger() throws IOException { + CoinbaseProAccountService service = (CoinbaseProAccountService) exchange.getAccountService(); + CoinbaseProAccount account = service.getCoinbaseProAccountInfo()[1]; + + FundingRecordParamAll params = FundingRecordParamAll.builder() + .subAccountId(account.getId()) + .build(); + + service.getLedger(params).forEach(record -> { + LOG.info(record.toString()); + assertThat(record).isNotNull(); + assertThat(record.getInternalId()).isNotNull(); + assertThat(record.getAmount()).isInstanceOf(BigDecimal.class); + assertThat(record.getDate()).isInstanceOf(Date.class); + assertThat(record.getBalance()).isInstanceOf(BigDecimal.class); + assertThat(record.getType()).isInstanceOf(Type.class); + assertThat(record.getDescription()).isNotNull(); + }); + + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateRawIntegration.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateRawIntegration.java new file mode 100644 index 00000000000..db3e5bc1002 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPrivateRawIntegration.java @@ -0,0 +1,91 @@ +package org.knowm.xchange.coinbasepro; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.coinbasepro.dto.CoinbasePagedResponse; +import org.knowm.xchange.coinbasepro.dto.CoinbaseProTransfers; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProAccount; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProFundingHistoryParams; +import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProLedgerDto.CoinbaseProLedgerTxType; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill; +import org.knowm.xchange.coinbasepro.service.CoinbaseProAccountService; +import org.knowm.xchange.coinbasepro.service.CoinbaseProAccountServiceRaw; +import org.knowm.xchange.coinbasepro.service.CoinbaseProTradeServiceRaw; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.instrument.Instrument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CoinbaseProPrivateRawIntegration { + + private final Exchange exchange = CoinbaseProPrivateInit.getCoinbasePrivateInstance(); + private static final Logger LOG = LoggerFactory.getLogger(CoinbaseProPrivateIntegration.class); + Instrument instrument = new CurrencyPair("BTC/USD"); + + /** + * AccountServiceRaw tests + */ + @Test + public void testCoinbaseAccountById() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getAccountInfo(); + + LOG.info(accountInfo.toString()); + assertThat(accountInfo.getWallet()).isNotNull(); + + CoinbaseProAccountServiceRaw raw = (CoinbaseProAccountServiceRaw) exchange.getAccountService(); + + CoinbaseProAccount[] accounts = raw.getCoinbaseProAccountInfo(); + assertThat(accounts).isNotEmpty(); + + CoinbaseProAccount account = raw.getCoinbaseProAccountById(accounts[0].getId()); + + LOG.info(account.toString()); + assertThat(account).isNotNull(); + assertThat(account.getId()).isNotNull(); + assertThat(account.getCurrency()).isNotNull(); + assertThat(account.getProfileId()).isNotNull(); + assertThat(account.getBalance()).isInstanceOf(BigDecimal.class); + assertThat(account.getHold()).isInstanceOf(BigDecimal.class); + assertThat(account.getAvailable()).isInstanceOf(BigDecimal.class); + assertThat(account.isTradingEnabled()).isTrue(); + } + + /** + * TradeServiceRaw tests + */ + @Test + public void testTradeHistoryRawData() throws IOException { + + CoinbaseProTradeServiceRaw raw = (CoinbaseProTradeServiceRaw) exchange.getTradeService(); + CoinbasePagedResponse rawData = raw.getCoinbaseProFills(null, CoinbaseProAdapters.adaptProductID(instrument), null, null, null, null, null, null); + + rawData.forEach(coinbaseProFill -> { + assertThat(coinbaseProFill).isNotNull(); + assertThat(coinbaseProFill.getTradeId()).isNotNull(); + assertThat(coinbaseProFill.getProductId()).isNotNull(); + assertThat(coinbaseProFill.getOrderId()).isNotNull(); + assertThat(coinbaseProFill.getUserId()).isNotNull(); + assertThat(coinbaseProFill.getProfileId()).isNotNull(); + assertThat(coinbaseProFill.getLiquidity()).isNotNull(); + assertThat(coinbaseProFill.getPrice()).isGreaterThan(BigDecimal.ZERO); + assertThat(coinbaseProFill.getSize()).isGreaterThan(BigDecimal.ZERO); + assertThat(coinbaseProFill.getFee()).isGreaterThanOrEqualTo(BigDecimal.ZERO); + assertThat(coinbaseProFill.getCreatedAt()).isNotNull(); + assertThat(coinbaseProFill.getSide()).isNotNull(); + assertThat(coinbaseProFill.isSettled()).isTrue(); + assertThat(coinbaseProFill.getUsdVolume()).isNotNull(); + assertThat(coinbaseProFill.getMarketType()).isNotNull(); + LOG.info(coinbaseProFill.toString()); + }); + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProProxyTest.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProProxyTest.java new file mode 100644 index 00000000000..abbc10a841b --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProProxyTest.java @@ -0,0 +1,25 @@ +package org.knowm.xchange.coinbasepro; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; +import org.junit.Test; +import org.knowm.xchange.client.ProxyConfig; +import org.knowm.xchange.coinbasepro.service.CoinbaseProBaseService; +import si.mazi.rescu.BodyLoggingRestInvocationHandler; +import si.mazi.rescu.CustomRestProxyFactoryImpl; + +public class CoinbaseProProxyTest extends CoinbaseProExchangeWiremock{ + + static { + ProxyConfig.getInstance().setRestProxyFactoryClass(CustomRestProxyFactoryImpl.class); + } + + @Test + public void testProxyFactory() + throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + CoinbaseProBaseService service = ((CoinbaseProBaseService) exchange.getMarketDataService()); + assertThat(Proxy.getInvocationHandler(service.getCoinbaseProExchangeRestProxy())).isInstanceOf(BodyLoggingRestInvocationHandler.class); + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPublicTest.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPublicTest.java new file mode 100644 index 00000000000..14d4c944f15 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/CoinbaseProPublicTest.java @@ -0,0 +1,45 @@ +package org.knowm.xchange.coinbasepro; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.coinbasepro.service.CoinbaseProMarketDataService; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CoinbaseProPublicTest { + + Exchange exchange = ExchangeFactory.INSTANCE.createExchange(CoinbaseProExchange.class); + CoinbaseProMarketDataService marketDataService = (CoinbaseProMarketDataService) exchange.getMarketDataService(); + private static final Logger LOG = LoggerFactory.getLogger(CoinbaseProPrivateIntegration.class); + + @Test + public void testCoinbaseInstruments() throws Exception { + + marketDataService.getInstruments().forEach((instrument, instrumentMetaData) -> { + LOG.info(instrument.toString()+ "--" + instrumentMetaData.toString()); + assertThat(instrument).isInstanceOf(CurrencyPair.class); + assertThat(instrumentMetaData.getPriceStepSize()).isNotNull(); + assertThat(instrumentMetaData.getAmountStepSize()).isNotNull(); + assertThat(instrumentMetaData.getVolumeScale()).isNotNull(); + assertThat(instrumentMetaData.getPriceScale()).isNotNull(); + assertThat(instrumentMetaData.getTradingFeeCurrency()).isNotNull(); + assertThat(instrumentMetaData.getTradingFee()).isNotNull(); + }); + } + + @Test + public void testCoinbaseCurrency() throws Exception { + marketDataService.getCurrencies().forEach((currency, currencyMetaData) -> { + LOG.info(currency.toString() + "--" + currencyMetaData.toString()); + assertThat(currency).isInstanceOf(Currency.class); + assertThat(currencyMetaData.getMinWithdrawalAmount()).isNotNull(); + assertThat(currencyMetaData.getWalletHealth()).isNotNull(); + assertThat(currencyMetaData.getScale()).isNotNull(); + }); + } +} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProMetadataTest.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProMetadataTest.java deleted file mode 100644 index e5bdf29e3c9..00000000000 --- a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/CoinbaseProMetadataTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.knowm.xchange.coinbasepro.dto; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.InputStream; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.ExchangeFactory; -import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.coinbasepro.CoinbaseProAdapters; -import org.knowm.xchange.coinbasepro.CoinbaseProExchange; -import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProCurrency; -import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProduct; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.meta.ExchangeMetaData; -import si.mazi.rescu.serialization.jackson.DefaultJacksonObjectMapperFactory; -import si.mazi.rescu.serialization.jackson.JacksonObjectMapperFactory; - -public class CoinbaseProMetadataTest { - - // @Test - public void unmarshalTest() throws IOException { - - JacksonObjectMapperFactory factory = new DefaultJacksonObjectMapperFactory(); - ObjectMapper mapper = factory.createObjectMapper(); - - InputStream is = - getClass().getResourceAsStream("/org/knowm/xchange/coinbasepro/dto/products.json"); - CoinbaseProProduct[] products = mapper.readValue(is, CoinbaseProProduct[].class); - assertThat(products).hasSize(10); - - ExchangeSpecification specification = new ExchangeSpecification(CoinbaseProExchange.class); - specification.setShouldLoadRemoteMetaData(false); - Exchange exchange = ExchangeFactory.INSTANCE.createExchange(specification); - ExchangeMetaData exchangeMetaData = exchange.getExchangeMetaData(); - exchangeMetaData = - CoinbaseProAdapters.adaptToExchangeMetaData( - exchangeMetaData, products, new CoinbaseProCurrency[] {}); - assertThat(exchangeMetaData.getInstruments().get(CurrencyPair.ETC_BTC).getPriceScale()) - .isEqualTo(5); - } -} diff --git a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFillTest.java b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFillTest.java index 47c9225c7d7..07279e2b95c 100644 --- a/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFillTest.java +++ b/xchange-coinbasepro/src/test/java/org/knowm/xchange/coinbasepro/dto/trade/CoinbaseProFillTest.java @@ -6,6 +6,8 @@ import java.io.IOException; import java.io.InputStream; import org.junit.Test; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill.Liquidity; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill.Side; import si.mazi.rescu.serialization.jackson.DefaultJacksonObjectMapperFactory; import si.mazi.rescu.serialization.jackson.JacksonObjectMapperFactory; @@ -31,9 +33,9 @@ public void unmarshalTest() throws IOException { assertThat(fill.getSize()).isEqualTo("0.01000000"); assertThat(fill.getOrderId()).isEqualTo("b4b3bbb1-e0e3-4532-9413-23123448ce35"); assertThat(fill.getCreatedAt()).isEqualTo("2017-05-01T07:31:50.243Z"); - assertThat(fill.getLiquidity()).isEqualTo("T"); + assertThat(fill.getLiquidity()).isEqualTo(Liquidity.T); assertThat(fill.getFee()).isEqualTo("0.0000017745000000"); - assertThat(fill.isSettled()).isEqualTo(true); - assertThat(fill.getSide()).isEqualTo("buy"); + assertThat(fill.isSettled()).isTrue(); + assertThat(fill.getSide()).isEqualTo(Side.buy); } } diff --git a/xchange-coinbasepro/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java b/xchange-coinbasepro/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java new file mode 100644 index 00000000000..b2d8f546c55 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java @@ -0,0 +1,24 @@ +package si.mazi.rescu; + +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; + +/** + * Invocation handler that logs the raw textual response body + */ +@Slf4j +public class BodyLoggingRestInvocationHandler extends RestInvocationHandler { + + BodyLoggingRestInvocationHandler(Class restInterface, String url, ClientConfig config) { + super(restInterface, url, config); + } + + @Override + protected Object mapInvocationResult(InvocationResult invocationResult, RestMethodMetadata methodMetadata) throws IOException { + // log the body + log.info(invocationResult.getHttpBody()); + + // do the normal processing + return super.mapInvocationResult(invocationResult, methodMetadata); + } +} diff --git a/xchange-coinbasepro/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java b/xchange-coinbasepro/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java new file mode 100644 index 00000000000..6594f8ec2d1 --- /dev/null +++ b/xchange-coinbasepro/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java @@ -0,0 +1,17 @@ +package si.mazi.rescu; + +/** + * The implementation of {@link IRestProxyFactory} that instantiates {@link BodyLoggingRestInvocationHandler} + */ +public class CustomRestProxyFactoryImpl implements IRestProxyFactory { + + @Override + public I createProxy(Class restInterface, String baseUrl, ClientConfig config, Interceptor... interceptors) { + return RestProxyFactory.createProxy(restInterface, RestProxyFactory.wrap(new BodyLoggingRestInvocationHandler(restInterface, baseUrl, config), interceptors)); + } + + @Override + public I createProxy(Class restInterface, String baseUrl) { + return createProxy(restInterface, baseUrl, null); + } +} diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAdapters.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAdapters.java index c5da51099f8..c2ee031b625 100644 --- a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAdapters.java +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAdapters.java @@ -47,6 +47,7 @@ import org.knowm.xchange.dto.Order; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -291,19 +292,19 @@ public static List adaptFundingHistory( } FundingRecord funding = - new FundingRecord( - address, - addressTag, - new Date(entry.getTimestamp()), - Currency.getInstance(entry.getAmountCurrency()), - entry.getAmount(), - transactionId, - externalId, - type, - status, - null, - entry.getFee(), - description); + FundingRecord.builder() + .address(address) + .addressTag(addressTag) + .date(new Date(entry.getTimestamp())) + .currency(Currency.getInstance(entry.getAmountCurrency())) + .amount(entry.getAmount()) + .internalId(transactionId) + .blockchainTransactionHash(externalId) + .type(type) + .status(status) + .fee(entry.getFee()) + .description(description) + .build(); fundings.add(funding); } @@ -349,19 +350,17 @@ public static List adaptFundingDetail(CoinmateTransferDetail coin } FundingRecord funding = - new FundingRecord( - entry.getDestination(), - entry.getDestinationTag(), - new Date(entry.getTimestamp()), - Currency.getInstance(entry.getAmountCurrency()), - entry.getAmount(), - Long.toString(entry.getId()), - null, - type, - status, - null, - entry.getFee(), - null); + FundingRecord.builder() + .address(entry.getDestination()) + .addressTag(entry.getDestinationTag()) + .date(new Date(entry.getTimestamp())) + .currency(Currency.getInstance(entry.getAmountCurrency())) + .amount(entry.getAmount()) + .internalId(Long.toString(entry.getId())) + .type(type) + .status(status) + .fee(entry.getFee()) + .build(); return Collections.singletonList(funding); } diff --git a/xchange-core/src/main/java/org/knowm/xchange/client/ProxyConfig.java b/xchange-core/src/main/java/org/knowm/xchange/client/ProxyConfig.java new file mode 100644 index 00000000000..4b821442916 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/client/ProxyConfig.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.client; + +import lombok.Data; +import si.mazi.rescu.IRestProxyFactory; +import si.mazi.rescu.RestProxyFactoryImpl; + +@Data +public class ProxyConfig { + + private Class restProxyFactoryClass = RestProxyFactoryImpl.class; + + private static ProxyConfig instance = new ProxyConfig(); + + public static ProxyConfig getInstance() { + return instance; + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/account/FundingRecord.java b/xchange-core/src/main/java/org/knowm/xchange/dto/account/FundingRecord.java index d2005cbd4dc..0cc502fdc14 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/dto/account/FundingRecord.java +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/account/FundingRecord.java @@ -5,6 +5,9 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import lombok.Getter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; import org.knowm.xchange.currency.Currency; /** @@ -13,7 +16,10 @@ *

Funding information contains the detail of deposit/withdrawal transaction for a specific * currency */ -public final class FundingRecord implements Serializable { +@Getter +@SuperBuilder +@ToString +public class FundingRecord implements Serializable { private static final long serialVersionUID = 3788398035845873448L; @@ -54,175 +60,25 @@ public final class FundingRecord implements Serializable { /** Description of the transaction */ private String description; - /** - * Constructs a {@link FundingRecord}. - * - * @param address Crypto currency address for deposit/withdrawal - * @param date Date/Time of transaction - * @param currency The transaction currency - * @param amount Amount deposited/withdrawn (always positive) - * @param internalId Internal transaction identifier, specific to the Exchange - * @param blockchainTransactionHash Transaction hash/id that identifies the transaction within the - * public ledger - * @param type Transaction Type {@link Type} - * @param status Status of the transaction whenever available (e.g. Pending, Completed or any - * descriptive status of transaction). Will be naively converted to Status enum if possible, - * or else be prefixed to description. - * @param balance Balance of the associated account after the transaction is performed - * @param fee Transaction Fee Amount (always positive) - * @param description Description of the transaction. It is a good idea to put here any extra info - * sent back from the exchange that doesn't fit elsewhere so users can still access it. - * @deprecated Use the constructor with enum status parameter. - */ - @Deprecated - public FundingRecord( - final String address, - final Date date, - final Currency currency, - final BigDecimal amount, - final String internalId, - final String blockchainTransactionHash, - final Type type, - final String status, - final BigDecimal balance, - final BigDecimal fee, - final String description) { - this( - address, - date, - currency, - amount, - internalId, - blockchainTransactionHash, - type, - Status.resolveStatus(status), - balance, - fee, - description); - if (this.status == null && status != null) { - this.description = - this.description == null || this.description.isEmpty() - ? status - : status + ": " + this.description; - } - } + private final String fromWallet; - /** - * Constructs a {@link FundingRecord}. - * - * @param address Crypto currency address for deposit/withdrawal - * @param addressTag Crypto address destination tag for deposit/withdrawal - * @param date Date/Time of transaction - * @param currency The transaction currency - * @param amount Amount deposited/withdrawn (always positive) - * @param internalId Internal transaction identifier, specific to the Exchange - * @param blockchainTransactionHash Transaction hash/id that identifies the transaction within the - * public ledger - * @param type Transaction Type {@link Type} - * @param status Status of the transaction whenever available - * @param balance Balance of the associated account after the transaction is performed - * @param fee Transaction Fee Amount (always positive) - * @param description Description of the transaction. It is a good idea to put here any extra info - * sent back from the exchange that doesn't fit elsewhere so users can still access it. - */ - public FundingRecord( - final String address, - final String addressTag, - final Date date, - final Currency currency, - final BigDecimal amount, - final String internalId, - final String blockchainTransactionHash, - final Type type, - final Status status, - final BigDecimal balance, - final BigDecimal fee, - final String description) { - this.address = address; - this.addressTag = addressTag == null || addressTag.isEmpty() ? null : addressTag; - this.date = date; - this.currency = currency; - this.amount = amount == null ? null : amount.abs(); - this.internalId = internalId; - this.blockchainTransactionHash = blockchainTransactionHash; - this.type = type; - this.status = status; - this.balance = balance; - this.fee = fee == null ? null : fee.abs(); - this.description = description; - } + private final String toWallet; + + private final String fromSubAccount; + + private final String toSubAccount; /** - * Constructs a {@link FundingRecord}. + * Customizing amount method to always be positive. Rest of the builder code will be auto generated by Lombok. * - * @param address Crypto currency address for deposit/withdrawal - * @param date Date/Time of transaction - * @param currency The transaction currency - * @param amount Amount deposited/withdrawn (always positive) - * @param internalId Internal transaction identifier, specific to the Exchange - * @param blockchainTransactionHash Transaction hash/id that identifies the transaction within the - * public ledger - * @param type Transaction Type {@link Type} - * @param status Status of the transaction whenever available - * @param balance Balance of the associated account after the transaction is performed - * @param fee Transaction Fee Amount (always positive) - * @param description Description of the transaction. It is a good idea to put here any extra info - * sent back from the exchange that doesn't fit elsewhere so users can still access it. */ - public FundingRecord( - final String address, - final Date date, - final Currency currency, - final BigDecimal amount, - final String internalId, - final String blockchainTransactionHash, - final Type type, - final Status status, - final BigDecimal balance, - final BigDecimal fee, - final String description) { - this( - address, - null, - date, - currency, - amount, - internalId, - blockchainTransactionHash, - type, - status, - balance, - fee, - description); - } - - /** @return Crypto currency address */ - public String getAddress() { - return address; - } - - public String getAddressTag() { - return addressTag; - } - - /** @return Date/Time of transaction */ - public Date getDate() { - return date; - } + public abstract static class FundingRecordBuilder< + C extends FundingRecord, B extends FundingRecordBuilder> { - /** @return The transaction currency */ - public Currency getCurrency() { - return currency; - } - - /** @return Amount deposited/withdrawn in given transaction currency (always positive) */ - public BigDecimal getAmount() { - return amount; - } - - /** @return Internal transaction identifier, specific to the Exchange. */ - public String getInternalId() { - return internalId; + public B amount(BigDecimal amount) { + this.amount = amount == null ? null : amount.abs(); + return self(); + } } @Deprecated // for backward compatibility. Will be removed @@ -230,62 +86,10 @@ public String getExternalId() { return blockchainTransactionHash; } - /** - * @return External Transaction id that identifies the transaction within the public ledger, eg. - * blockchain transaction hash. - */ - public String getBlockchainTransactionHash() { - return blockchainTransactionHash; - } - - /** @return Transaction Type {@link Type} */ - public Type getType() { - return type; - } - - /** - * @return Status of the transaction whenever available (e.g. Open, Completed or any descriptive - * status of transaction) - */ - public Status getStatus() { - return status; - } - - /** @return Balance of the associated account after the transaction is performed */ - public BigDecimal getBalance() { - return balance; - } - - /** @return Transaction Fee Amount in given transaction currency (always positive) */ - public BigDecimal getFee() { - return fee; - } - - /** @return Description of the transaction */ - public String getDescription() { - return description; - } - - @Override - public String toString() { - return String.format( - "FundingRecord{address='%s', date=%s, currency=%s, amount=%s, internalId=%s, blockchainTransactionHash=%s, description='%s', type=%s, status=%s, balance=%s, fee=%s}", - address, - date, - currency, - amount, - internalId, - blockchainTransactionHash, - description, - type, - status, - balance, - fee); - } - /** Enum representing funding transaction type */ public enum Type { WITHDRAWAL(false), + WITHDRAW(false), DEPOSIT(true), AIRDROP(true), /** @@ -299,9 +103,15 @@ public enum Type { * response */ OTHER_OUTFLOW(false), + INTEREST(false), + DELIVERY(false), + SETTLEMENT(false), + INTERNAL_WALLET_TRANSFER(false), + INTERNAL_SUB_ACCOUNT_TRANSFER(false), /** Used for transfers between exchanges accounts */ INTERNAL_WITHDRAWAL(false), + INTERNAL_WITHDRAW(false), /** Used for transfers between exchanges accounts */ INTERNAL_DEPOSIT(true), @@ -310,7 +120,9 @@ public enum Type { REALISED_LOSS(false), /** Used for realised profits from derivatives */ - REALISED_PROFIT(true); + REALISED_PROFIT(true), + + TRADE(false); private static final Map fromString = new HashMap<>(); @@ -350,24 +162,28 @@ public enum Status { "AWAITING APPROVAL", "VERIFYING", "PENDING_APPROVAL", - "PENDING"), + "PENDING", + "SECURITY_CHECK", + "TO_BE_CONFIRMED", + "PROCESSING", + "PENDING_TO_BE_CREDITED_TO_FUNDING_POOL"), /** * The exchange has processed the transfer fully and successfully. The funding typically cannot * be cancelled any more. For withdrawals, the funds are gone from the exchange, though they may * have not reached their destination yet. For deposits, the funds are available to the user. */ - COMPLETE("COMPLETED"), + COMPLETE("COMPLETED", "SUCCESS","BLOCKCHAIN_CONFIRMED","CREDITED_TO_FUNDING_POOL_SUCCESSFULLY"), /** The transfer was cancelled either by the user or by the exchange. */ - CANCELLED("REVOKED", "CANCEL", "REFUND"), + CANCELLED("REVOKED", "CANCEL", "REFUND", "CANCEL_BY_USER"), /** * The transfer has failed for any reason other than user cancellation after it was initiated * and before it was successfully processed. For withdrawals, the funds are available to the * user again. */ - FAILED("FAILURE"), + FAILED("FAILURE", "FAILED", "REJECT", "FAIL","UNKNOWN","DEPOSIT_FAILED"), ; private static final Map fromString = new HashMap<>(); @@ -390,119 +206,11 @@ public enum Status { this.statusArray = statusArray; } - public static Status resolveStatus(String str) { + public static Status resolveStatus(String str) throws IllegalArgumentException{ if (str == null) { return null; } return fromString.get(str.toUpperCase()); } } - - public static final class Builder { - - private String address; - private String addressTag; - private Date date; - private Currency currency; - private BigDecimal amount; - private String internalId; - private String blockchainTransactionHash; - private String description; - private Type type; - private Status status; - private BigDecimal balance; - private BigDecimal fee; - - public static Builder from(FundingRecord record) { - return new Builder() - .setAddress(record.address) - .setAddressTag(record.addressTag) - .setBlockchainTransactionHash(record.blockchainTransactionHash) - .setDate(record.date) - .setCurrency(record.currency) - .setAmount(record.amount) - .setInternalId(record.internalId) - .setDescription(record.description) - .setType(record.type) - .setStatus(record.status) - .setBalance(record.balance) - .setFee(record.fee); - } - - public Builder setAddress(String address) { - this.address = address; - return this; - } - - public Builder setAddressTag(String addressTag) { - this.addressTag = addressTag; - return this; - } - - public Builder setDate(Date date) { - this.date = date; - return this; - } - - public Builder setCurrency(Currency currency) { - this.currency = currency; - return this; - } - - public Builder setAmount(BigDecimal amount) { - this.amount = amount; - return this; - } - - public Builder setInternalId(String internalId) { - this.internalId = internalId; - return this; - } - - public Builder setBlockchainTransactionHash(String blockchainTransactionHash) { - this.blockchainTransactionHash = blockchainTransactionHash; - return this; - } - - public Builder setDescription(String description) { - this.description = description; - return this; - } - - public Builder setType(Type type) { - this.type = type; - return this; - } - - public Builder setStatus(Status status) { - this.status = status; - return this; - } - - public Builder setBalance(BigDecimal balance) { - this.balance = balance; - return this; - } - - public Builder setFee(BigDecimal fee) { - this.fee = fee; - return this; - } - - public FundingRecord build() { - return new FundingRecord( - address, - addressTag, - date, - currency, - amount, - internalId, - blockchainTransactionHash, - type, - status, - balance, - fee, - description); - } - } } diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/account/OpenPosition.java b/xchange-core/src/main/java/org/knowm/xchange/dto/account/OpenPosition.java index 5c5c803f432..f018ed70635 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/dto/account/OpenPosition.java +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/account/OpenPosition.java @@ -7,9 +7,13 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.ToString; import org.knowm.xchange.instrument.Instrument; @JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +@ToString public class OpenPosition implements Serializable { /** The instrument */ private final Instrument instrument; @@ -40,30 +44,6 @@ public OpenPosition( this.unRealisedPnl = unRealisedPnl; } - public Instrument getInstrument() { - return instrument; - } - - public Type getType() { - return type; - } - - public BigDecimal getSize() { - return size; - } - - public BigDecimal getPrice() { - return price; - } - - public BigDecimal getLiquidationPrice() { - return liquidationPrice; - } - - public BigDecimal getUnRealisedPnl() { - return unRealisedPnl; - } - @Override public boolean equals(final Object o) { if (this == o) return true; @@ -82,24 +62,6 @@ public int hashCode() { return Objects.hash(instrument, type, size, price, liquidationPrice, unRealisedPnl); } - @Override - public String toString() { - return "OpenPosition{" - + "instrument=" - + instrument - + ", type=" - + type - + ", size=" - + size - + ", price=" - + price - + ", liquidationPrice=" - + liquidationPrice - + ", unRealisedPnl=" - + unRealisedPnl - + '}'; - } - public enum Type { LONG, SHORT diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/account/Wallet.java b/xchange-core/src/main/java/org/knowm/xchange/dto/account/Wallet.java index 8e5622ea481..fb620a92b88 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/dto/account/Wallet.java +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/account/Wallet.java @@ -33,7 +33,8 @@ public enum WalletFeature { /** You can fund other margin traders with funds allocated to this wallet to earn an interest */ MARGIN_FUNDING, /** Wallet for futures platform*/ - FUTURES_TRADING + FUTURES_TRADING, + OPTIONS_TRADING } /** The keys represent the currency of the wallet. */ diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/account/params/FundingRecordParamAll.java b/xchange-core/src/main/java/org/knowm/xchange/dto/account/params/FundingRecordParamAll.java new file mode 100644 index 00000000000..1165125cf80 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/account/params/FundingRecordParamAll.java @@ -0,0 +1,32 @@ +package org.knowm.xchange.dto.account.params; + +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; + +@Getter +@Setter +@Builder +@AllArgsConstructor +public class FundingRecordParamAll { + + private String accountCategory; + private String transferId; + private String subAccountId; + private Currency currency; + private FundingRecord.Status status; + private Date startTime; + private Date endTime; + private Integer limit; + private Type type; + private boolean usePagination; + + public static FundingRecordParamAllBuilder builder() { + return new FundingRecordParamAllBuilder().usePagination(true); + } +} \ No newline at end of file diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/meta/CurrencyMetaData.java b/xchange-core/src/main/java/org/knowm/xchange/dto/meta/CurrencyMetaData.java index c13985ee393..5582a112e4c 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/dto/meta/CurrencyMetaData.java +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/meta/CurrencyMetaData.java @@ -3,7 +3,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.io.Serializable; import java.math.BigDecimal; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; +@ToString +@Builder +@Getter public class CurrencyMetaData implements Serializable { private static final long serialVersionUID = -247899067657358542L; @@ -60,33 +66,4 @@ public CurrencyMetaData( this.walletHealth = walletHealth; } - public Integer getScale() { - return scale; - } - - public BigDecimal getWithdrawalFee() { - return withdrawalFee; - } - - public BigDecimal getMinWithdrawalAmount() { - return minWithdrawalAmount; - } - - public WalletHealth getWalletHealth() { - return walletHealth; - } - - @Override - public String toString() { - return "CurrencyMetaData [" - + "scale=" - + scale - + ", withdrawalFee=" - + withdrawalFee - + ", minWithdrawalAmount=" - + minWithdrawalAmount - + ", walletHealth=" - + walletHealth - + "]"; - } } diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java b/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java index b10c6ef0ab7..2a5a28f4922 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java +++ b/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java @@ -10,6 +10,7 @@ import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.params.FundingRecordParamAll; import org.knowm.xchange.exceptions.ExchangeException; import org.knowm.xchange.exceptions.NotAvailableFromExchangeException; import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; @@ -48,6 +49,23 @@ default AccountInfo getAccountInfo() throws IOException { throw new NotYetImplementedForExchangeException("getAccountInfo"); } + /** + * Get subaccount info + * + * @return the AccountInfo object, null if some sort of error occurred. Implementers should log + * the error. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default AccountInfo getSubAccountInfo(String subAccountId) throws IOException { + throw new NotYetImplementedForExchangeException("getSubAccountInfo"); + } + /** * Convenience method, typically just delegates to withdrawFunds(WithdrawFundsParams params) * @@ -186,4 +204,95 @@ default List getFundingHistory(TradeHistoryParams params) throws default Map getDynamicTradingFeesByInstrument() throws IOException { throw new NotYetImplementedForExchangeException("getDynamicTradingFeesByInstrument"); } + + /** + * @return list of transfer history if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getInternalTransferHistory(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getInternalTransferHistory"); + } + + /** + * @return list of withdraw history if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getWithdrawHistory(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getWithdrawHistory"); + } + + /** + * @return list of deposit history for a subAccount if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getSubAccountDepositHistory(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getSubAccountDepositHistory"); + } + + /** + * @return list of deposit history if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getDepositHistory(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getDepositHistory"); + } + + /** + * @return list of internal wallet's transfer history if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getWalletTransferHistory(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getWalletTransferHistory"); + } + + /** + * @return list of ledger history if available or an empty list otherwise. This should never + * return null. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default List getLedger(FundingRecordParamAll params) throws IOException { + throw new NotYetImplementedForExchangeException("getLedger"); + } + } diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/account/params/TransferHistoryParam.java b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/TransferHistoryParam.java new file mode 100644 index 00000000000..974145f0ed2 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/TransferHistoryParam.java @@ -0,0 +1,3 @@ +package org.knowm.xchange.service.account.params; + +public interface TransferHistoryParam {} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/marketdata/MarketDataService.java b/xchange-core/src/main/java/org/knowm/xchange/service/marketdata/MarketDataService.java index 670cb4a227e..dec0782f374 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/service/marketdata/MarketDataService.java +++ b/xchange-core/src/main/java/org/knowm/xchange/service/marketdata/MarketDataService.java @@ -2,9 +2,13 @@ import java.io.IOException; import java.util.List; +import java.util.Map; import org.knowm.xchange.Exchange; +import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.marketdata.*; +import org.knowm.xchange.dto.meta.CurrencyMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.exceptions.ExchangeException; import org.knowm.xchange.exceptions.NotAvailableFromExchangeException; import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; @@ -229,4 +233,38 @@ default FundingRates getFundingRates() throws IOException { default FundingRate getFundingRate(Instrument instrument) throws IOException { throw new NotYetImplementedForExchangeException("getFundingRate"); } + + /** + * Get currencies currently provided by the exchange + * + * @return The list of currencies, null if some sort of error occurred. Implementers should log + * the error. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default Map getCurrencies() throws IOException { + throw new NotYetImplementedForExchangeException("getCurrencies"); + } + + /** + * Get instruments/currency pairs currently provided by the exchange + * + * @return The list of instruments/currency pairs, null if some sort of error occurred. + * Implementers should log the error. + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default Map getInstruments() throws IOException { + throw new NotYetImplementedForExchangeException("getInstruments"); + } } diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamId.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamId.java new file mode 100644 index 00000000000..920c94c7f80 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamId.java @@ -0,0 +1,21 @@ +package org.knowm.xchange.service.trade.params; + +public class DefaultTradeHistoryParamId implements TradeHistoryParamId { + + private String id; + + public DefaultTradeHistoryParamId(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamUserReference.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamUserReference.java new file mode 100644 index 00000000000..40b73208763 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultTradeHistoryParamUserReference.java @@ -0,0 +1,20 @@ +package org.knowm.xchange.service.trade.params; + +public class DefaultTradeHistoryParamUserReference implements TradeHistoryParamUserReference{ + + private String userReference; + + public DefaultTradeHistoryParamUserReference(String userReference) { + this.userReference = userReference; + } + + @Override + public String getUserReference() { + return userReference; + } + + @Override + public void setUserReference(String userReference) { + this.userReference = userReference; + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamAccountId.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamAccountId.java new file mode 100644 index 00000000000..abc2a2d31cb --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamAccountId.java @@ -0,0 +1,7 @@ +package org.knowm.xchange.service.trade.params; + +public interface TradeHistoryParamAccountId { + String getAccountId(); + + void setAccountId(String accountId); +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamId.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamId.java new file mode 100644 index 00000000000..1a991809ae7 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamId.java @@ -0,0 +1,7 @@ +package org.knowm.xchange.service.trade.params; + +public interface TradeHistoryParamId { + String getId(); + + void setId(String id); +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamUserReference.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamUserReference.java new file mode 100644 index 00000000000..cce436073c8 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamUserReference.java @@ -0,0 +1,7 @@ +package org.knowm.xchange.service.trade.params; + +public interface TradeHistoryParamUserReference { + String getUserReference(); + + void setUserReference(String userReference); +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamsAll.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamsAll.java index f97ccdc945d..d10528113a9 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamsAll.java +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/TradeHistoryParamsAll.java @@ -24,7 +24,9 @@ public class TradeHistoryParamsAll TradeHistoryParamMultiCurrencyPair, TradeHistoryParamInstrument, TradeHistoryParamMultiInstrument, - TradeHistoryParamLimit { + TradeHistoryParamLimit, + TradeHistoryParamId, + TradeHistoryParamUserReference{ private Integer pageLength; private Integer pageNumber; @@ -36,6 +38,28 @@ public class TradeHistoryParamsAll private Instrument instrument; private Collection instruments = Collections.emptySet(); private Integer limit; + private String id; + private String userReference; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public String getUserReference() { + return userReference; + } + + @Override + public void setUserReference(String userReference) { + this.userReference = userReference; + } @Override public Integer getPageLength() { diff --git a/xchange-core/src/test/java/org/knowm/xchange/dto/account/FundingRecordStatusTest.java b/xchange-core/src/test/java/org/knowm/xchange/dto/account/FundingRecordStatusTest.java index 3db6a13d68b..76d9c11a513 100644 --- a/xchange-core/src/test/java/org/knowm/xchange/dto/account/FundingRecordStatusTest.java +++ b/xchange-core/src/test/java/org/knowm/xchange/dto/account/FundingRecordStatusTest.java @@ -6,6 +6,7 @@ import static org.knowm.xchange.dto.account.FundingRecord.Type.DEPOSIT; import org.junit.Test; +import org.knowm.xchange.dto.account.FundingRecord.Status; public class FundingRecordStatusTest { @@ -26,16 +27,7 @@ public void shouldProcessNullDescription() throws Exception { @Test public void shouldProcessStatusAsDescriptionWhenDescInputNull() throws Exception { - testStatusDesc("Unknown", null, null, "Unknown"); - } - - @Test - public void shouldPrependUnrecognizedStatusStringToDescription() throws Exception { - testStatusDesc( - "AdminCancelled", - "The administrator has cancelled the transfers.", - null, - "AdminCancelled: The administrator has cancelled the transfers."); + testStatusDesc("Unknown", null, null, null); } private void testStatusDesc( @@ -43,9 +35,16 @@ private void testStatusDesc( String descriptionInput, FundingRecord.Status expectedStatus, String expectedDescription) { - final FundingRecord fundingRecord = - new FundingRecord( - "", null, BTC, ONE, "", "", DEPOSIT, statusInput, ONE, ONE, descriptionInput); + final FundingRecord fundingRecord = FundingRecord.builder() + .currency(BTC) + .amount(ONE) + .type(DEPOSIT) + .status(Status.resolveStatus(statusInput)) + .fee(ONE) + .balance(ONE) + .description(descriptionInput) + .build(); + assertThat(fundingRecord.getStatus()).isEqualTo(expectedStatus); assertThat(fundingRecord.getDescription()).isEqualTo(expectedDescription); } diff --git a/xchange-exmo/src/main/java/org/knowm/xchange/exmo/service/ExmoAccountServiceRaw.java b/xchange-exmo/src/main/java/org/knowm/xchange/exmo/service/ExmoAccountServiceRaw.java index 99b21da649b..ced47d04fad 100644 --- a/xchange-exmo/src/main/java/org/knowm/xchange/exmo/service/ExmoAccountServiceRaw.java +++ b/xchange-exmo/src/main/java/org/knowm/xchange/exmo/service/ExmoAccountServiceRaw.java @@ -65,21 +65,16 @@ public List getFundingHistory(Date since) throws IOException { else if (status.equalsIgnoreCase("cancelled")) statusEnum = FundingRecord.Status.CANCELLED; FundingRecord fundingRecord = - new FundingRecord( - address, - DateUtils.fromUnixTime(Long.valueOf(time)), - Currency.getInstance(curr), - new BigDecimal(amount).abs(), - null, - txid, - type.equalsIgnoreCase("deposit") - ? FundingRecord.Type.DEPOSIT - : FundingRecord.Type.WITHDRAWAL, - statusEnum, - null, - null, - description); - + FundingRecord.builder() + .address(address) + .date(DateUtils.fromUnixTime(time)) + .currency(Currency.getInstance(curr)) + .amount(new BigDecimal(amount).abs()) + .blockchainTransactionHash(txid) + .type(type.equalsIgnoreCase("deposit") ? FundingRecord.Type.DEPOSIT : FundingRecord.Type.WITHDRAWAL) + .status(statusEnum) + .description(description) + .build(); results.add(fundingRecord); } diff --git a/xchange-gateio/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java b/xchange-gateio/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java index 35376cfc7dc..58f1d1b43ae 100644 --- a/xchange-gateio/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java +++ b/xchange-gateio/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java @@ -16,6 +16,7 @@ import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; import org.knowm.xchange.dto.account.FundingRecord.Status; +import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -289,18 +290,16 @@ public static List adaptDepositsWithdrawals( .forEach( d -> { FundingRecord r = - new FundingRecord( - d.address, - d.getTimestamp(), - Currency.getInstance(d.currency), - d.amount, - d.id, - d.txid, - FundingRecord.Type.DEPOSIT, - status(d.status), - null, - null, - null); + FundingRecord.builder() + .address(d.address) + .date(d.getTimestamp()) + .currency(Currency.getInstance(d.currency)) + .amount(d.amount) + .internalId(d.id) + .blockchainTransactionHash(d.txid) + .type(Type.DEPOSIT) + .status(status(d.status)) + .build(); result.add(r); }); depositsWithdrawals @@ -308,18 +307,16 @@ public static List adaptDepositsWithdrawals( .forEach( w -> { FundingRecord r = - new FundingRecord( - w.address, - w.getTimestamp(), - Currency.getInstance(w.currency), - w.amount, - w.id, - w.txid, - FundingRecord.Type.WITHDRAWAL, - status(w.status), - null, - null, - null); + FundingRecord.builder() + .address(w.address) + .date(w.getTimestamp()) + .currency(Currency.getInstance(w.currency)) + .amount(w.amount) + .internalId(w.id) + .blockchainTransactionHash(w.txid) + .type(Type.WITHDRAWAL) + .status(status(w.status)) + .build(); result.add(r); }); diff --git a/xchange-gemini/src/main/java/org/knowm/xchange/gemini/v1/GeminiAdapters.java b/xchange-gemini/src/main/java/org/knowm/xchange/gemini/v1/GeminiAdapters.java index a58c124cd0a..afb9b586ccf 100644 --- a/xchange-gemini/src/main/java/org/knowm/xchange/gemini/v1/GeminiAdapters.java +++ b/xchange-gemini/src/main/java/org/knowm/xchange/gemini/v1/GeminiAdapters.java @@ -491,16 +491,16 @@ public static FundingRecord adapt(GeminiTransfer transfer) { ? FundingRecord.Type.WITHDRAWAL : FundingRecord.Type.DEPOSIT; - return new FundingRecord.Builder() - .setStatus(status) - .setType(type) - .setInternalId(transfer.eid) - .setAddress(transfer.destination) - .setCurrency(Currency.getInstance(transfer.currency)) - .setDate(DateUtils.fromMillisUtc(transfer.timestamp)) - .setAmount(transfer.amount) - .setBlockchainTransactionHash(transfer.txnHash) - .setDescription(description) + return FundingRecord.builder() + .status(status) + .type(type) + .internalId(transfer.eid) + .address(transfer.destination) + .currency(Currency.getInstance(transfer.currency)) + .date(DateUtils.fromMillisUtc(transfer.timestamp)) + .amount(transfer.amount) + .blockchainTransactionHash(transfer.txnHash) + .description(description) .build(); } diff --git a/xchange-hitbtc/src/main/java/org/knowm/xchange/hitbtc/v2/HitbtcAdapters.java b/xchange-hitbtc/src/main/java/org/knowm/xchange/hitbtc/v2/HitbtcAdapters.java index 57ce229f838..2b4a1148279 100644 --- a/xchange-hitbtc/src/main/java/org/knowm/xchange/hitbtc/v2/HitbtcAdapters.java +++ b/xchange-hitbtc/src/main/java/org/knowm/xchange/hitbtc/v2/HitbtcAdapters.java @@ -320,17 +320,17 @@ public static FundingRecord adapt(HitbtcTransaction transaction) { description += ", paymentId: " + transaction.getPaymentId(); } - return new FundingRecord.Builder() - .setAddress(transaction.getAddress()) - .setCurrency(Currency.getInstance(transaction.getCurrency())) - .setAmount(transaction.getAmount()) - .setType(convertType(transaction.getType())) - .setFee(transaction.getFee()) - .setDescription(description) - .setStatus(convertStatus(transaction.getStatus())) - .setInternalId(transaction.getId()) - .setBlockchainTransactionHash(transaction.getHash()) - .setDate(transaction.getCreatedAt()) + return FundingRecord.builder() + .address(transaction.getAddress()) + .currency(Currency.getInstance(transaction.getCurrency())) + .amount(transaction.getAmount()) + .type(convertType(transaction.getType())) + .fee(transaction.getFee()) + .description(description) + .status(convertStatus(transaction.getStatus())) + .internalId(transaction.getId()) + .blockchainTransactionHash(transaction.getHash()) + .date(transaction.getCreatedAt()) .build(); } diff --git a/xchange-huobi/src/main/java/org/knowm/xchange/huobi/HuobiAdapters.java b/xchange-huobi/src/main/java/org/knowm/xchange/huobi/HuobiAdapters.java index 19b5a425253..8e8b0302447 100644 --- a/xchange-huobi/src/main/java/org/knowm/xchange/huobi/HuobiAdapters.java +++ b/xchange-huobi/src/main/java/org/knowm/xchange/huobi/HuobiAdapters.java @@ -394,18 +394,17 @@ public static List adaptFundingHistory(HuobiFundingRecord[] fundi public static FundingRecord adaptFundingRecord(HuobiFundingRecord r) { - return new FundingRecord( - r.getAddress(), - r.getCreatedAt(), - Currency.getInstance(r.getCurrency()), - r.getAmount(), - Long.toString(r.getId()), - r.getTxhash(), - r.getType(), - adaptFundingStatus(r), - null, - r.getFee(), - null); + return FundingRecord.builder() + .address(r.getAddress()) + .date(r.getCreatedAt()) + .currency(Currency.getInstance(r.getCurrency())) + .amount(r.getAmount()) + .internalId(Long.toString(r.getId())) + .blockchainTransactionHash(r.getTxhash()) + .type(r.getType()) + .status(adaptFundingStatus(r)) + .fee(r.getFee()) + .build(); } private static Status adaptFundingStatus(HuobiFundingRecord record) { diff --git a/xchange-idex/src/main/java/org/knowm/xchange/idex/IdexAccountService.java b/xchange-idex/src/main/java/org/knowm/xchange/idex/IdexAccountService.java index 20fb8e4a919..10451cdb889 100644 --- a/xchange-idex/src/main/java/org/knowm/xchange/idex/IdexAccountService.java +++ b/xchange-idex/src/main/java/org/knowm/xchange/idex/IdexAccountService.java @@ -120,34 +120,30 @@ private List mutableList( returnDepositsWithdrawalsPost.getWithdrawals().stream() .map( fundingLedger -> - new FundingRecord( - exchange.getExchangeSpecification().getApiKey(), - new Date(Long.parseLong(fundingLedger.getTimestamp()) * 1000), - new Currency(fundingLedger.getCurrency()), - safeParse(fundingLedger.getAmount()), - fundingLedger.getTransactionHash(), - fundingLedger.getDepositNumber(), - Type.WITHDRAWAL, - Status.resolveStatus(fundingLedger.getStatus()), - BigDecimal.ZERO, - BigDecimal.ZERO, - "")) + FundingRecord.builder() + .address(exchange.getExchangeSpecification().getApiKey()) + .date(new Date(Long.parseLong(fundingLedger.getTimestamp()) * 1000)) + .currency(new Currency(fundingLedger.getCurrency())) + .amount(safeParse(fundingLedger.getAmount())) + .internalId(fundingLedger.getTransactionHash()) + .blockchainTransactionHash(fundingLedger.getDepositNumber()) + .type(Type.WITHDRAWAL) + .status(Status.resolveStatus(fundingLedger.getStatus())) + .build()) .collect(Collectors.toList()), returnDepositsWithdrawalsPost.getDeposits().stream() .map( fundingLedger1 -> - new FundingRecord( - exchange.getExchangeSpecification().getApiKey(), - new Date(Long.parseLong(fundingLedger1.getTimestamp()) * 1000), - new Currency(fundingLedger1.getCurrency()), - safeParse(fundingLedger1.getAmount()), - fundingLedger1.getTransactionHash(), - fundingLedger1.getDepositNumber(), - Type.DEPOSIT, - Status.resolveStatus(fundingLedger1.getStatus()), - BigDecimal.ZERO, - BigDecimal.ZERO, - "")) + FundingRecord.builder() + .address(exchange.getExchangeSpecification().getApiKey()) + .date(new Date(Long.parseLong(fundingLedger1.getTimestamp()) * 1000)) + .currency(new Currency(fundingLedger1.getCurrency())) + .amount(safeParse(fundingLedger1.getAmount())) + .internalId(fundingLedger1.getTransactionHash()) + .blockchainTransactionHash(fundingLedger1.getDepositNumber()) + .type(Type.DEPOSIT) + .status(Status.resolveStatus(fundingLedger1.getStatus())) + .build()) .collect(Collectors.toList())) .stream() .flatMap(List::stream) diff --git a/xchange-independentreserve/src/main/java/org/knowm/xchange/independentreserve/IndependentReserveAdapters.java b/xchange-independentreserve/src/main/java/org/knowm/xchange/independentreserve/IndependentReserveAdapters.java index e3164bb8b4b..98b7119b1a8 100644 --- a/xchange-independentreserve/src/main/java/org/knowm/xchange/independentreserve/IndependentReserveAdapters.java +++ b/xchange-independentreserve/src/main/java/org/knowm/xchange/independentreserve/IndependentReserveAdapters.java @@ -285,18 +285,16 @@ public static FundingRecord adaptTransaction(IndependentReserveTransaction trans } else if (transaction.getCredit() != null) { amount = transaction.getCredit(); } - return new FundingRecord( - null, - transaction.getCreatedTimestamp(), - new Currency(transaction.getCurrencyCode()), - amount, - null, - adaptTransactionHash(transaction), - adaptTransactionTypeToFundingRecordType(transaction.getType()), - adaptTransactionStatusToFundingRecordStatus(transaction.getStatus()), - transaction.getBalance(), - null, - transaction.getComment()); + return FundingRecord.builder() + .date(transaction.getCreatedTimestamp()) + .currency(Currency.getInstance(transaction.getCurrencyCode())) + .amount(amount) + .blockchainTransactionHash(adaptTransactionHash(transaction)) + .type(adaptTransactionTypeToFundingRecordType(transaction.getType())) + .status(adaptTransactionStatusToFundingRecordStatus(transaction.getStatus())) + .balance(transaction.getBalance()) + .description(transaction.getComment()) + .build(); } public static CurrencyPair adaptBrokerageCurrencyPair( diff --git a/xchange-itbit/src/main/java/org/knowm/xchange/itbit/ItBitAdapters.java b/xchange-itbit/src/main/java/org/knowm/xchange/itbit/ItBitAdapters.java index 82257c81ffc..9f0695f607e 100644 --- a/xchange-itbit/src/main/java/org/knowm/xchange/itbit/ItBitAdapters.java +++ b/xchange-itbit/src/main/java/org/knowm/xchange/itbit/ItBitAdapters.java @@ -351,18 +351,16 @@ public static FundingRecord adapt(ItBitFunding itBitFunding) { Currency currency = adaptCcy(itBitFunding.currency); - return new FundingRecord( - itBitFunding.destinationAddress, - date, - currency, - itBitFunding.amount, - itBitFunding.withdrawalId, - itBitFunding.txnHash, - type, - status, - null, - null, - null); + return FundingRecord.builder() + .address(itBitFunding.destinationAddress) + .date(date) + .currency(currency) + .amount(itBitFunding.amount) + .internalId(itBitFunding.withdrawalId) + .blockchainTransactionHash(itBitFunding.txnHash) + .type(type) + .status(status) + .build(); } catch (ParseException e) { throw new IllegalStateException("Cannot parse " + itBitFunding, e); } diff --git a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java index 6f36ce240d8..9ed72bff56f 100644 --- a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java +++ b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java @@ -21,6 +21,7 @@ import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.OpenPosition; import org.knowm.xchange.dto.account.OpenPositions; import org.knowm.xchange.dto.account.Wallet; @@ -489,18 +490,15 @@ public static List adaptFundingHistory( if (type != null) { final String internalId = krakenLedger.getRefId(); // or ledgerEntry.getKey()? FundingRecord fundingRecordEntry = - new FundingRecord( - null, - timestamp, - currency, - krakenLedger.getTransactionAmount(), - internalId, - null, - FundingRecord.Type.fromString(krakenLedger.getLedgerType().name()), - FundingRecord.Status.COMPLETE, - krakenLedger.getBalance(), - krakenLedger.getFee(), - null); + FundingRecord.builder() + .date(timestamp) + .currency(currency) + .amount(krakenLedger.getTransactionAmount()) + .internalId(internalId) + .type(FundingRecord.Type.fromString(krakenLedger.getLedgerType().name())) + .status(Status.COMPLETE) + .fee(krakenLedger.getFee()) + .build(); fundingRecords.add(fundingRecordEntry); } } diff --git a/xchange-kucoin/src/main/java/org/knowm/xchange/kucoin/KucoinAdapters.java b/xchange-kucoin/src/main/java/org/knowm/xchange/kucoin/KucoinAdapters.java index 22bfd285fc6..f4b27353d52 100644 --- a/xchange-kucoin/src/main/java/org/knowm/xchange/kucoin/KucoinAdapters.java +++ b/xchange-kucoin/src/main/java/org/knowm/xchange/kucoin/KucoinAdapters.java @@ -413,17 +413,18 @@ private static final class PriceAndSize { } public static FundingRecord adaptFundingRecord(WithdrawalResponse wr) { - FundingRecord.Builder b = new FundingRecord.Builder(); - return b.setAddress(wr.getAddress()) - .setAmount(wr.getAmount()) - .setCurrency(Currency.getInstance(wr.getCurrency())) - .setFee(wr.getFee()) - .setType(Type.WITHDRAWAL) - .setStatus(convertStatus(wr.getStatus())) - .setInternalId(wr.getId()) - .setBlockchainTransactionHash(wr.getWalletTxId()) - .setDescription(wr.getMemo()) - .setDate(wr.getCreatedAt()) + + return FundingRecord.builder() + .address(wr.getAddress()) + .amount(wr.getAmount()) + .currency(Currency.getInstance(wr.getCurrency())) + .fee(wr.getFee()) + .type(Type.WITHDRAWAL) + .status(convertStatus(wr.getStatus())) + .internalId(wr.getId()) + .blockchainTransactionHash(wr.getWalletTxId()) + .description(wr.getMemo()) + .date(wr.getCreatedAt()) .build(); } @@ -445,16 +446,16 @@ private static Status convertStatus(String status) { } public static FundingRecord adaptFundingRecord(DepositResponse dr) { - FundingRecord.Builder b = new FundingRecord.Builder(); - return b.setAddress(dr.getAddress()) - .setAmount(dr.getAmount()) - .setCurrency(Currency.getInstance(dr.getCurrency())) - .setFee(dr.getFee()) - .setType(Type.DEPOSIT) - .setStatus(convertStatus(dr.getStatus())) - .setBlockchainTransactionHash(dr.getWalletTxId()) - .setDescription(dr.getMemo()) - .setDate(dr.getCreatedAt()) + return FundingRecord.builder() + .address(dr.getAddress()) + .amount(dr.getAmount()) + .currency(Currency.getInstance(dr.getCurrency())) + .fee(dr.getFee()) + .type(Type.DEPOSIT) + .status(convertStatus(dr.getStatus())) + .blockchainTransactionHash(dr.getWalletTxId()) + .description(dr.getMemo()) + .date(dr.getCreatedAt()) .build(); } } diff --git a/xchange-livecoin/src/main/java/org/knowm/xchange/livecoin/LivecoinAdapters.java b/xchange-livecoin/src/main/java/org/knowm/xchange/livecoin/LivecoinAdapters.java index f5de978560f..cb8c82f4793 100644 --- a/xchange-livecoin/src/main/java/org/knowm/xchange/livecoin/LivecoinAdapters.java +++ b/xchange-livecoin/src/main/java/org/knowm/xchange/livecoin/LivecoinAdapters.java @@ -20,6 +20,7 @@ import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -248,18 +249,16 @@ public static FundingRecord adaptFundingRecord(Map map) { FundingRecord.Type type = FundingRecord.Type.WITHDRAWAL; if (map.get("type").toString().equals("DEPOSIT")) type = FundingRecord.Type.DEPOSIT; - return new FundingRecord( - Optional.ofNullable(map.get("externalKey")).map(Object::toString).orElse(null), - DateUtils.fromMillisUtc(Long.parseLong(map.get("date").toString())), - getInstance(map.get("fixedCurrency").toString()), - new BigDecimal(map.get("amount").toString()), - map.get("id").toString(), - null, - type, - FundingRecord.Status.COMPLETE, - null, - new BigDecimal(map.get("fee").toString()), - null); + return FundingRecord.builder() + .address(Optional.ofNullable(map.get("externalKey")).map(Object::toString).orElse(null)) + .date(DateUtils.fromMillisUtc(Long.parseLong(map.get("date").toString()))) + .currency(getInstance(map.get("fixedCurrency").toString())) + .amount(new BigDecimal(map.get("amount").toString())) + .internalId(map.get("id").toString()) + .type(type) + .status(Status.COMPLETE) + .fee(new BigDecimal(map.get("fee").toString())) + .build(); } public static Wallet adaptWallet(List livecoinBalances) { diff --git a/xchange-luno/src/main/java/org/knowm/xchange/luno/service/LunoAccountService.java b/xchange-luno/src/main/java/org/knowm/xchange/luno/service/LunoAccountService.java index 367abf419b8..e3f5d27b1b9 100644 --- a/xchange-luno/src/main/java/org/knowm/xchange/luno/service/LunoAccountService.java +++ b/xchange-luno/src/main/java/org/knowm/xchange/luno/service/LunoAccountService.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.knowm.xchange.Exchange; import org.knowm.xchange.currency.Currency; @@ -106,18 +107,15 @@ public List getFundingHistory(TradeHistoryParams params) List result = new ArrayList<>(); for (Withdrawal w : lunoAPI.withdrawals().getWithdrawals()) { result.add( - new FundingRecord( - null, - w.getCreatedAt(), - LunoUtil.fromLunoCurrency(w.currency), - w.amount, - w.id, - null, - Type.WITHDRAWAL, - convert(w.status), - null, - w.fee, - null)); + FundingRecord.builder() + .date(w.getCreatedAt()) + .currency(LunoUtil.fromLunoCurrency(w.currency)) + .amount(w.amount) + .internalId(w.id) + .type(Type.WITHDRAWAL) + .status(convert(w.status)) + .fee(w.fee) + .build()); } return result; } diff --git a/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkCoinAdapters.java b/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkCoinAdapters.java index 66966c9ce85..05ddd69eec6 100644 --- a/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkCoinAdapters.java +++ b/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkCoinAdapters.java @@ -415,18 +415,15 @@ public static List adaptFundingHistory( } fundingRecords.add( - new FundingRecord( - okCoinRecordEntry.getAddress(), - adaptDate(okCoinRecordEntry.getDate()), - c, - okCoinRecordEntry.getAmount(), - null, - null, - type, - status, - null, - okCoinRecordEntry.getFee(), - null)); + FundingRecord.builder() + .address(okCoinRecordEntry.getAddress()) + .date(adaptDate(okCoinRecordEntry.getDate())) + .currency(c) + .amount(okCoinRecordEntry.getAmount()) + .type(type) + .status(status) + .fee(okCoinRecordEntry.getFee()) + .build()); } } diff --git a/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkexAdaptersV3.java b/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkexAdaptersV3.java index b5f6fe72065..9d52e741fdb 100644 --- a/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkexAdaptersV3.java +++ b/xchange-okcoin/src/main/java/org/knowm/xchange/okcoin/OkexAdaptersV3.java @@ -101,15 +101,15 @@ public static LimitOrder convert(OkexOpenOrder o) { } public static FundingRecord adaptFundingRecord(OkexWithdrawalRecord r) { - return new FundingRecord.Builder() - .setAddress(r.getTo()) - .setAmount(r.getAmount()) - .setCurrency(Currency.getInstance(r.getCurrency())) - .setDate(r.getTimestamp()) - .setInternalId(r.getWithdrawalId()) - .setStatus(convertWithdrawalStatus(r.getStatus())) - .setBlockchainTransactionHash(r.getTxid()) - .setType(Type.WITHDRAWAL) + return FundingRecord.builder() + .address(r.getTo()) + .amount(r.getAmount()) + .currency(Currency.getInstance(r.getCurrency())) + .date(r.getTimestamp()) + .internalId(r.getWithdrawalId()) + .status(convertWithdrawalStatus(r.getStatus())) + .blockchainTransactionHash(r.getTxid()) + .type(Type.WITHDRAWAL) .build(); } @@ -138,14 +138,14 @@ private static Status convertWithdrawalStatus(String status) { } public static FundingRecord adaptFundingRecord(OkexDepositRecord r) { - return new FundingRecord.Builder() - .setAddress(r.getTo()) - .setAmount(r.getAmount()) - .setCurrency(Currency.getInstance(r.getCurrency())) - .setDate(r.getTimestamp()) - .setStatus(convertDepositStatus(r.getStatus())) - .setBlockchainTransactionHash(r.getTxid()) - .setType(Type.DEPOSIT) + return FundingRecord.builder() + .address(r.getTo()) + .amount(r.getAmount()) + .currency(Currency.getInstance(r.getCurrency())) + .date(r.getTimestamp()) + .status(convertDepositStatus(r.getStatus())) + .blockchainTransactionHash(r.getTxid()) + .type(Type.DEPOSIT) .build(); } /** diff --git a/xchange-paymium/src/main/java/org/knowm/xchange/paymium/service/PaymiumAccountService.java b/xchange-paymium/src/main/java/org/knowm/xchange/paymium/service/PaymiumAccountService.java index 72d68310d0a..341a79c5df2 100644 --- a/xchange-paymium/src/main/java/org/knowm/xchange/paymium/service/PaymiumAccountService.java +++ b/xchange-paymium/src/main/java/org/knowm/xchange/paymium/service/PaymiumAccountService.java @@ -2,11 +2,13 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.knowm.xchange.Exchange; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AccountInfo; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.paymium.PaymiumAdapters; import org.knowm.xchange.paymium.dto.account.PaymiumOrder; import org.knowm.xchange.service.account.AccountService; @@ -70,18 +72,16 @@ public List getFundingHistory(TradeHistoryParams params) throws I } res.add( - new FundingRecord( - order.getBitcoinAddress(), - order.getUpdatedAt(), - Currency.getInstance(order.getCurrency()), - order.getAmount(), - String.valueOf(order.getUuid()), - order.getUuid(), - funding, - FundingRecord.Status.COMPLETE, - null, - null, - null)); + FundingRecord.builder() + .address(order.getBitcoinAddress()) + .date(order.getUpdatedAt()) + .currency(Currency.getInstance(order.getCurrency())) + .amount(order.getAmount()) + .internalId(String.valueOf(order.getUuid())) + .blockchainTransactionHash(order.getUuid()) + .type(funding) + .status(Status.COMPLETE) + .build()); } return res; diff --git a/xchange-poloniex/src/main/java/org/knowm/xchange/poloniex/PoloniexAdapters.java b/xchange-poloniex/src/main/java/org/knowm/xchange/poloniex/PoloniexAdapters.java index 1825b1d321b..fb3c4ff3cfb 100644 --- a/xchange-poloniex/src/main/java/org/knowm/xchange/poloniex/PoloniexAdapters.java +++ b/xchange-poloniex/src/main/java/org/knowm/xchange/poloniex/PoloniexAdapters.java @@ -19,6 +19,7 @@ import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.FundingRecord.Type; import org.knowm.xchange.dto.marketdata.*; import org.knowm.xchange.dto.marketdata.Trades.TradeSortType; @@ -305,41 +306,28 @@ private static FundingRecord adaptAdjustment(PoloniexAdjustment a) { } // There could be other forms of adjustements, but it seems to be some kind of deposit. - return new FundingRecord( - null, - a.getTimestamp(), - Currency.getInstance(a.getCurrency()), - a.getAmount(), - null, - null, - type, - FundingRecord.Status.resolveStatus(a.getStatus()), - null, - null, - a.getCategory() - + ":" - + a.getReason() - + "\n" - + a.getAdjustmentTitle() - + "\n" - + a.getAdjustmentDesc() - + "\n" - + a.getAdjustmentHelp()); + return FundingRecord.builder() + .date(a.getTimestamp()) + .currency(Currency.getInstance(a.getCurrency())) + .amount(a.getAmount()) + .type(type) + .status(Status.resolveStatus(a.getStatus())) + .description(a.getCategory() + ":" + a.getReason() + "\n" + a.getAdjustmentTitle() + "\n" + a.getAdjustmentDesc() + "\n" + a.getAdjustmentHelp()) + .build(); } private static FundingRecord adaptDeposit(final PoloniexDeposit d) { - return new FundingRecord( - d.getAddress(), - d.getTimestamp(), - Currency.getInstance(d.getCurrency()), - d.getAmount(), - String.valueOf(d.getDepositNumber()), - d.getTxid(), - DEPOSIT, - FundingRecord.Status.resolveStatus(d.getStatus()), - null, - null, - d.getStatus()); + return FundingRecord.builder() + .address(d.getAddress()) + .date(d.getTimestamp()) + .currency(Currency.getInstance(d.getCurrency())) + .amount(d.getAmount()) + .internalId(String.valueOf(d.getDepositNumber())) + .blockchainTransactionHash(d.getTxid()) + .type(DEPOSIT) + .status(Status.resolveStatus(d.getStatus())) + .description(d.getStatus()) + .build(); } private static FundingRecord adaptWithdrawal(final PoloniexWithdrawal w) { @@ -351,18 +339,18 @@ private static FundingRecord adaptWithdrawal(final PoloniexWithdrawal w) { // Poloniex returns the fee as an absolute value, that behaviour differs from UserTrades final BigDecimal feeAmount = w.getFee(); - return new FundingRecord( - w.getAddress(), - w.getTimestamp(), - Currency.getInstance(w.getCurrency()), - w.getAmount(), - String.valueOf(w.getWithdrawalNumber()), - externalId, - WITHDRAWAL, - status, - null, - feeAmount, - w.getStatus()); + return FundingRecord.builder() + .address(w.getAddress()) + .date(w.getTimestamp()) + .currency(Currency.getInstance(w.getCurrency())) + .amount(w.getAmount()) + .internalId(String.valueOf(w.getWithdrawalNumber())) + .blockchainTransactionHash(externalId) + .type(WITHDRAWAL) + .status(status) + .fee(feeAmount) + .description(w.getStatus()) + .build(); } public static LimitOrder adaptUserTradesToOrderStatus( diff --git a/xchange-quoine/src/main/java/org/knowm/xchange/quoine/QuoineAdapters.java b/xchange-quoine/src/main/java/org/knowm/xchange/quoine/QuoineAdapters.java index 38cc5658a46..bd6c5c85eb5 100644 --- a/xchange-quoine/src/main/java/org/knowm/xchange/quoine/QuoineAdapters.java +++ b/xchange-quoine/src/main/java/org/knowm/xchange/quoine/QuoineAdapters.java @@ -10,6 +10,7 @@ import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.marketdata.Ticker; @@ -219,17 +220,16 @@ public static FundingRecord adaptFunding( fee = fee == null ? transaction.network_fee : fee.add(transaction.network_fee); } - return new FundingRecord( - null, - DateUtils.fromUnixTime(transaction.createdAt), - currency, - transaction.gross_amount, - transaction.id, - transaction.transaction_hash, - deposit, - FundingRecord.Status.COMPLETE, - null, - fee, - transaction.notes); + return FundingRecord.builder() + .date(DateUtils.fromUnixTime(transaction.createdAt)) + .currency(currency) + .amount(transaction.gross_amount) + .internalId(transaction.id) + .blockchainTransactionHash(transaction.transaction_hash) + .type(deposit) + .status(Status.COMPLETE) + .fee(fee) + .description(transaction.notes) + .build(); } } diff --git a/xchange-stream-bybit/pom.xml b/xchange-stream-bybit/pom.xml new file mode 100644 index 00000000000..c0e026a6351 --- /dev/null +++ b/xchange-stream-bybit/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + org.knowm.xchange + xchange-parent + 5.1.1-SNAPSHOT + + + xchange-stream-bybit + XChange Bybit Stream + + + + org.knowm.xchange + xchange-stream-core + ${project.parent.version} + + + org.knowm.xchange + xchange-stream-service-netty + ${project.parent.version} + + + org.knowm.xchange + xchange-bybit + ${project.parent.version} + + + + \ No newline at end of file diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingAdapters.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingAdapters.java new file mode 100644 index 00000000000..e5670f2f8ea --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingAdapters.java @@ -0,0 +1,18 @@ +package info.bitrich.xchangestream.bybit; + +import java.util.ArrayList; +import java.util.List; +import org.knowm.xchange.bybit.BybitAdapters; +import org.knowm.xchange.bybit.dto.trade.BybitUserTradeDto; +import org.knowm.xchange.dto.trade.UserTrade; + +public class BybitStreamingAdapters { + + public static List adaptStreamingUserTradeList(List data) { + List userTrades = new ArrayList<>(); + + data.forEach(bybitUserTradeData -> userTrades.add(BybitAdapters.adaptUserTrade(bybitUserTradeData, bybitUserTradeData.getCategory()))); + + return userTrades; + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java new file mode 100644 index 00000000000..75a77977896 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java @@ -0,0 +1,86 @@ +package info.bitrich.xchangestream.bybit; + +import info.bitrich.xchangestream.core.ProductSubscription; +import info.bitrich.xchangestream.core.StreamingExchange; +import info.bitrich.xchangestream.core.StreamingTradeService; +import info.bitrich.xchangestream.service.netty.ConnectionStateModel; +import io.reactivex.Completable; +import io.reactivex.Observable; +import org.knowm.xchange.bybit.BybitExchange; + +public class BybitStreamingExchange extends BybitExchange implements StreamingExchange { + + private enum MarketType { + SPOT, + LINEAR, + INVERSE, + OPTION + } + private BybitStreamingService streamingService; + + private BybitStreamingTradeService streamingTradeService; + + @Override + public Completable connect(ProductSubscription... args) { + + if(exchangeSpecification.getApiKey() != null){ + streamingService = new BybitStreamingService(getBybitURI(useSandbox(exchangeSpecification), true, ""), exchangeSpecification); + streamingTradeService = new BybitStreamingTradeService(streamingService); + + } else { + streamingService = new BybitStreamingService(getBybitURI(useSandbox(exchangeSpecification), false, MarketType.SPOT.toString().toLowerCase()), exchangeSpecification); + } + + return streamingService.connect(); + } + + private String getBybitURI(boolean isSandBox, boolean isAuthenticated, String marketType){ + return "wss://stream"+ (isSandBox ? "-testnet" : "") + ".bybit.com/v5/" + ((isAuthenticated) ? "private" : "public/" + marketType); + } + + @Override + public boolean isAlive() { + return streamingService != null && streamingService.isSocketOpen(); + } + + @Override + public StreamingTradeService getStreamingTradeService() { + return streamingTradeService; + } + + @Override + public Completable disconnect() { + streamingService.pingPongDisconnectIfConnected(); + return streamingService.disconnect(); + } + + @Override + public Observable connectionSuccess() { + return streamingService.subscribeConnectionSuccess(); + } + + @Override + public Observable disconnectObservable() { + return streamingService.subscribeDisconnect(); + } + + @Override + public Observable reconnectFailure() { + return streamingService.subscribeReconnectFailure(); + } + + @Override + public Observable connectionStateObservable() { + return streamingService.subscribeConnectionState(); + } + + @Override + public void useCompressedMessages(boolean compressedMessages) { + streamingService.useCompressedMessages(compressedMessages); + } + + @Override + public void resubscribeChannels() { + streamingService.resubscribeChannels(); + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java new file mode 100644 index 00000000000..282d0ae0bc3 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java @@ -0,0 +1,118 @@ +package info.bitrich.xchangestream.bybit; + +import static org.knowm.xchange.utils.DigestUtils.bytesToHex; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import info.bitrich.xchangestream.bybit.dto.BybitStreamingDto; +import info.bitrich.xchangestream.bybit.dto.BybitStreamingDto.Op; +import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService; +import info.bitrich.xchangestream.service.netty.WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler; +import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; +import io.reactivex.Completable; +import io.reactivex.CompletableSource; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.exceptions.ExchangeException; +import org.knowm.xchange.service.BaseParamsDigest; + +public class BybitStreamingService extends JsonNettyStreamingService { + + private final ExchangeSpecification exchangeSpecification; + + private final Observable pingPongSrc = Observable.interval(15, 15, TimeUnit.SECONDS); + private Disposable pingPongSubscription; + + public BybitStreamingService(String apiUrl, ExchangeSpecification exchangeSpecification) { + super(apiUrl); + this.exchangeSpecification = exchangeSpecification; + } + + @Override + public Completable connect() { + Completable conn = super.connect(); + return conn.andThen( + (CompletableSource) + (completable) -> { + try { + if(exchangeSpecification.getApiKey() != null){ + login(); + } + + if (pingPongSubscription != null && !pingPongSubscription.isDisposed()) { + pingPongSubscription.dispose(); + } + + pingPongSubscription = pingPongSrc.subscribe(o ->this.sendMessage("{ \"op\": \"ping\" }")); + completable.onComplete(); + } catch (Exception e) { + completable.onError(e); + } + }); + } + + public void login() throws JsonProcessingException { + Mac mac; + try { + mac = Mac.getInstance(BaseParamsDigest.HMAC_SHA_256); + final SecretKey secretKey = + new SecretKeySpec(exchangeSpecification.getSecretKey().getBytes(StandardCharsets.UTF_8), BaseParamsDigest.HMAC_SHA_256); + mac.init(secretKey); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new ExchangeException("Invalid API secret", e); + } + + long expires = Instant.now().plus(20, ChronoUnit.MINUTES).toEpochMilli(); + mac.update(("GET/realtime"+expires).getBytes(StandardCharsets.UTF_8)); + String signature = bytesToHex(mac.doFinal()); + + BybitStreamingDto message = new BybitStreamingDto(Op.auth, Arrays.asList(exchangeSpecification.getApiKey(), expires, signature)); + + this.sendMessage(objectMapper.writeValueAsString(message)); + } + + @Override + protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() { + return WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler.INSTANCE; + } + + @Override + protected String getChannelNameFromMessage(JsonNode message) throws IOException { + String channelName = ""; + + if(message.has("topic")){ + channelName = message.get("topic").asText(); + } + + return channelName; + } + + @Override + public String getSubscribeMessage(String channelName, Object... args) throws IOException { + return objectMapper.writeValueAsString(new BybitStreamingDto(Op.subscribe, Collections.singletonList(channelName))); + } + + @Override + public String getUnsubscribeMessage(String channelName, Object... args) throws IOException { + return objectMapper.writeValueAsString(new BybitStreamingDto(Op.unsubscribe, Collections.singletonList(channelName))); + } + + public void pingPongDisconnectIfConnected() { + if (pingPongSubscription != null && !pingPongSubscription.isDisposed()) { + pingPongSubscription.dispose(); + } + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingTradeService.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingTradeService.java new file mode 100644 index 00000000000..70fb3481ba2 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingTradeService.java @@ -0,0 +1,42 @@ +package info.bitrich.xchangestream.bybit; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.bybit.dto.BybitUserTradeResponseDto; +import info.bitrich.xchangestream.core.StreamingTradeService; +import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper; +import io.reactivex.Observable; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.instrument.Instrument; + +public class BybitStreamingTradeService implements StreamingTradeService { + + private static final String EXECUTION_CHANNEL = "execution"; + private final BybitStreamingService streamingService; + + private final ObjectMapper objectMapper = StreamingObjectMapperHelper.getObjectMapper(); + + public BybitStreamingTradeService(BybitStreamingService streamingService) { + this.streamingService = streamingService; + } + @Override + public Observable getUserTrades(Instrument instrument, Object... args) { + return getUserTrades(); + } + + @Override + public Observable getUserTrades(CurrencyPair currencyPair, Object... args) { + return getUserTrades(); + } + + @Override + public Observable getUserTrades() { + return streamingService + .subscribeChannel(EXECUTION_CHANNEL) + .filter(jsonNode -> jsonNode.has("topic")) + .filter(jsonNode -> jsonNode.get("topic").asText().equals(EXECUTION_CHANNEL)) + .map(s -> objectMapper.treeToValue(s, BybitUserTradeResponseDto.class)) + .map(bybitUserTradeResponseDto -> BybitStreamingAdapters.adaptStreamingUserTradeList(bybitUserTradeResponseDto.getData())) + .flatMap(Observable::fromIterable); + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitStreamingDto.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitStreamingDto.java new file mode 100644 index 00000000000..1aeb939d1f3 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitStreamingDto.java @@ -0,0 +1,23 @@ +package info.bitrich.xchangestream.bybit.dto; + +import java.util.List; +import lombok.Data; + +@Data +public class BybitStreamingDto { + + private Op op; + + List args; + + public BybitStreamingDto(Op op, List args) { + this.op = op; + this.args = args; + } + + public enum Op { + auth, + subscribe, + unsubscribe + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitUserTradeResponseDto.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitUserTradeResponseDto.java new file mode 100644 index 00000000000..0c914aa7c85 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/dto/BybitUserTradeResponseDto.java @@ -0,0 +1,31 @@ +package info.bitrich.xchangestream.bybit.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Date; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.knowm.xchange.bybit.dto.trade.BybitUserTradeDto; + +@ToString +@Getter +@AllArgsConstructor +public class BybitUserTradeResponseDto { + + @JsonProperty("id") + private String id; + + @JsonProperty("topic") + private String topic; + + @JsonProperty("creationTime") + private Date creationTime; + + @JsonProperty("data") + private List data; + + /* No args constructor for use in serialization */ + public BybitUserTradeResponseDto() { + } +} diff --git a/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamingPrivateEndpointsTest.java b/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamingPrivateEndpointsTest.java new file mode 100644 index 00000000000..87e794e3da6 --- /dev/null +++ b/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamingPrivateEndpointsTest.java @@ -0,0 +1,52 @@ +package info.bitrich.xchangestream.bybit; + +import info.bitrich.xchangestream.core.ProductSubscription; +import info.bitrich.xchangestream.core.StreamingExchange; +import info.bitrich.xchangestream.core.StreamingExchangeFactory; +import java.io.IOException; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bybit.BybitExchange; +import org.knowm.xchange.derivative.FuturesContract; +import org.knowm.xchange.instrument.Instrument; + +@Ignore +public class BybitStreamingPrivateEndpointsTest { + + Instrument instrument = new FuturesContract("BTC/USDT/PERP"); + StreamingExchange exchange; + @Before + public void setUp() { + Properties properties = new Properties(); + + try { + properties.load(this.getClass().getResourceAsStream("/secret.keys")); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ExchangeSpecification spec = new BybitExchange().getDefaultExchangeSpecification(); + + spec.setApiKey(properties.getProperty("apikey")); + spec.setSecretKey(properties.getProperty("secret")); + spec.setExchangeSpecificParametersItem(Exchange.USE_SANDBOX, true); + + exchange = StreamingExchangeFactory.INSTANCE.createExchange(BybitStreamingExchange.class); + exchange.applySpecification(spec); + + exchange.connect(ProductSubscription.create().build()).blockingAwait(); + } + + @Test + public void testUserTrades() throws InterruptedException { + + exchange.getStreamingTradeService().getUserTrades(instrument).subscribe(System.out::println); + + TimeUnit.SECONDS.sleep(5); + } +} diff --git a/xchange-stream-bybit/src/test/resources/logback.xml b/xchange-stream-bybit/src/test/resources/logback.xml new file mode 100644 index 00000000000..a08cb474a54 --- /dev/null +++ b/xchange-stream-bybit/src/test/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + + + + + %d{HH:mm:ss.SSS} [%contextName] [%thread] %-5level %logger{36} - %msg %xEx%n + + + + + + + + + + + + + + diff --git a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingService.java b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingService.java index a1cf2d7541e..1fa6dbe32a4 100644 --- a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingService.java +++ b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingService.java @@ -23,7 +23,7 @@ import java.util.stream.Stream; import org.knowm.xchange.coinbasepro.dto.account.CoinbaseProWebsocketAuthData; -import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.instrument.Instrument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,12 +95,12 @@ public Observable subscribeChannel(String channelName, Object... args) /** * Subscribes to web socket transactions related to the specified currency, in their raw format. * - * @param currencyPair The currency pair. + * @param instrument The currency pair. * @return The stream. */ public Observable getRawWebSocketTransactions( - CurrencyPair currencyPair, boolean filterChannelName) { - String channelName = currencyPair.base.toString() + "-" + currencyPair.counter.toString(); + Instrument instrument, boolean filterChannelName) { + String channelName = instrument.getBase().toString() + "-" + instrument.getCounter().toString(); final ObjectMapper mapper = StreamingObjectMapperHelper.getObjectMapper(); return subscribeChannel(channelName) .map(s -> mapToTransaction(mapper, s)) @@ -169,17 +169,16 @@ private static CoinbaseProWebSocketTransaction mapToTransaction(ObjectMapper map String type = getText(node.get("type")); // use manual JSON to object conversion for the heaviest transaction types if (type != null && (type.equals("l2update") || type.equals("snapshot"))) { - return new CoinbaseProWebSocketTransaction(type, - null, null, null, null, null, null, null, null, null, null, null, null, null, - getL2Array(node.get("bids")), - getL2Array(node.get("asks")), - getL2Array(node.get("changes")), - null, - getText(node.get("product_id")), - 0, - getText(node.get("time")), - null, 0, null, null, null, null, null, null - ); + return CoinbaseProWebSocketTransaction.builder() + .type(type) + .bids(getL2Array(node.get("bids"))) + .asks(getL2Array(node.get("asks"))) + .changes(getL2Array(node.get("changes"))) + .productId(getText(node.get("product_id"))) + .sequence(0) + .time(getText(node.get("time"))) + .tradeId(0) + .build(); } return mapper.treeToValue(node, CoinbaseProWebSocketTransaction.class); } diff --git a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingTradeService.java b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingTradeService.java index bb3fc91fbd4..663e9cd2901 100644 --- a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingTradeService.java +++ b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingTradeService.java @@ -6,7 +6,6 @@ import info.bitrich.xchangestream.core.StreamingTradeService; import io.reactivex.Observable; import java.util.Collections; -import java.util.List; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order; @@ -29,29 +28,28 @@ public class CoinbaseProStreamingTradeService implements StreamingTradeService { this.service = service; } - private boolean containsPair(List pairs, CurrencyPair pair) { - for (Instrument item : pairs) { - if (pair.compareTo((CurrencyPair) item) == 0) { - return true; - } - } + @Override + public Observable getUserTrades(CurrencyPair currencyPair, Object... args) { + checkPairAndAuthentication(currencyPair); - return false; + return service + .getRawWebSocketTransactions(currencyPair, true) + .filter(message -> message.getType().equals(MATCH)) + .filter((CoinbaseProWebSocketTransaction s) -> s.getUserId() != null) + .map(CoinbaseProWebSocketTransaction::toCoinbaseProFill) + .map((CoinbaseProFill f) -> adaptTradeHistory(Collections.singletonList(f))) + .map((UserTrades h) -> h.getUserTrades().get(0)); } @Override - public Observable getUserTrades(CurrencyPair currencyPair, Object... args) { - if (!containsPair(service.getProduct().getUserTrades(), currencyPair)) - throw new UnsupportedOperationException( - String.format("The currency pair %s is not subscribed for user trades", currencyPair)); - if (!service.isAuthenticated()) { - throw new ExchangeSecurityException("Not authenticated"); - } + public Observable getUserTrades(Instrument instrument, Object... args) { + checkPairAndAuthentication(instrument); + return service - .getRawWebSocketTransactions(currencyPair, true) + .getRawWebSocketTransactions(instrument, true) .filter(message -> message.getType().equals(MATCH)) .filter((CoinbaseProWebSocketTransaction s) -> s.getUserId() != null) - .map((CoinbaseProWebSocketTransaction s) -> s.toCoinbaseProFill()) + .map(CoinbaseProWebSocketTransaction::toCoinbaseProFill) .map((CoinbaseProFill f) -> adaptTradeHistory(Collections.singletonList(f))) .map((UserTrades h) -> h.getUserTrades().get(0)); } @@ -64,12 +62,8 @@ public Observable getUserTrades(CurrencyPair currencyPair, Object... */ @Override public Observable getOrderChanges(CurrencyPair currencyPair, Object... args) { - if (!containsPair(service.getProduct().getOrders(), currencyPair)) - throw new UnsupportedOperationException( - String.format("The currency pair %s is not subscribed for orders", currencyPair)); - if (!service.isAuthenticated()) { - throw new ExchangeSecurityException("Not authenticated"); - } + checkPairAndAuthentication(currencyPair); + if (!orderChangesWarningLogged) { LOG.warn( "The order change stream is not yet fully implemented for Coinbase Pro. " @@ -83,6 +77,15 @@ public Observable getOrderChanges(CurrencyPair currencyPair, Object... ar .map(CoinbaseProStreamingAdapters::adaptOrder); } + private void checkPairAndAuthentication(Instrument instrument) { + if (!service.getProduct().getUserTrades().contains(instrument)) + throw new UnsupportedOperationException( + String.format("The currency pair %s is not subscribed for user trades", instrument)); + if (!service.isAuthenticated()) { + throw new ExchangeSecurityException("Not authenticated"); + } + } + /** * Web socket transactions related to the specified currency, in their raw format. * diff --git a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/dto/CoinbaseProWebSocketTransaction.java b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/dto/CoinbaseProWebSocketTransaction.java index 4d01ed44bc9..4bab270b2f3 100644 --- a/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/dto/CoinbaseProWebSocketTransaction.java +++ b/xchange-stream-coinbasepro/src/main/java/info/bitrich/xchangestream/coinbasepro/dto/CoinbaseProWebSocketTransaction.java @@ -8,16 +8,23 @@ import java.util.SortedMap; import java.util.TimeZone; import java.util.stream.Stream; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductStats; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProProductTicker; import org.knowm.xchange.coinbasepro.dto.marketdata.CoinbaseProTrade; import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill; +import org.knowm.xchange.coinbasepro.dto.trade.CoinbaseProFill.Side; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.marketdata.OrderBook; import org.knowm.xchange.dto.trade.LimitOrder; /** Domain object mapping a CoinbasePro web socket message. */ +@Getter +@ToString +@Builder public class CoinbaseProWebSocketTransaction { private final String type; private final String orderId; @@ -46,6 +53,8 @@ public class CoinbaseProWebSocketTransaction { private final String takerOrderId; private final String takerUserId; + private final BigDecimal takerFeeRate; + private final BigDecimal makerFeeRate; private final String userId; private final String takerProfileId; private final String profileId; @@ -77,6 +86,8 @@ public CoinbaseProWebSocketTransaction( @JsonProperty("maker_order_id") String makerOrderId, @JsonProperty("taker_order_id") String takerOrderId, @JsonProperty("taker_user_id") String takerUserId, + @JsonProperty("taker_fee_rate") BigDecimal takerFeeRate, + @JsonProperty("maker_fee_rate") BigDecimal makerFeeRate, @JsonProperty("user_id") String userId, @JsonProperty("taker_profile_id") String takerProfileId, @JsonProperty("profile_id") String profileId) { @@ -107,6 +118,8 @@ public CoinbaseProWebSocketTransaction( this.sequence = sequence; this.time = time; this.takerUserId = takerUserId; + this.takerFeeRate = takerFeeRate; + this.makerFeeRate = makerFeeRate; this.userId = userId; this.takerProfileId = takerProfileId; this.profileId = profileId; @@ -210,160 +223,16 @@ public CoinbaseProFill toCoinbaseProFill() { useSide = "buy"; } } - return new CoinbaseProFill( - String.valueOf(tradeId), - productId, - price, - size, - taker ? takerOrderId : makerOrderId, - time, - null, - null, - true, - useSide); - } - - public String getType() { - return type; - } - - public String getOrderId() { - return orderId; - } - - public String getOrderType() { - return orderType; - } - - public BigDecimal getSize() { - return size; - } - - public BigDecimal getPrice() { - return price; - } - - public BigDecimal getBestBid() { - return bestBid; - } - - public BigDecimal getBestAsk() { - return bestAsk; - } - - public BigDecimal getLastSize() { - return lastSize; - } - - public BigDecimal getVolume24h() { - return volume24h; - } - - public BigDecimal getOpen24h() { - return open24h; - } - - public BigDecimal getLow24h() { - return low24h; - } - - public BigDecimal getHigh24h() { - return high24h; - } - - public String getSide() { - return side; - } - - public String getClientOid() { - return clientOid; - } - - public String getProductId() { - return productId; - } - - public Long getSequence() { - return sequence; - } - - public String getTime() { - return time; - } - - public BigDecimal getRemainingSize() { - return remainingSize; - } - - public String getReason() { - return reason; - } - - public long getTradeId() { - return tradeId; - } - - public String getMakerOrderId() { - return makerOrderId; - } - - /** @deprecated Use {@link #getTakerOrderId()} */ - @Deprecated - public String getTakenOrderId() { - return takerOrderId; - } - - public String getTakerOrderId() { - return takerOrderId; - } - - public String getTakerUserId() { - return takerUserId; - } - - public String getUserId() { - return userId; - } - - public String getTakerProfileId() { - return takerProfileId; - } - - public String getProfileId() { - return profileId; - } - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("CoinbaseProWebSocketTransaction{"); - sb.append("type='").append(type).append('\''); - sb.append(", orderId='").append(orderId).append('\''); - sb.append(", orderType='").append(orderType).append('\''); - sb.append(", size=").append(size); - sb.append(", remainingSize=").append(remainingSize); - sb.append(", price=").append(price); - sb.append(", bestBid=").append(bestBid); - sb.append(", bestAsk=").append(bestAsk); - sb.append(", lastSize=").append(lastSize); - sb.append(", volume24h=").append(volume24h); - sb.append(", open24h=").append(open24h); - sb.append(", low24h=").append(low24h); - sb.append(", high24h=").append(high24h); - sb.append(", side='").append(side).append('\''); - sb.append(", bids=").append(bids); - sb.append(", asks=").append(asks); - sb.append(", changes=").append(asks); - sb.append(", clientOid='").append(clientOid).append('\''); - sb.append(", productId='").append(productId).append('\''); - sb.append(", sequence=").append(sequence); - sb.append(", time='").append(time).append('\''); - sb.append(", reason='").append(reason).append('\''); - sb.append(", trade_id='").append(tradeId).append('\''); - if (userId != null) sb.append(", userId='").append(userId).append('\''); - if (profileId != null) sb.append(", profileId='").append(profileId).append('\''); - if (takerUserId != null) sb.append(", takerUserId='").append(takerUserId).append('\''); - if (takerProfileId != null) sb.append(", takerProfileId='").append(takerProfileId).append('\''); - sb.append('}'); - return sb.toString(); + return CoinbaseProFill.builder() + .tradeId(String.valueOf(tradeId)) + .productId(productId) + .price(price) + .size(size) + .orderId(taker ? takerOrderId : makerOrderId) + .createdAt(time) + .fee(taker ? price.multiply(size).multiply(takerFeeRate) : price.multiply(size).multiply(makerFeeRate)) + .side(Side.valueOf(useSide)) + .settled(true) + .build(); } } diff --git a/xchange-stream-coinbasepro/src/test/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingPrivateIntegration.java b/xchange-stream-coinbasepro/src/test/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingPrivateIntegration.java new file mode 100644 index 00000000000..4d7bfda2998 --- /dev/null +++ b/xchange-stream-coinbasepro/src/test/java/info/bitrich/xchangestream/coinbasepro/CoinbaseProStreamingPrivateIntegration.java @@ -0,0 +1,93 @@ +package info.bitrich.xchangestream.coinbasepro; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import info.bitrich.xchangestream.core.ProductSubscription; +import info.bitrich.xchangestream.core.StreamingExchange; +import info.bitrich.xchangestream.core.StreamingExchangeFactory; +import io.reactivex.disposables.Disposable; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import org.junit.Before; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.instrument.Instrument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CoinbaseProStreamingPrivateIntegration { + + StreamingExchange exchange; + private static final Logger LOG = LoggerFactory.getLogger(CoinbaseProStreamingPrivateIntegration.class); + Instrument instrument = new CurrencyPair("BTC/USD"); + + @Before + public void setUp() { + Properties properties = new Properties(); + + try { + properties.load(CoinbaseProStreamingPrivateIntegration.class.getResourceAsStream("/secret.keys")); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ExchangeSpecification spec = new CoinbaseProStreamingExchange().getDefaultExchangeSpecification(); + + spec.setApiKey(properties.getProperty("coinbaseApi")); + spec.setSecretKey(properties.getProperty("coinbaseSecret")); + spec.setExchangeSpecificParametersItem("passphrase", properties.getProperty("coinbasePassphrase")); + spec.setExchangeSpecificParametersItem(Exchange.USE_SANDBOX, true); + + exchange = StreamingExchangeFactory.INSTANCE.createExchange(spec); + } + + @Test + public void testUserTrades() throws InterruptedException, IOException { + exchange + .connect(ProductSubscription.create().addAll(instrument).build()) + .blockingAwait(); + + List userTradeList = new ArrayList<>(); + + Disposable dis = exchange.getStreamingTradeService().getUserTrades(instrument).subscribe(userTrade -> { + LOG.info(userTrade.toString()); + userTradeList.add(userTrade); + }); + int count = 0; + + while (count < 5){ + TimeUnit.SECONDS.sleep(2); + exchange.getTradeService().placeMarketOrder(new MarketOrder.Builder(OrderType.BID, instrument) + .originalAmount(BigDecimal.valueOf(0.0001)) + .build()); + count++; + } + dis.dispose(); + + assertThat(userTradeList.size()).isGreaterThanOrEqualTo(4); + userTradeList.forEach(userTrade -> { + assertThat(userTrade.getPrice()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getType()).isEqualTo(OrderType.BID); + assertThat(userTrade.getInstrument()).isEqualTo(instrument); + assertThat(userTrade.getOriginalAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getFeeAmount()).isGreaterThan(BigDecimal.ZERO); + assertThat(userTrade.getOrderId()).isNotNull(); + assertThat(userTrade.getId()).isNotNull(); + assertThat(userTrade.getFeeCurrency()).isInstanceOf(Currency.class); + assertThat(userTrade.getTimestamp()).isAfter(Date.from(Instant.now().minus(5, ChronoUnit.MINUTES))); + }); + } +} diff --git a/xchange-therock/src/main/java/org/knowm/xchange/therock/service/TheRockAccountService.java b/xchange-therock/src/main/java/org/knowm/xchange/therock/service/TheRockAccountService.java index f0832ad7462..5cbdec2cb0a 100644 --- a/xchange-therock/src/main/java/org/knowm/xchange/therock/service/TheRockAccountService.java +++ b/xchange-therock/src/main/java/org/knowm/xchange/therock/service/TheRockAccountService.java @@ -9,6 +9,7 @@ import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AccountInfo; import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Status; import org.knowm.xchange.exceptions.NotAvailableFromExchangeException; import org.knowm.xchange.service.account.AccountService; import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams; @@ -39,18 +40,16 @@ private static FundingRecord adapt(TheRockTransaction txn, FundingRecord.Type ty address = transferDetail.getRecipient(); } - return new FundingRecord( - address, - txn.getDate(), - Currency.getInstance(txn.getCurrency()), - txn.getPrice(), - String.valueOf(txn.getId()), - transferDetailId, - type, - FundingRecord.Status.COMPLETE, - null, - null, - null); + return FundingRecord.builder() + .address(address) + .date(txn.getDate()) + .currency(Currency.getInstance(txn.getCurrency())) + .amount(txn.getPrice()) + .internalId(String.valueOf(txn.getId())) + .blockchainTransactionHash(transferDetailId) + .type(type) + .status(Status.COMPLETE) + .build(); } @Override