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.
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/PKG-INFO +1 -1
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/__init__.py +2 -2
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/base_api.py +74 -2
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/PKG-INFO +1 -1
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/SOURCES.txt +1 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/setup.py +1 -1
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_base_api.py +63 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_cli.py +19 -0
- numerapi-2.23.1/tests/test_cryptoapi.py +43 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/LICENSE +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/README.md +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/cli.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/cryptoapi.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/numerapi.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/py.typed +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/signalsapi.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi/utils.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/dependency_links.txt +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/entry_points.txt +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/requires.txt +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/numerapi.egg-info/top_level.txt +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/setup.cfg +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_numerapi.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_signalsapi.py +0 -0
- {numerapi-2.23.0.dev2 → numerapi-2.23.1}/tests/test_utils.py +0 -0
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
from importlib.metadata import version, PackageNotFoundError
|
|
4
4
|
|
|
5
5
|
try:
|
|
6
|
-
__version__ = version("
|
|
6
|
+
__version__ = version("numerapi")
|
|
7
7
|
except PackageNotFoundError:
|
|
8
|
-
__version__ =
|
|
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
|
|
1191
|
+
"tournament": self.tournament_id,
|
|
1120
1192
|
"lastNRounds": last_n_rounds,
|
|
1121
1193
|
"distinctOnRound": distinct_on_round,
|
|
1122
1194
|
}
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|