numerapi 2.23.0.dev2__tar.gz → 2.23.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/PKG-INFO +1 -1
  2. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/__init__.py +2 -2
  3. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/base_api.py +74 -2
  4. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/PKG-INFO +1 -1
  5. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/SOURCES.txt +1 -0
  6. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/setup.py +1 -1
  7. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_base_api.py +63 -0
  8. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_cli.py +19 -0
  9. numerapi-2.23.1/tests/test_cryptoapi.py +43 -0
  10. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/LICENSE +0 -0
  11. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/README.md +0 -0
  12. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/cli.py +0 -0
  13. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/cryptoapi.py +0 -0
  14. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/numerapi.py +0 -0
  15. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/py.typed +0 -0
  16. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/signalsapi.py +0 -0
  17. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/utils.py +0 -0
  18. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/dependency_links.txt +0 -0
  19. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/entry_points.txt +0 -0
  20. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/requires.txt +0 -0
  21. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/top_level.txt +0 -0
  22. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/setup.cfg +0 -0
  23. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_numerapi.py +0 -0
  24. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_signalsapi.py +0 -0
  25. {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numerapi
3
- Version: 2.23.0.dev2
3
+ Version: 2.23.1
4
4
  Summary: Automatically download and upload data for the Numerai machine learning competition
5
5
  Home-page: https://github.com/uuazed/numerapi
6
6
  Maintainer: uuazed
@@ -3,9 +3,9 @@
3
3
  from importlib.metadata import version, PackageNotFoundError
4
4
 
5
5
  try:
6
- __version__ = version("package-name")
6
+ __version__ = version("numerapi")
7
7
  except PackageNotFoundError:
8
- __version__ = 'unknown'
8
+ __version__ = "unknown"
9
9
 
10
10
 
11
11
  # pylint: disable=wrong-import-position
@@ -620,6 +620,79 @@ class Api:
620
620
  round_num = data["number"]
621
621
  return round_num
622
622
 
623
+ def list_rounds(
624
+ self,
625
+ number: int | None = None,
626
+ target: str | None = None,
627
+ status: str | None = None,
628
+ limit: int | None = None,
629
+ ) -> List[Dict]:
630
+ """List rounds with the filters supported by the round resolver.
631
+
632
+ Args:
633
+ number (int, optional): round number filter
634
+ target (str, optional): round target filter
635
+ status (str, optional): round status filter. One of `upcoming`,
636
+ `open`, `resolving`, or `resolved`
637
+ limit (int, optional): maximum number of rounds to return
638
+
639
+ Returns:
640
+ list of dicts: round entries matching the provided filters
641
+ """
642
+ query = """
643
+ query($tournament: Int
644
+ $number: Int
645
+ $target: String
646
+ $status: RoundStatus
647
+ $limit: Int) {
648
+ rounds(tournament: $tournament
649
+ number: $number
650
+ target: $target
651
+ status: $status
652
+ limit: $limit) {
653
+ id
654
+ tournament
655
+ number
656
+ target
657
+ closeTime
658
+ closeStakingTime
659
+ openTime
660
+ scoreTime
661
+ resolveTime
662
+ resolvedGeneral
663
+ resolvedStaking
664
+ payoutFactor
665
+ stakeThreshold
666
+ minCorrMultiplier
667
+ maxCorrMultiplier
668
+ defaultCorrMultiplier
669
+ minMmcMultiplier
670
+ maxMmcMultiplier
671
+ defaultMmcMultiplier
672
+ dataDatestamp
673
+ }
674
+ }
675
+ """
676
+ arguments = {
677
+ "tournament": self.tournament_id,
678
+ "number": number,
679
+ "target": target,
680
+ "status": None if status is None else status.upper(),
681
+ "limit": limit,
682
+ }
683
+ rounds = self.raw_query(query, arguments)["data"]["rounds"]
684
+ for round_info in rounds:
685
+ for field in [
686
+ "closeTime",
687
+ "closeStakingTime",
688
+ "openTime",
689
+ "scoreTime",
690
+ "resolveTime",
691
+ ]:
692
+ utils.replace(round_info, field, utils.parse_datetime_string)
693
+ utils.replace(round_info, "payoutFactor", utils.parse_float_string)
694
+ return rounds
695
+
623
696
  def set_bio(self, model_id: str, bio: str) -> bool:
624
697
  """Set bio field for a model id.
625
698
 
@@ -1054,7 +1127,6 @@ class Api:
1054
1127
  version: str | None = None,
1055
1128
  day: int | None = None,
1056
1129
  resolved: bool | None = None,
1057
- tournament: int | None = None,
1058
1130
  last_n_rounds: int | None = None,
1059
1131
  distinct_on_round: bool | None = None,
1060
1132
  ) -> List[Dict]:
@@ -1116,7 +1188,7 @@ class Api:
1116
1188
  "version": version,
1117
1189
  "day": day,
1118
1190
  "resolved": resolved,
1119
- "tournament": self.tournament_id if tournament is None else tournament,
1191
+ "tournament": self.tournament_id,
1120
1192
  "lastNRounds": last_n_rounds,
1121
1193
  "distinctOnRound": distinct_on_round,
1122
1194
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numerapi
3
- Version: 2.23.0.dev2
3
+ Version: 2.23.1
4
4
  Summary: Automatically download and upload data for the Numerai machine learning competition
5
5
  Home-page: https://github.com/uuazed/numerapi
6
6
  Maintainer: uuazed
@@ -17,6 +17,7 @@ numerapi.egg-info/requires.txt
17
17
  numerapi.egg-info/top_level.txt
18
18
  tests/test_base_api.py
19
19
  tests/test_cli.py
20
+ tests/test_cryptoapi.py
20
21
  tests/test_numerapi.py
21
22
  tests/test_signalsapi.py
22
23
  tests/test_utils.py
@@ -5,7 +5,7 @@ def load(path):
5
5
  return open(path, "r").read()
6
6
 
7
7
 
8
- numerapi_version = "2.23.0.dev2"
8
+ numerapi_version = "2.23.1"
9
9
 
10
10
 
11
11
  classifiers = [
@@ -178,6 +178,69 @@ def test_submission_scores(api):
178
178
  assert request_body["variables"]["distinctOnRound"] is True
179
179
 
180
180
 
181
+ @responses.activate
182
+ def test_list_rounds(api):
183
+ api.tournament_id = 11
184
+ data = {
185
+ "data": {
186
+ "rounds": [
187
+ {
188
+ "id": "round-1",
189
+ "tournament": 11,
190
+ "number": 123,
191
+ "target": "main",
192
+ "closeTime": "2026-03-27T00:00:00Z",
193
+ "closeStakingTime": "2026-03-26T12:00:00Z",
194
+ "openTime": "2026-03-20T00:00:00Z",
195
+ "scoreTime": "2026-03-29T00:00:00Z",
196
+ "resolveTime": "2026-04-01T00:00:00Z",
197
+ "resolvedGeneral": False,
198
+ "resolvedStaking": False,
199
+ "payoutFactor": "0.8",
200
+ "stakeThreshold": 0.1,
201
+ "minCorrMultiplier": 0.0,
202
+ "maxCorrMultiplier": 1.0,
203
+ "defaultCorrMultiplier": 0.5,
204
+ "minMmcMultiplier": 0.0,
205
+ "maxMmcMultiplier": 1.0,
206
+ "defaultMmcMultiplier": 0.5,
207
+ "dataDatestamp": 20260320,
208
+ }
209
+ ]
210
+ }
211
+ }
212
+ responses.add(responses.POST, base_api.API_TOURNAMENT_URL, json=data)
213
+
214
+ res = api.list_rounds(number=123, target="main", status="open", limit=5)
215
+
216
+ assert len(res) == 1
217
+ assert isinstance(res[0]["closeTime"], datetime.datetime)
218
+ assert isinstance(res[0]["closeStakingTime"], datetime.datetime)
219
+ assert isinstance(res[0]["openTime"], datetime.datetime)
220
+ assert isinstance(res[0]["scoreTime"], datetime.datetime)
221
+ assert isinstance(res[0]["resolveTime"], datetime.datetime)
222
+ assert isinstance(res[0]["payoutFactor"], decimal.Decimal)
223
+
224
+ request_body = json.loads(responses.calls[0].request.body)
225
+ assert request_body["variables"]["tournament"] == 11
226
+ assert request_body["variables"]["number"] == 123
227
+ assert request_body["variables"]["target"] == "main"
228
+ assert request_body["variables"]["status"] == "OPEN"
229
+ assert request_body["variables"]["limit"] == 5
230
+
231
+
232
+ @responses.activate
233
+ def test_list_rounds_uses_api_tournament_id_by_default(api):
234
+ api.tournament_id = 11
235
+ data = {"data": {"rounds": []}}
236
+ responses.add(responses.POST, base_api.API_TOURNAMENT_URL, json=data)
237
+
238
+ api.list_rounds()
239
+
240
+ request_body = json.loads(responses.calls[0].request.body)
241
+ assert request_body["variables"]["tournament"] == 11
242
+
243
+
181
244
  @responses.activate
182
245
  def test_pending_model_payouts(api):
183
246
  api.token = ("", "")
@@ -1,8 +1,11 @@
1
+ import importlib
2
+ import importlib.metadata
1
3
  import os
2
4
  import pytest
3
5
  from click.testing import CliRunner
4
6
  from unittest.mock import patch
5
7
 
8
+ import numerapi
6
9
  from numerapi import cli
7
10
 
8
11
 
@@ -117,3 +120,19 @@ def test_version():
117
120
  result = CliRunner().invoke(cli.version)
118
121
  # just testing if calling works fine
119
122
  assert result.exit_code == 0
123
+
124
+
125
+ def test_version_uses_numerapi_distribution_name(monkeypatch):
126
+ called_package_names = []
127
+
128
+ def mock_version(package_name):
129
+ called_package_names.append(package_name)
130
+ return "2.23.1"
131
+
132
+ with monkeypatch.context() as context:
133
+ context.setattr(importlib.metadata, "version", mock_version)
134
+ importlib.reload(numerapi)
135
+ assert called_package_names == ["numerapi"]
136
+ assert numerapi.__version__ == "2.23.1"
137
+
138
+ importlib.reload(numerapi)
@@ -0,0 +1,43 @@
1
+ import decimal
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ import numerapi
7
+
8
+
9
+ @pytest.fixture(scope="function", name="api")
10
+ def api_fixture():
11
+ api = numerapi.CryptoAPI(verbosity="DEBUG")
12
+ return api
13
+
14
+
15
+ @patch("numerapi.cryptoapi.CryptoAPI.raw_query")
16
+ def test_get_leaderboard(mocked, api):
17
+ mocked.return_value = {
18
+ "data": {
19
+ "cryptosignalsLeaderboard": [
20
+ {
21
+ "nmrStaked": "13.0",
22
+ "rank": 1,
23
+ "username": "crypto_user",
24
+ "corrRep": 0.1,
25
+ "mmcRep": 0.2,
26
+ "return_1_day": 0.03,
27
+ "return_52_weeks": 0.4,
28
+ "return_13_weeks": 0.15,
29
+ }
30
+ ]
31
+ }
32
+ }
33
+
34
+ lb = api.get_leaderboard(1)
35
+
36
+ assert len(lb) == 1
37
+ assert lb[0]["username"] == "crypto_user"
38
+ assert lb[0]["nmrStaked"] == decimal.Decimal("13.0")
39
+ mocked.assert_called_once()
40
+ args, kwargs = mocked.call_args
41
+ assert "cryptosignalsLeaderboard" in args[0]
42
+ assert args[1] == {"limit": 1, "offset": 0}
43
+ assert kwargs == {}
File without changes
File without changes
File without changes