numerapi 2.23.0.dev1__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.dev1 → numerapi-2.23.0.dev3}/PKG-INFO +1 -1
  2. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/base_api.py +80 -1
  3. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/PKG-INFO +1 -1
  4. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/SOURCES.txt +1 -0
  5. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/setup.py +1 -1
  6. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/tests/test_base_api.py +67 -0
  7. numerapi-2.23.0.dev3/tests/test_cryptoapi.py +43 -0
  8. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/LICENSE +0 -0
  9. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/README.md +0 -0
  10. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/__init__.py +0 -0
  11. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/cli.py +0 -0
  12. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/cryptoapi.py +0 -0
  13. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/numerapi.py +0 -0
  14. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/py.typed +0 -0
  15. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/signalsapi.py +0 -0
  16. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi/utils.py +0 -0
  17. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/dependency_links.txt +0 -0
  18. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/entry_points.txt +0 -0
  19. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/requires.txt +0 -0
  20. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/numerapi.egg-info/top_level.txt +0 -0
  21. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/setup.cfg +0 -0
  22. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/tests/test_cli.py +0 -0
  23. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/tests/test_numerapi.py +0 -0
  24. {numerapi-2.23.0.dev1 → numerapi-2.23.0.dev3}/tests/test_signalsapi.py +0 -0
  25. {numerapi-2.23.0.dev1 → 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.dev1
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:
@@ -427,7 +428,7 @@ class Api:
427
428
 
428
429
  max_amount = max_amount if max_amount is not None else amount
429
430
  query = """
430
- mutation($submissionId: ID!, $staker: String!, $maxAmount: String!) {
431
+ query($submissionId: ID!, $staker: String!, $maxAmount: String!) {
431
432
  v3StakeAuth(
432
433
  submissionId: $submissionId
433
434
  staker: $staker
@@ -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.dev1
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.dev1"
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 = ("", "")
@@ -297,6 +360,8 @@ def test_v3_stake_auth(api):
297
360
  result = api.v3_stake_auth("submission-id", "0xstaker", amount=25)
298
361
 
299
362
  body = json.loads(responses.calls[0].request.body)
363
+ assert "mutation" not in body["query"]
364
+ assert "query(" in body["query"]
300
365
  assert "v3StakeAuth" in body["query"]
301
366
  assert body["variables"]["submissionId"] == "submission-id"
302
367
  assert body["variables"]["maxAmount"] == "25"
@@ -335,6 +400,8 @@ def test_v3_stake_auth_accepts_max_amount(api):
335
400
  )
336
401
 
337
402
  body = json.loads(responses.calls[0].request.body)
403
+ assert "mutation" not in body["query"]
404
+ assert "query(" in body["query"]
338
405
  assert body["variables"]["maxAmount"] == "30"
339
406
  assert result["maxAmount"] == "30"
340
407
  assert result["amount"] == "30"
@@ -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