From 2b18ca4fe12bcb6addabf3b9a83221d545801320 Mon Sep 17 00:00:00 2001 From: rizer1980 <4340180@gmail.com> Date: Sun, 15 Mar 2026 21:47:28 +0300 Subject: [PATCH 1/4] [bybit,binance,okx] implement funding rate history --- .../knowm/xchange/binance/BinanceFutures.java | 20 ++++ .../xchange/binance/BinanceResilience.java | 9 ++ .../marketdata/BinanceFundingRateHistory.java | 30 ++++++ .../service/BinanceMarketDataService.java | 11 +- .../service/BinanceMarketDataServiceRaw.java | 10 ++ .../xchange/binance/BinanceFutureTest.java | 5 + .../java/org/knowm/xchange/bybit/Bybit.java | 14 +++ .../marketdata/BybitFundingRateHistory.java | 22 ++++ .../BybitFundingRateHistoryRaw.java | 24 +++++ .../bybit/service/BybitMarketDataService.java | 13 +++ .../service/BybitMarketDataServiceRaw.java | 7 ++ .../service/BybitMarketDataServiceTest.java | 19 +++- .../resources/getFundingRateHistory.json5 | 16 +++ .../java/org/knowm/xchange/okex/Okex.java | 17 ++- .../dto/marketdata/OkxFundingRateHistory.java | 36 +++++++ .../okex/service/OkexMarketDataService.java | 5 + .../service/OkexMarketDataServiceRaw.java | 102 ++++++++++-------- .../okex/OkexPublicDataIntegration.java | 12 +++ 18 files changed, 325 insertions(+), 47 deletions(-) create mode 100644 xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java create mode 100644 xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistory.java create mode 100644 xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistoryRaw.java create mode 100644 xchange-bybit/src/test/resources/getFundingRateHistory.json5 create mode 100644 xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceFutures.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceFutures.java index 76700b622c1..34342328c67 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceFutures.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceFutures.java @@ -10,6 +10,7 @@ import org.knowm.xchange.binance.dto.BinanceException; import org.knowm.xchange.binance.dto.marketdata.BinanceAggTrades; import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRate; +import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateHistory; import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateInfo; import org.knowm.xchange.binance.dto.marketdata.BinanceOrderbook; import org.knowm.xchange.binance.dto.marketdata.BinanceTicker24h; @@ -154,4 +155,23 @@ List klines( @QueryParam("startTime") Long startTime, @QueryParam("endTime") Long endTime) throws IOException, BinanceException; + + /** + * Get Funding Rate History + * + * @param symbol optional, instrument + * @param limit optional, Default 100; + * @param startTime optional, Timestamp in ms to get funding rate from INCLUSIVE. + * @param endTime optional, Timestamp in ms to get funding rate until INCLUSIVE. + * @throws IOException + * @throws BinanceException + */ + @GET + @Path("fapi/v1/fundingRate") + List fundingRateHistory( + @QueryParam("symbol") String symbol, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit) + throws IOException, BinanceException; } diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceResilience.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceResilience.java index 799b74409d9..4f37d540b31 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceResilience.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/BinanceResilience.java @@ -16,6 +16,7 @@ public final class BinanceResilience { // Futures specified public static final String ORDERS_PER_MINUTE_RATE_LIMITER = "ordersPerMINUTE"; + public static final String FUNDING_RATE_AND_INFO_RATE_LIMITER = "fundingRateAndInfo"; private BinanceResilience() {} @@ -68,6 +69,14 @@ public static ResilienceRegistries createRegistries() { public static ResilienceRegistries createRegistriesFuture() { ResilienceRegistries registries = new ResilienceRegistries(); + registries + .rateLimiters() + .rateLimiter( + FUNDING_RATE_AND_INFO_RATE_LIMITER, + RateLimiterConfig.from(registries.rateLimiters().getDefaultConfig()) + .limitRefreshPeriod(Duration.ofMinutes(5)) + .limitForPeriod(500) + .build()); registries .rateLimiters() .rateLimiter( diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java new file mode 100644 index 00000000000..3ac71264842 --- /dev/null +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java @@ -0,0 +1,30 @@ +package org.knowm.xchange.binance.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Getter; +import lombok.ToString; +import org.knowm.xchange.binance.BinanceAdapters; +import org.knowm.xchange.instrument.Instrument; + +@Getter +@ToString +public class BinanceFundingRateHistory { + + private final Instrument instrument; + private final BigDecimal fundingRate; + private final Date fundingTime; + private final BigDecimal markPrice; + + public BinanceFundingRateHistory( + @JsonProperty("symbol") String symbol, + @JsonProperty("fundingRate") BigDecimal fundingRate, + @JsonProperty("fundingTime") long fundingTime, + @JsonProperty("markPrice") BigDecimal markPrice) { + this.instrument = BinanceAdapters.adaptSymbol(symbol, true); + this.fundingRate = fundingRate; + this.fundingTime = new Date(fundingTime); + this.markPrice = markPrice; + } +} diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java index 8c56b791441..6a4cdc6c007 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java @@ -11,13 +11,18 @@ import org.knowm.xchange.binance.BinanceErrorAdapter; import org.knowm.xchange.binance.BinanceExchange; import org.knowm.xchange.binance.dto.BinanceException; +import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateHistory; import org.knowm.xchange.binance.dto.marketdata.BinanceOrderbook; import org.knowm.xchange.binance.dto.marketdata.BinanceTicker24h; import org.knowm.xchange.client.ResilienceRegistries; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.derivative.FuturesContract; import org.knowm.xchange.dto.Order.OrderType; -import org.knowm.xchange.dto.marketdata.*; +import org.knowm.xchange.dto.marketdata.FundingRate; +import org.knowm.xchange.dto.marketdata.FundingRates; +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.ExchangeHealth; import org.knowm.xchange.dto.trade.LimitOrder; import org.knowm.xchange.exceptions.ExchangeException; @@ -146,6 +151,10 @@ private OrderBook getBinanceOrderBook(Instrument instrument, Object... args) thr } } + public List fundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + return fundingRateHistoryRaw(instrument, startTime, endTime, limit); + } + public static OrderBook convertOrderBook(BinanceOrderbook ob, Instrument pair) { List bids = ob.bids.entrySet().stream() diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataServiceRaw.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataServiceRaw.java index 6c91d7059ce..f376e336684 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataServiceRaw.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataServiceRaw.java @@ -1,5 +1,6 @@ package org.knowm.xchange.binance.service; +import static org.knowm.xchange.binance.BinanceResilience.FUNDING_RATE_AND_INFO_RATE_LIMITER; import static org.knowm.xchange.binance.BinanceResilience.REQUEST_WEIGHT_RATE_LIMITER; import java.io.IOException; @@ -9,6 +10,7 @@ import org.knowm.xchange.binance.BinanceExchange; import org.knowm.xchange.binance.dto.marketdata.BinanceAggTrades; import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRate; +import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateHistory; import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateInfo; import org.knowm.xchange.binance.dto.marketdata.BinanceKline; import org.knowm.xchange.binance.dto.marketdata.BinanceOrderbook; @@ -171,6 +173,7 @@ public BinanceFundingRate getBinanceFundingRate(Instrument instrument) throws IO public List getBinanceFundingRateInfo() throws IOException { return decorateApiCall(() -> binanceFutures.fundingRateInfo()) + .withRateLimiter(rateLimiter(FUNDING_RATE_AND_INFO_RATE_LIMITER)) .withRetry(retry("fundingRate")) .call(); } @@ -195,6 +198,13 @@ public List tickerAllBookTickers() throws IOException { .call(); } + public List fundingRateHistoryRaw(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + return decorateApiCall(() -> binanceFutures.fundingRateHistory(BinanceAdapters.toSymbol(instrument), startTime, endTime, limit)) + .withRetry(retry("fundingRateHistory")) + .withRateLimiter(rateLimiter(FUNDING_RATE_AND_INFO_RATE_LIMITER)) + .call(); + } + protected int depthPermits(Integer limit) { if (limit == null || limit <= 100) { return 5; diff --git a/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java b/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java index de85dadc419..b6fdc6dbc11 100644 --- a/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java +++ b/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java @@ -19,10 +19,12 @@ import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.binance.dto.marketdata.BinanceFundingRateHistory; import org.knowm.xchange.binance.dto.trade.BinanceCancelOrderParams; import org.knowm.xchange.binance.dto.trade.BinanceQueryOrderParams; import org.knowm.xchange.binance.dto.trade.BinanceTradeHistoryParams; import org.knowm.xchange.binance.service.BinanceAccountService; +import org.knowm.xchange.binance.service.BinanceMarketDataService; import org.knowm.xchange.derivative.FuturesContract; import org.knowm.xchange.dto.Order; import org.knowm.xchange.dto.account.AccountInfo; @@ -87,6 +89,9 @@ public void binanceFutureMarketDataService() throws IOException { fundingRates .getFundingRates() .forEach(fundingRate -> System.out.println(fundingRate.toString())); + List fundingRateHistory = ((BinanceMarketDataService) binanceExchange.getMarketDataService()) + .fundingRateHistory(instrument, System.currentTimeMillis() - 24 * 60 * 60 * 1000, System.currentTimeMillis(), null); + fundingRateHistory.forEach(fundingRate -> System.out.println(fundingRate.toString())); } @Test 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 2888bc6d2cd..cd39c646138 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 @@ -6,7 +6,9 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import java.io.IOException; +import org.knowm.xchange.bybit.dto.BybitCategorizedPayload; import org.knowm.xchange.bybit.dto.BybitResult; +import org.knowm.xchange.bybit.dto.marketdata.BybitFundingRateHistoryRaw; 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; @@ -42,4 +44,16 @@ BybitResult> getInstrumentsInfo( @Path("/tickers") BybitResult> getTickers(@QueryParam("category") String category) throws IOException, BybitException; + + /** + * @apiSpec API + */ + @GET + @Path("/funding/history") + BybitResult> getFundingHistory(@QueryParam("category") String category, + @QueryParam("symbol") String symbol, + @QueryParam("startTime") Long startTime, + @QueryParam("endTime") Long endTime, + @QueryParam("limit") Integer limit) + throws IOException, BybitException; } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistory.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistory.java new file mode 100644 index 00000000000..f5ea9541c4f --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistory.java @@ -0,0 +1,22 @@ +package org.knowm.xchange.bybit.dto.marketdata; + +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Getter; +import org.knowm.xchange.instrument.Instrument; + +@Getter +public class BybitFundingRateHistory { + + private final Instrument instrument; + + private final BigDecimal fundingRate; + + private final Instant fundingRateTimestamp; + + public BybitFundingRateHistory(Instrument instrument, BigDecimal fundingRate, Instant fundingRateTimestamp) { + this.instrument = instrument; + this.fundingRate = fundingRate; + this.fundingRateTimestamp = fundingRateTimestamp; + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistoryRaw.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistoryRaw.java new file mode 100644 index 00000000000..ad98f17b2f2 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/marketdata/BybitFundingRateHistoryRaw.java @@ -0,0 +1,24 @@ +package org.knowm.xchange.bybit.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class BybitFundingRateHistoryRaw { + + private final String instrument; + + private final BigDecimal fundingRate; + + private final Instant fundingRateTimestamp; + + public BybitFundingRateHistoryRaw(@JsonProperty("symbol") String instrument, @JsonProperty("fundingRate") BigDecimal fundingRate, @JsonProperty("fundingRateTimestamp") long fundingRateTimestamp) { + this.instrument = instrument; + this.fundingRate = fundingRate; + this.fundingRateTimestamp = Instant.ofEpochMilli(fundingRateTimestamp); + } +} 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 7daa056c80f..a5e144be276 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 @@ -7,6 +7,8 @@ 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.BybitFundingRateHistory; +import org.knowm.xchange.bybit.dto.marketdata.BybitFundingRateHistoryRaw; 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; @@ -102,4 +104,15 @@ public List getTickers(Params params) throws IOException { } return result; } + + public List getFundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + BybitCategory category = BybitAdapters.getCategory(instrument); + List raw = getFundingRateHistoryRaw(instrument, startTime, endTime, limit); + List result = new ArrayList<>(); + for (BybitFundingRateHistoryRaw entry : raw) { + Instrument converted = BybitAdapters.convertBybitSymbolToInstrument(entry.getInstrument(), category); + result.add(new BybitFundingRateHistory(converted, entry.getFundingRate(), entry.getFundingRateTimestamp())); + } + return result; + } } 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 2c2d8540053..0c8ac293df4 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,15 +1,18 @@ 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.BybitFundingRateHistoryRaw; 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.client.ResilienceRegistries; +import org.knowm.xchange.instrument.Instrument; public class BybitMarketDataServiceRaw extends BybitBaseService { @@ -48,4 +51,8 @@ public BybitResult> getTickers(BybitCategory category) } return result; } + + public List getFundingRateHistoryRaw(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + return bybit.getFundingHistory(BybitAdapters.getCategory(instrument).getValue(), BybitAdapters.convertToBybitSymbol(instrument), startTime, endTime, limit).getResult().getList(); + } } 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 d38e94c07a0..9b5e2925772 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 @@ -3,13 +3,17 @@ import static org.assertj.core.api.Assertions.assertThat; import java.math.BigDecimal; +import java.time.Instant; import java.util.Date; +import java.util.List; import org.junit.Before; import org.junit.Test; import org.knowm.xchange.Exchange; +import org.knowm.xchange.bybit.dto.marketdata.BybitFundingRateHistory; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.derivative.FuturesContract; import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.marketdata.MarketDataService; public class BybitMarketDataServiceTest extends BaseWiremockTest { @@ -48,7 +52,7 @@ public void testGetTickerWithInverseArg() throws Exception { public void testGetTickerWithSpotArg() throws Exception { initGetStub("/v5/market/tickers", "/getTickerSpot.json5"); - Ticker ticker = marketDataService.getTicker(CurrencyPair.BTC_USD); + Ticker ticker = marketDataService.getTicker((Instrument) CurrencyPair.BTC_USD); assertThat(ticker.getInstrument().toString()).isEqualTo("BTC/USD"); assertThat(ticker.getOpen()).isEqualTo(new BigDecimal("20393.48")); @@ -65,4 +69,17 @@ public void testGetTickerWithSpotArg() throws Exception { assertThat(ticker.getAskSize()).isEqualTo(new BigDecimal("1.862172")); assertThat(ticker.getPercentageChange()).isEqualTo(new BigDecimal("0.0068")); } + + @Test + public void testGetFundingRateHistory() throws Exception { + initGetStub("/v5/market/funding/history", "/getFundingRateHistory.json5"); + + List fundingRateHistory = ((BybitMarketDataService) marketDataService).getFundingRateHistory(new FuturesContract("ETH/USDT/PERP"), + null, null, null); + + assertThat(fundingRateHistory.get(0).getInstrument().toString()).isEqualTo("ETH/USDT/PERP"); + assertThat(fundingRateHistory.get(0).getFundingRate()).isEqualTo(new BigDecimal("0.0001")); + assertThat(fundingRateHistory.get(0).getFundingRateTimestamp()).isEqualTo(Instant.ofEpochMilli(1672051897447L)); + + } } diff --git a/xchange-bybit/src/test/resources/getFundingRateHistory.json5 b/xchange-bybit/src/test/resources/getFundingRateHistory.json5 new file mode 100644 index 00000000000..466daf07166 --- /dev/null +++ b/xchange-bybit/src/test/resources/getFundingRateHistory.json5 @@ -0,0 +1,16 @@ +{ + "retCode": 0, + "retMsg": "OK", + "result": { + "category": "linear", + "list": [ + { + "symbol": "ETHUSDT", + "fundingRate": "0.0001", + "fundingRateTimestamp": "1672051897447" + } + ] + }, + "retExtInfo": {}, + "time": 1672051897447 +} \ No newline at end of file diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/Okex.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/Okex.java index c382353d8b3..2337706ab53 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/Okex.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/Okex.java @@ -20,6 +20,7 @@ import org.knowm.xchange.okex.dto.marketdata.OkexOrderbook; import org.knowm.xchange.okex.dto.marketdata.OkexTicker; import org.knowm.xchange.okex.dto.marketdata.OkexTrade; +import org.knowm.xchange.okex.dto.marketdata.OkxFundingRateHistory; @Path("/api/v5") @Produces(APPLICATION_JSON) @@ -27,6 +28,7 @@ public interface Okex { String instrumentsPath = "/public/instruments"; // Stated as 20 req/2 sec String tickerPath = "/market/ticker"; // Stated as 20 req/2 sec String tickersPath = "/market/tickers"; // Stated as 20 req/2 sec + String fundingRateHistoryPath = "/public/funding-rate-history"; // Stated as 10 req/2 sec // To avoid 429s, actual req/second may need to be lowered! Map> publicPathRateLimits = @@ -35,6 +37,7 @@ public interface Okex { put(instrumentsPath, Arrays.asList(8, 1)); put(tickerPath, Arrays.asList(8, 1)); put(tickersPath, Arrays.asList(8, 1)); + put(fundingRateHistoryPath, Arrays.asList(4, 1)); } }; @@ -56,14 +59,14 @@ OkexResponse> getTrades( throws IOException, OkexException; @GET - @Path("/market/ticker") + @Path(tickerPath) OkexResponse> getTicker( @QueryParam("instId") String instrument, @HeaderParam("X-SIMULATED-TRADING") String simulatedTrading) throws IOException, OkexException; @GET - @Path("/market/tickers") + @Path(tickersPath) OkexResponse> getTickers( @QueryParam("instType") String instType, @HeaderParam("X-SIMULATED-TRADING") String simulatedTrading) @@ -105,4 +108,14 @@ OkexResponse> getCandles( @QueryParam("limit") String limit, @HeaderParam("X-SIMULATED-TRADING") String simulatedTrading) throws IOException, OkexException; + + @GET + @Path(fundingRateHistoryPath) + OkexResponse> getFundingRateHistory( + @QueryParam("instId") String instrument, + @QueryParam("after") Long after, + @QueryParam("before") Long before, + @QueryParam("limit") Integer limit, + @HeaderParam("X-SIMULATED-TRADING") String simulatedTrading) + throws IOException, OkexException; } diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java new file mode 100644 index 00000000000..13045a6c427 --- /dev/null +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java @@ -0,0 +1,36 @@ +package org.knowm.xchange.okex.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Getter; +import lombok.ToString; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.okex.OkexAdapters; + +@Getter +@ToString +public class OkxFundingRateHistory { + + private final String instType; + private final Instrument instrument; + private final BigDecimal predictedFundingRate; + private final String fundingRate; + private final Instant fundingTime; + private final String method; + + public OkxFundingRateHistory(@JsonProperty("instType") String instType, + @JsonProperty("instId") String instrument, + @JsonProperty("fundingRate") BigDecimal predictedFundingRate, + @JsonProperty("realizedRate") String fundingRate, + @JsonProperty("fundingTime") long fundingTime, + @JsonProperty("method") String method) { + this.instType = instType; + this.instrument = OkexAdapters.adaptOkexInstrumentId(instrument); + this.predictedFundingRate = predictedFundingRate; + this.fundingRate = fundingRate; + this.fundingTime = Instant.ofEpochMilli(fundingTime); + this.method = method; + } + +} diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java index 98f8962d70c..518891a3fbe 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java @@ -18,6 +18,7 @@ import org.knowm.xchange.okex.dto.OkexInstType; import org.knowm.xchange.okex.dto.OkexResponse; import org.knowm.xchange.okex.dto.marketdata.OkexCandleStick; +import org.knowm.xchange.okex.dto.marketdata.OkxFundingRateHistory; import org.knowm.xchange.service.marketdata.MarketDataService; import org.knowm.xchange.service.marketdata.params.Params; import org.knowm.xchange.service.trade.params.CandleStickDataParams; @@ -99,4 +100,8 @@ public List getTickers(Params params) throws IOException { .map(OkexAdapters::adaptTicker) .collect(Collectors.toList()); } + + public List getFundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + return getOkxFundingRateHistoryRaw(OkexAdapters.adaptInstrument(instrument), startTime, endTime, limit); + } } diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java index 720ff17f73b..eb6a30b3c30 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java @@ -20,10 +20,12 @@ import org.knowm.xchange.okex.dto.marketdata.OkexOrderbook; import org.knowm.xchange.okex.dto.marketdata.OkexTicker; import org.knowm.xchange.okex.dto.marketdata.OkexTrade; +import org.knowm.xchange.okex.dto.marketdata.OkxFundingRateHistory; import org.knowm.xchange.utils.DateUtils; /** Author: Max Gao (gaamox@tutanota.com) Created: 08-06-2021 */ public class OkexMarketDataServiceRaw extends OkexBaseService { + public OkexMarketDataServiceRaw( OkexExchange exchange, ResilienceRegistries resilienceRegistries) { super(exchange, resilienceRegistries); @@ -34,15 +36,15 @@ public OkexResponse> getOkexInstruments( throws OkexException, IOException { try { return decorateApiCall( - () -> - okex.getInstruments( - instrumentType, - underlying, - instrumentId, - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_SIMULATED))) + () -> + okex.getInstruments( + instrumentType, + underlying, + instrumentId, + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_SIMULATED))) .withRateLimiter(rateLimiter(Okex.instrumentsPath)) .call(); } catch (OkexException e) { @@ -54,13 +56,13 @@ public OkexResponse> getOkexTicker(String instrumentId) throws OkexException, IOException { try { return decorateApiCall( - () -> - okex.getTicker( - instrumentId, - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_SIMULATED))) + () -> + okex.getTicker( + instrumentId, + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_SIMULATED))) .withRateLimiter(rateLimiter(Okex.tickerPath)) .call(); } catch (OkexException e) { @@ -72,13 +74,13 @@ public OkexResponse> getOkexTickers(OkexInstType instType) throws OkexException, IOException { try { return decorateApiCall( - () -> - okex.getTickers( - instType.toString(), - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_SIMULATED))) + () -> + okex.getTickers( + instType.toString(), + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_SIMULATED))) .withRateLimiter(rateLimiter(Okex.tickersPath)) .call(); } catch (OkexException e) { @@ -90,13 +92,13 @@ public OkexResponse> getOkexFundingRate(String instrumentI throws OkexException, IOException { try { return decorateApiCall( - () -> - okex.getFundingRate( - instrumentId, - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_SIMULATED))) + () -> + okex.getFundingRate( + instrumentId, + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_SIMULATED))) .withRateLimiter(rateLimiter(Okex.instrumentsPath)) .call(); } catch (OkexException e) { @@ -107,19 +109,19 @@ public OkexResponse> getOkexFundingRate(String instrumentI public OkexResponse> getOkexCurrencies() throws OkexException, IOException { try { return decorateApiCall( - () -> - okexAuthenticated.getCurrencies( - exchange.getExchangeSpecification().getApiKey(), - signatureCreator, - DateUtils.toUTCISODateString(new Date()), - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_PASSPHRASE), - (String) - exchange - .getExchangeSpecification() - .getExchangeSpecificParametersItem(PARAM_SIMULATED))) + () -> + okexAuthenticated.getCurrencies( + exchange.getExchangeSpecification().getApiKey(), + signatureCreator, + DateUtils.toUTCISODateString(new Date()), + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_PASSPHRASE), + (String) + exchange + .getExchangeSpecification() + .getExchangeSpecificParametersItem(PARAM_SIMULATED))) .withRateLimiter(rateLimiter(OkexAuthenticated.currenciesPath)) .call(); } catch (OkexException e) { @@ -171,4 +173,18 @@ public OkexResponse> getCandle( (String) exchange.getExchangeSpecification().getExchangeSpecificParametersItem(PARAM_SIMULATED)); } + + public List getOkxFundingRateHistoryRaw(String instrument, Long startTime, Long endTime, Integer limit) throws IOException { + return decorateApiCall( + () -> + okex.getFundingRateHistory( + instrument, + startTime, + endTime, + limit, + (String) + exchange.getExchangeSpecification().getExchangeSpecificParametersItem(PARAM_SIMULATED)).getData()). + withRateLimiter(rateLimiter(Okex.fundingRateHistoryPath)) + .call(); + } } diff --git a/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java b/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java index f517ae03000..e361e87de45 100644 --- a/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java +++ b/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java @@ -25,6 +25,7 @@ import org.knowm.xchange.okex.dto.OkexInstType; import org.knowm.xchange.okex.dto.OkexResponse; import org.knowm.xchange.okex.dto.marketdata.OkexCandleStick; +import org.knowm.xchange.okex.dto.marketdata.OkxFundingRateHistory; import org.knowm.xchange.okex.service.OkexMarketDataService; public class OkexPublicDataIntegration { @@ -144,4 +145,15 @@ public void testInstrumentOkexConvertions() { assertThat(OkexAdapters.adaptInstrument(new CurrencyPair("BTC/USDT"))).isEqualTo("BTC-USDT"); assertThat(OkexAdapters.adaptInstrument(new CurrencyPair("BTC/USDC"))).isEqualTo("BTC-USD"); } + + @Test + public void testFundingRateHistory() { + try { + List fundingRateHistory = ((OkexMarketDataService) exchange.getMarketDataService()).getFundingRateHistory(instrument, null, null, null); + System.out.println(fundingRateHistory); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } } From 4b611573a9dbdafb8464144478ec529ae52a86bc Mon Sep 17 00:00:00 2001 From: rizer1980 <4340180@gmail.com> Date: Mon, 16 Mar 2026 20:14:27 +0300 Subject: [PATCH 2/4] fix --- .../xchange/okex/dto/marketdata/OkxFundingRateHistory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java index 13045a6c427..d23460acebd 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/dto/marketdata/OkxFundingRateHistory.java @@ -15,14 +15,14 @@ public class OkxFundingRateHistory { private final String instType; private final Instrument instrument; private final BigDecimal predictedFundingRate; - private final String fundingRate; + private final BigDecimal fundingRate; private final Instant fundingTime; private final String method; public OkxFundingRateHistory(@JsonProperty("instType") String instType, @JsonProperty("instId") String instrument, @JsonProperty("fundingRate") BigDecimal predictedFundingRate, - @JsonProperty("realizedRate") String fundingRate, + @JsonProperty("realizedRate") BigDecimal fundingRate, @JsonProperty("fundingTime") long fundingTime, @JsonProperty("method") String method) { this.instType = instType; From f71a782816071c614d929d340624213ed6eba3d2 Mon Sep 17 00:00:00 2001 From: rizer1980 <4340180@gmail.com> Date: Mon, 16 Mar 2026 20:25:54 +0300 Subject: [PATCH 3/4] fix --- .../binance/dto/marketdata/BinanceFundingRateHistory.java | 6 +++--- .../xchange/binance/service/BinanceMarketDataService.java | 2 +- .../java/org/knowm/xchange/binance/BinanceFutureTest.java | 2 +- .../knowm/xchange/bybit/service/BybitMarketDataService.java | 3 +++ .../knowm/xchange/okex/service/OkexMarketDataService.java | 6 +++++- .../xchange/okex/service/OkexMarketDataServiceRaw.java | 6 ++++-- .../org/knowm/xchange/okex/OkexPublicDataIntegration.java | 3 ++- 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java index 3ac71264842..e3a66bd54a3 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceFundingRateHistory.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; -import java.util.Date; +import java.time.Instant; import lombok.Getter; import lombok.ToString; import org.knowm.xchange.binance.BinanceAdapters; @@ -14,7 +14,7 @@ public class BinanceFundingRateHistory { private final Instrument instrument; private final BigDecimal fundingRate; - private final Date fundingTime; + private final Instant fundingTime; private final BigDecimal markPrice; public BinanceFundingRateHistory( @@ -24,7 +24,7 @@ public BinanceFundingRateHistory( @JsonProperty("markPrice") BigDecimal markPrice) { this.instrument = BinanceAdapters.adaptSymbol(symbol, true); this.fundingRate = fundingRate; - this.fundingTime = new Date(fundingTime); + this.fundingTime = Instant.ofEpochMilli(fundingTime); this.markPrice = markPrice; } } diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java index 6a4cdc6c007..e1d09057451 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/service/BinanceMarketDataService.java @@ -151,7 +151,7 @@ private OrderBook getBinanceOrderBook(Instrument instrument, Object... args) thr } } - public List fundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { + public List getFundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { return fundingRateHistoryRaw(instrument, startTime, endTime, limit); } diff --git a/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java b/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java index b6fdc6dbc11..ab62804b73d 100644 --- a/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java +++ b/xchange-binance/src/test/java/org/knowm/xchange/binance/BinanceFutureTest.java @@ -90,7 +90,7 @@ public void binanceFutureMarketDataService() throws IOException { .getFundingRates() .forEach(fundingRate -> System.out.println(fundingRate.toString())); List fundingRateHistory = ((BinanceMarketDataService) binanceExchange.getMarketDataService()) - .fundingRateHistory(instrument, System.currentTimeMillis() - 24 * 60 * 60 * 1000, System.currentTimeMillis(), null); + .getFundingRateHistory(instrument, System.currentTimeMillis() - 24 * 60 * 60 * 1000, System.currentTimeMillis(), null); fundingRateHistory.forEach(fundingRate -> System.out.println(fundingRate.toString())); } 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 a5e144be276..a7ffde90748 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 @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import org.knowm.xchange.bybit.BybitAdapters; import org.knowm.xchange.bybit.BybitExchange; @@ -113,6 +114,8 @@ public List getFundingRateHistory(Instrument instrument Instrument converted = BybitAdapters.convertBybitSymbolToInstrument(entry.getInstrument(), category); result.add(new BybitFundingRateHistory(converted, entry.getFundingRate(), entry.getFundingRateTimestamp())); } + // sort, oldest first + result.sort(Comparator.comparingLong(s -> s.getFundingRateTimestamp().toEpochMilli())); return result; } } diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java index 518891a3fbe..15e9945a1df 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataService.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import org.knowm.xchange.client.ResilienceRegistries; @@ -102,6 +103,9 @@ public List getTickers(Params params) throws IOException { } public List getFundingRateHistory(Instrument instrument, Long startTime, Long endTime, Integer limit) throws IOException { - return getOkxFundingRateHistoryRaw(OkexAdapters.adaptInstrument(instrument), startTime, endTime, limit); + List result = getOkxFundingRateHistoryRaw(OkexAdapters.adaptInstrument(instrument), startTime, endTime, limit); + // sort, oldest first + result.sort(Comparator.comparingLong(c -> c.getFundingTime().toEpochMilli())); + return result; } } diff --git a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java index eb6a30b3c30..ddfed52036f 100644 --- a/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java +++ b/xchange-okex/src/main/java/org/knowm/xchange/okex/service/OkexMarketDataServiceRaw.java @@ -23,7 +23,9 @@ import org.knowm.xchange.okex.dto.marketdata.OkxFundingRateHistory; import org.knowm.xchange.utils.DateUtils; -/** Author: Max Gao (gaamox@tutanota.com) Created: 08-06-2021 */ +/** + * Author: Max Gao (gaamox@tutanota.com) Created: 08-06-2021 + */ public class OkexMarketDataServiceRaw extends OkexBaseService { public OkexMarketDataServiceRaw( @@ -179,8 +181,8 @@ public List getOkxFundingRateHistoryRaw(String instrument () -> okex.getFundingRateHistory( instrument, - startTime, endTime, + startTime, limit, (String) exchange.getExchangeSpecification().getExchangeSpecificParametersItem(PARAM_SIMULATED)).getData()). diff --git a/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java b/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java index e361e87de45..bd19db55f3d 100644 --- a/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java +++ b/xchange-okex/src/test/java/org/knowm/xchange/okex/OkexPublicDataIntegration.java @@ -149,7 +149,8 @@ public void testInstrumentOkexConvertions() { @Test public void testFundingRateHistory() { try { - List fundingRateHistory = ((OkexMarketDataService) exchange.getMarketDataService()).getFundingRateHistory(instrument, null, null, null); + List fundingRateHistory = ((OkexMarketDataService) exchange.getMarketDataService()).getFundingRateHistory(instrument, System.currentTimeMillis() - 24 * 60 * 60 * 1000, + System.currentTimeMillis(), null); System.out.println(fundingRateHistory); } catch (IOException e) { throw new RuntimeException(e); From b8864f1a27268a4a0fdbf8e4f6f16f213c8f76c9 Mon Sep 17 00:00:00 2001 From: rizer1980 <4340180@gmail.com> Date: Fri, 20 Mar 2026 18:51:31 +0300 Subject: [PATCH 4/4] add H3,H5,H7 funding rate --- .../java/org/knowm/xchange/dto/marketdata/FundingRate.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xchange-core/src/main/java/org/knowm/xchange/dto/marketdata/FundingRate.java b/xchange-core/src/main/java/org/knowm/xchange/dto/marketdata/FundingRate.java index 7be7326acb6..444c362a75b 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/dto/marketdata/FundingRate.java +++ b/xchange-core/src/main/java/org/knowm/xchange/dto/marketdata/FundingRate.java @@ -108,8 +108,11 @@ private static long calculateFundingRateEffectiveInMinutes(Date fundingRateDate) public enum FundingRateInterval { H1(1), H2(2), + H3(3), H4(4), + H5(5), H6(6), + H7(7), H8(8); private final int hours;