From 149d3f492fef1522a2c16a9247c375687bf679ea Mon Sep 17 00:00:00 2001 From: "zihao.jiang" Date: Mon, 5 Jan 2026 17:59:20 +0800 Subject: [PATCH] add tradeQuoteCcy request param to the trade-related endpoints --- okx/TradingData.py | 25 ++++ okx/consts.py | 1 + test/test_trading_data.py | 12 +- test/unit/okx/test_trading_data.py | 177 +++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 test/unit/okx/test_trading_data.py diff --git a/okx/TradingData.py b/okx/TradingData.py index 7100ef4..0a0d3e4 100644 --- a/okx/TradingData.py +++ b/okx/TradingData.py @@ -47,6 +47,31 @@ def get_taker_block_volume(self, ccy, period=''): params = {'ccy': ccy, 'period': period} return self._request_with_params(GET, TAKER_FLOW, params) + def get_contracts_open_interest_history(self, instId, period=None, begin=None, end=None, limit=None): + """ + Get contract open interest history + Retrieve the contract open interest statistics of futures and perp. + Rate limit: 10 requests per 2 seconds + Rate limit rule: IP + Instrument ID + + :param instId: Instrument ID, e.g. BTC-USDT-SWAP. Only applicable to FUTURES, SWAP + :param period: Bar size, the default is 5m, e.g. [5m/15m/30m/1H/2H/4H] + :param begin: Return records newer than the requested ts + :param end: Pagination of data to return records earlier than the requested ts + :param limit: Number of results per request. The maximum is 100. The default is 100 + :return: API response + """ + params = {'instId': instId} + if period is not None: + params['period'] = period + if begin is not None: + params['begin'] = begin + if end is not None: + params['end'] = end + if limit is not None: + params['limit'] = limit + return self._request_with_params(GET, CONTRACTS_OPEN_INTEREST_HISTORY, params) + diff --git a/okx/consts.py b/okx/consts.py index cef00ef..e58d9af 100644 --- a/okx/consts.py +++ b/okx/consts.py @@ -143,6 +143,7 @@ OPEN_INTEREST_VOLUME_EXPIRY = '/api/v5/rubik/stat/option/open-interest-volume-expiry' INTEREST_VOLUME_STRIKE = '/api/v5/rubik/stat/option/open-interest-volume-strike' TAKER_FLOW = '/api/v5/rubik/stat/option/taker-block-volume' +CONTRACTS_OPEN_INTEREST_HISTORY = '/api/v5/rubik/stat/contracts/open-interest-history' # Trade PLACR_ORDER = '/api/v5/trade/order' diff --git a/test/test_trading_data.py b/test/test_trading_data.py index 0872f37..ecb6c49 100644 --- a/test/test_trading_data.py +++ b/test/test_trading_data.py @@ -29,8 +29,16 @@ def test_open_interest_volume_strike(self): print(self.TradingDataAPI.get_interest_volume_strike(ccy="BTC",expTime="20220901")) """ - def test_taker_block_vol(self): - print(self.TradingDataAPI.get_taker_flow(ccy='BTC')) + + def test_get_contracts_open_interest_history(self): + print(self.TradingDataAPI.get_contracts_open_interest_history(instId='BTC-USDT-SWAP')) + + def test_get_contracts_open_interest_history_with_params(self): + print(self.TradingDataAPI.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP', + period='1H', + limit='50' + )) if __name__ == "__main__": unittest.main() diff --git a/test/unit/okx/test_trading_data.py b/test/unit/okx/test_trading_data.py new file mode 100644 index 0000000..c710a76 --- /dev/null +++ b/test/unit/okx/test_trading_data.py @@ -0,0 +1,177 @@ +""" +Unit tests for okx.TradingData module + +Mirrors the structure: okx/TradingData.py -> test/unit/okx/test_trading_data.py +""" +import unittest +from unittest.mock import patch +from okx.TradingData import TradingDataAPI +from okx import consts as c + + +class TestTradingDataAPIContractsOpenInterestHistory(unittest.TestCase): + """Unit tests for the get_contracts_open_interest_history method""" + + def setUp(self): + """Set up test fixtures""" + self.trading_data_api = TradingDataAPI(flag='0') + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_with_required_params(self, mock_request): + """Test get_contracts_open_interest_history with required parameters only""" + # Arrange + mock_response = { + 'code': '0', + 'msg': '', + 'data': [ + {'ts': '1609459200000', 'oi': '100000', 'oiCcy': '10'} + ] + } + mock_request.return_value = mock_response + + # Act + result = self.trading_data_api.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP' + ) + + # Assert + expected_params = { + 'instId': 'BTC-USDT-SWAP' + } + mock_request.assert_called_once_with(c.GET, c.CONTRACTS_OPEN_INTEREST_HISTORY, expected_params) + self.assertEqual(result, mock_response) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_with_all_params(self, mock_request): + """Test get_contracts_open_interest_history with all parameters provided""" + # Arrange + mock_response = { + 'code': '0', + 'msg': '', + 'data': [ + {'ts': '1609459200000', 'oi': '100000', 'oiCcy': '10'} + ] + } + mock_request.return_value = mock_response + + # Act + result = self.trading_data_api.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP', + period='1H', + begin='1609459200000', + end='1609545600000', + limit='50' + ) + + # Assert + expected_params = { + 'instId': 'BTC-USDT-SWAP', + 'period': '1H', + 'begin': '1609459200000', + 'end': '1609545600000', + 'limit': '50' + } + mock_request.assert_called_once_with(c.GET, c.CONTRACTS_OPEN_INTEREST_HISTORY, expected_params) + self.assertEqual(result, mock_response) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_with_period(self, mock_request): + """Test get_contracts_open_interest_history with period parameter""" + # Arrange + mock_response = {'code': '0', 'msg': '', 'data': []} + mock_request.return_value = mock_response + + # Act + result = self.trading_data_api.get_contracts_open_interest_history( + instId='ETH-USDT-SWAP', + period='5m' + ) + + # Assert + expected_params = { + 'instId': 'ETH-USDT-SWAP', + 'period': '5m' + } + mock_request.assert_called_once_with(c.GET, c.CONTRACTS_OPEN_INTEREST_HISTORY, expected_params) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_different_periods(self, mock_request): + """Test get_contracts_open_interest_history with different period values""" + mock_response = {'code': '0', 'msg': '', 'data': []} + mock_request.return_value = mock_response + + periods = ['5m', '15m', '30m', '1H', '2H', '4H', '6H', '12H', '1D', '2D', '3D', '5D', '1W', '1M', '3M'] + + for period in periods: + mock_request.reset_mock() + result = self.trading_data_api.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP', + period=period + ) + + call_args = mock_request.call_args[0][2] + self.assertEqual(call_args['period'], period) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_different_inst_ids(self, mock_request): + """Test get_contracts_open_interest_history with different instrument IDs""" + mock_response = {'code': '0', 'msg': '', 'data': []} + mock_request.return_value = mock_response + + inst_ids = ['BTC-USDT-SWAP', 'ETH-USDT-SWAP', 'BTC-USD-SWAP', 'BTC-USDT-240329'] + + for inst_id in inst_ids: + mock_request.reset_mock() + result = self.trading_data_api.get_contracts_open_interest_history( + instId=inst_id + ) + + call_args = mock_request.call_args[0][2] + self.assertEqual(call_args['instId'], inst_id) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_with_pagination(self, mock_request): + """Test get_contracts_open_interest_history with pagination parameters""" + # Arrange + mock_response = {'code': '0', 'msg': '', 'data': []} + mock_request.return_value = mock_response + + # Act + result = self.trading_data_api.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP', + begin='1609459200000', + end='1609545600000', + limit='100' + ) + + # Assert + expected_params = { + 'instId': 'BTC-USDT-SWAP', + 'begin': '1609459200000', + 'end': '1609545600000', + 'limit': '100' + } + mock_request.assert_called_once_with(c.GET, c.CONTRACTS_OPEN_INTEREST_HISTORY, expected_params) + + @patch.object(TradingDataAPI, '_request_with_params') + def test_get_contracts_open_interest_history_utc_periods(self, mock_request): + """Test get_contracts_open_interest_history with UTC+0 period values""" + mock_response = {'code': '0', 'msg': '', 'data': []} + mock_request.return_value = mock_response + + utc_periods = ['6Hutc', '12Hutc', '1Dutc', '2Dutc', '3Dutc', '5Dutc', '1Wutc', '1Mutc', '3Mutc'] + + for period in utc_periods: + mock_request.reset_mock() + result = self.trading_data_api.get_contracts_open_interest_history( + instId='BTC-USDT-SWAP', + period=period + ) + + call_args = mock_request.call_args[0][2] + self.assertEqual(call_args['period'], period) + + +if __name__ == '__main__': + unittest.main() +