numerapi 2.23.0.dev2__tar.gz → 2.23.0.dev3__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.0.dev3}/PKG-INFO +1 -1
  2. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/base_api.py +79 -0
  3. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/PKG-INFO +1 -1
  4. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/SOURCES.txt +1 -0
  5. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/setup.py +1 -1
  6. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/tests/test_base_api.py +63 -0
  7. numerapi-2.23.0.dev3/tests/test_cryptoapi.py +43 -0
  8. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/LICENSE +0 -0
  9. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/README.md +0 -0
  10. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/__init__.py +0 -0
  11. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/cli.py +0 -0
  12. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/cryptoapi.py +0 -0
  13. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/numerapi.py +0 -0
  14. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/py.typed +0 -0
  15. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/signalsapi.py +0 -0
  16. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi/utils.py +0 -0
  17. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/dependency_links.txt +0 -0
  18. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/entry_points.txt +0 -0
  19. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/requires.txt +0 -0
  20. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/numerapi.egg-info/top_level.txt +0 -0
  21. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/setup.cfg +0 -0
  22. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/tests/test_cli.py +0 -0
  23. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/tests/test_numerapi.py +0 -0
  24. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/tests/test_signalsapi.py +0 -0
  25. {numerapi-2.23.0.dev2 → numerapi-2.23.0.dev3}/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.0.dev3
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
@@ -14,6 +14,7 @@ import requests
14
14
  from numerapi import utils
15
15
 
16
16
  API_TOURNAMENT_URL = "https://api-tournament.numer.ai"
17
+ _DEFAULT_TOURNAMENT = object()
17
18
 
18
19
 
19
20
  class Api:
@@ -620,6 +621,84 @@ class Api:
620
621
  round_num = data["number"]
621
622
  return round_num
622
623
 
624
+ def list_rounds(
625
+ self,
626
+ tournament: int | None | object = _DEFAULT_TOURNAMENT,
627
+ number: int | None = None,
628
+ target: str | None = None,
629
+ status: str | None = None,
630
+ limit: int | None = None,
631
+ ) -> List[Dict]:
632
+ """List rounds with the filters supported by the round resolver.
633
+
634
+ Args:
635
+ tournament (int, optional): tournament filter, defaults to the API
636
+ instance tournament. Pass `None` to omit the tournament filter
637
+ number (int, optional): round number filter
638
+ target (str, optional): round target filter
639
+ status (str, optional): round status filter. One of `upcoming`,
640
+ `open`, `resolving`, or `resolved`
641
+ limit (int, optional): maximum number of rounds to return
642
+
643
+ Returns:
644
+ list of dicts: round entries matching the provided filters
645
+ """
646
+ query = """
647
+ query($tournament: Int
648
+ $number: Int
649
+ $target: String
650
+ $status: RoundStatus
651
+ $limit: Int) {
652
+ rounds(tournament: $tournament
653
+ number: $number
654
+ target: $target
655
+ status: $status
656
+ limit: $limit) {
657
+ id
658
+ tournament
659
+ number
660
+ target
661
+ closeTime
662
+ closeStakingTime
663
+ openTime
664
+ scoreTime
665
+ resolveTime
666
+ resolvedGeneral
667
+ resolvedStaking
668
+ payoutFactor
669
+ stakeThreshold
670
+ minCorrMultiplier
671
+ maxCorrMultiplier
672
+ defaultCorrMultiplier
673
+ minMmcMultiplier
674
+ maxMmcMultiplier
675
+ defaultMmcMultiplier
676
+ dataDatestamp
677
+ }
678
+ }
679
+ """
680
+ if tournament is _DEFAULT_TOURNAMENT:
681
+ tournament = self.tournament_id if self.tournament_id else None
682
+ arguments = {
683
+ "tournament": tournament,
684
+ "number": number,
685
+ "target": target,
686
+ "status": None if status is None else status.upper(),
687
+ "limit": limit,
688
+ }
689
+ rounds = self.raw_query(query, arguments)["data"]["rounds"]
690
+ for round_info in rounds:
691
+ for field in [
692
+ "closeTime",
693
+ "closeStakingTime",
694
+ "openTime",
695
+ "scoreTime",
696
+ "resolveTime",
697
+ ]:
698
+ utils.replace(round_info, field, utils.parse_datetime_string)
699
+ utils.replace(round_info, "payoutFactor", utils.parse_float_string)
700
+ return rounds
701
+
623
702
  def set_bio(self, model_id: str, bio: str) -> bool:
624
703
  """Set bio field for a model id.
625
704
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numerapi
3
- Version: 2.23.0.dev2
3
+ Version: 2.23.0.dev3
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.0.dev3"
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_without_tournament_filter(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(tournament=None)
239
+
240
+ request_body = json.loads(responses.calls[0].request.body)
241
+ assert request_body["variables"]["tournament"] is None
242
+
243
+
181
244
  @responses.activate
182
245
  def test_pending_model_payouts(api):
183
246
  api.token = ("", "")
@@ -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