From fc8de415b29661a8415bea3536bf14805b6690ee Mon Sep 17 00:00:00 2001 From: Andy Geach Date: Sat, 31 Jan 2026 14:04:56 +0000 Subject: [PATCH 1/5] fix deprecation warnings --- tests/test_historical_prices.py | 9 --------- tests/test_historical_prices_flat.py | 4 ---- tests/test_integration.py | 6 +++--- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/tests/test_historical_prices.py b/tests/test_historical_prices.py index a9e87c0..8d098e9 100644 --- a/tests/test_historical_prices.py +++ b/tests/test_historical_prices.py @@ -53,7 +53,6 @@ def test_historical_prices_v3_datetime_happy(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -95,7 +94,6 @@ def test_historical_prices_v3_num_points_happy(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -126,7 +124,6 @@ def test_historical_prices_v3_num_points_bad_numpoints(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json={ "errorCode": "Unable to convert value=3.14159 to type= Integer int" @@ -151,7 +148,6 @@ def test_historical_prices_v3_num_points_bad_resolution(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -235,7 +231,6 @@ def test_historical_prices_by_epic_and_date_range_v1_happy(self): responses.add( responses.GET, re.compile("https://demo-api.ig.com/gateway/deal/prices/.+"), - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -273,7 +268,6 @@ def test_historical_prices_by_epic_and_date_range_happy(self): responses.add( responses.GET, re.compile("https://demo-api.ig.com/gateway/deal/prices/.+"), - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -376,7 +370,6 @@ def test_historical_prices_by_epic_and_num_points_happy(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP/DAY/10", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -426,7 +419,6 @@ def test_historical_prices_by_epic_and_num_points_bad_numpoints(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json={ "errorCode": "Unable to convert value=3.14159 to type= Integer int" @@ -448,7 +440,6 @@ def test_historical_prices_by_epic_and_num_points_bad_resolution(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json={}, status=200, diff --git a/tests/test_historical_prices_flat.py b/tests/test_historical_prices_flat.py index cd0fb7c..029105b 100644 --- a/tests/test_historical_prices_flat.py +++ b/tests/test_historical_prices_flat.py @@ -54,7 +54,6 @@ def test_historical_prices_v3_datetime_happy(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -97,7 +96,6 @@ def test_historical_prices_v3_num_points_happy(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, @@ -131,7 +129,6 @@ def test_historical_prices_v3_num_points_bad_numpoints(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json={ "errorCode": "Unable to convert value=3.14159 to type= Integer int" @@ -159,7 +156,6 @@ def test_historical_prices_v3_num_points_bad_resolution(self): responses.add( responses.GET, "https://demo-api.ig.com/gateway/deal/prices/MT.D.GC.Month2.IP", - match_querystring=False, headers={"CST": "abc123", "X-SECURITY-TOKEN": "xyz987"}, json=response_body, status=200, diff --git a/tests/test_integration.py b/tests/test_integration.py index a372404..483f4d7 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -460,7 +460,7 @@ def test_search_markets(self, ig_service: IGService): def test_fetch_historical_prices_by_epic_and_numpoints(self, ig_service: IGService): response = ig_service.fetch_historical_prices_by_epic_and_num_points( - "CS.D.EURUSD.MINI.IP", "H", 4 + "CS.D.EURUSD.MINI.IP", "h", 4 ) assert isinstance(response["allowance"], dict) assert isinstance(response["prices"], pd.DataFrame) @@ -470,7 +470,7 @@ def test_fetch_historical_prices_by_epic_and_numpoints_flat( self, ig_service: IGService ): response = ig_service.fetch_historical_prices_by_epic_and_num_points( - "CS.D.EURUSD.MINI.IP", "H", 4, format=ig_service.flat_prices + "CS.D.EURUSD.MINI.IP", "h", 4, format=ig_service.flat_prices ) assert isinstance(response["allowance"], dict) assert isinstance(response["prices"], pd.DataFrame) @@ -481,7 +481,7 @@ def test_fetch_historical_prices_by_epic_and_numpoints_mid( self, ig_service: IGService ): response = ig_service.fetch_historical_prices_by_epic_and_num_points( - "CS.D.EURUSD.MINI.IP", "H", 4, format=ig_service.mid_prices + "CS.D.EURUSD.MINI.IP", "h", 4, format=ig_service.mid_prices ) assert isinstance(response["allowance"], dict) assert isinstance(response["prices"], pd.DataFrame) From 47e60a16046e789ce3cdf84eb78bf309b7582afe Mon Sep 17 00:00:00 2001 From: Andy Geach Date: Mon, 2 Feb 2026 10:54:53 +0000 Subject: [PATCH 2/5] disable navigation tests --- tests/test_integration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_integration.py b/tests/test_integration.py index 483f4d7..9e3bdca 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -81,6 +81,7 @@ def ig_service(request, retrying): ig_service.logout() +# TODO refactor for new navigation API @pytest.fixture() def top_level_nodes(ig_service: IGService): """test fixture gets the top level navigation nodes""" @@ -270,6 +271,7 @@ def test_create_session_bad_api_key(self, retrying): with pytest.raises(IGException): ig_service.create_session() + @pytest.mark.xfail(reason="Navigation API has been changed by IG") def test_fetch_top_level_navigation_nodes(self, top_level_nodes): assert isinstance(top_level_nodes, pd.DataFrame) @@ -402,6 +404,7 @@ def assert_sentiment(response): assert isinstance(short, float) assert long + short == 100.0 + @pytest.mark.xfail(reason="Navigation API has been changed by IG") def test_fetch_sub_nodes_by_node(self, ig_service: IGService, top_level_nodes): rand_index = randint(0, len(top_level_nodes) - 1) response = ig_service.fetch_sub_nodes_by_node(rand_index) From fde230a48a6c3aa76b7fab1849956771dd2bb63b Mon Sep 17 00:00:00 2001 From: Andy Geach Date: Mon, 2 Feb 2026 11:01:30 +0000 Subject: [PATCH 3/5] drop python 3.9 --- .github/workflows/unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 6b267d7..6ca19bc 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ "3.9", "3.10", "3.11", "3.12" ] + python-version: [ "3.10", "3.11", "3.12" ] steps: From 73c6ebb6f7e53548dc025a075aae6019e8798815 Mon Sep 17 00:00:00 2001 From: Andy Geach Date: Wed, 15 Apr 2026 10:11:00 +0100 Subject: [PATCH 4/5] add support for search v2 --- tests/test_integration.py | 5 +++++ trading_ig/rest.py | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/test_integration.py b/tests/test_integration.py index 954065b..e3344ff 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -461,6 +461,11 @@ def test_search_markets(self, ig_service: IGService): response = ig_service.search_markets(search_term) assert isinstance(response, pd.DataFrame) + def test_search_markets_v2(self, ig_service: IGService): + epics = "CS.D.GBPUSD.TODAY.IP" + response = ig_service.search_markets_v2(epics) + assert isinstance(response, pd.DataFrame) + def test_fetch_historical_prices_by_epic_and_numpoints(self, ig_service: IGService): response = ig_service.fetch_historical_prices_by_epic_and_num_points( "CS.D.EURUSD.MINI.IP", "h", 4 diff --git a/trading_ig/rest.py b/trading_ig/rest.py index 4e3c620..aaf1236 100644 --- a/trading_ig/rest.py +++ b/trading_ig/rest.py @@ -1569,6 +1569,19 @@ def search_markets(self, search_term, session=None): data = pd.DataFrame(data["markets"]) return data + def search_markets_v2(self, epics, session=None): + """Returns all markets matching the epics""" + self.non_trading_rate_limit_pause_or_pass() + version = "2" + endpoint = "/markets" + params = {"epics": epics} + action = "read" + response = self._req(action, endpoint, params, session, version) + data = self.parse_response(response.text) + if self.return_dataframe: + data = pd.DataFrame(data["marketDetails"]) + return data + def format_prices(self, prices, version, flag_calc_spread=False): """ Format prices data as a DataFrame with hierarchical columns From 6a366686c34579c28f3490a994649b00a5f9ac4a Mon Sep 17 00:00:00 2001 From: Andy Geach Date: Wed, 15 Apr 2026 10:12:19 +0100 Subject: [PATCH 5/5] better dates in integration tests --- tests/test_integration.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index e3344ff..46b9a2d 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -163,8 +163,8 @@ def test_fetch_account_activity_by_period(self, ig_service: IGService): assert isinstance(response, pd.DataFrame) def test_fetch_account_activity_by_date(self, ig_service: IGService): - to_date = datetime.now() - from_date = to_date - timedelta(days=7) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity_by_date(from_date, to_date) assert isinstance(response, pd.DataFrame) @@ -174,23 +174,23 @@ def test_fetch_account_activity_v2_span(self, ig_service: IGService): assert isinstance(response, pd.DataFrame) def test_fetch_account_activity_v2_dates(self, ig_service): - to_date = datetime(2021, 7, 31) - from_date = to_date - timedelta(days=7) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity_v2( from_date=from_date, to_date=to_date ) assert isinstance(response, pd.DataFrame) def test_fetch_account_activity_from(self, ig_service: IGService): - to_date = datetime.now() - timedelta(days=3) - from_date = to_date - timedelta(days=7) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity(from_date=from_date) assert isinstance(response, pd.DataFrame) assert response.shape[1] == 9 def test_fetch_account_activity_from_to(self, ig_service: IGService): - to_date = datetime(2023, 7, 18) - from_date = to_date - timedelta(days=7) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity( from_date=from_date, to_date=to_date ) @@ -198,8 +198,8 @@ def test_fetch_account_activity_from_to(self, ig_service: IGService): assert response.shape[1] == 9 def test_fetch_account_activity_detailed(self, ig_service): - to_date = datetime(2023, 7, 18) - from_date = to_date - timedelta(days=7) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity( from_date=from_date, to_date=to_date, detailed=True ) @@ -208,7 +208,7 @@ def test_fetch_account_activity_detailed(self, ig_service): def test_fetch_account_activity_old(self, ig_service: IGService): from_date = datetime(1970, 1, 1) - to_date = from_date + timedelta(days=7) + to_date = from_date + timedelta(days=60) response = ig_service.fetch_account_activity( from_date=from_date, to_date=to_date ) @@ -216,8 +216,8 @@ def test_fetch_account_activity_old(self, ig_service: IGService): assert response.shape[0] == 0 def test_fetch_account_activity_fiql(self, ig_service: IGService): - to_date = datetime(2023, 7, 18) - from_date = to_date - timedelta(days=30) + to_date = datetime.now() - timedelta(days=30) + from_date = to_date - timedelta(days=60) response = ig_service.fetch_account_activity( from_date=from_date, to_date=to_date, fiql_filter="channel==PUBLIC_WEB_API" )