numerai-cli 1.0.3__tar.gz → 1.1.0__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.
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/PKG-INFO +2 -1
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/README.md +1 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/constants.py +1 -7
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/node/__init__.py +20 -16
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/node/config.py +17 -6
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/node/test.py +2 -3
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/util/docker.py +37 -12
- {numerai-cli-1.0.3/numerai/examples/tournament-python3 → numerai-cli-1.1.0/numerai/examples/crypto-python3}/Dockerfile +3 -3
- numerai-cli-1.1.0/numerai/examples/crypto-python3/predict.py +105 -0
- numerai-cli-1.1.0/numerai/examples/crypto-python3/requirements.txt +5 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/examples/signals-python3/Dockerfile +4 -4
- numerai-cli-1.1.0/numerai/examples/signals-python3/predict.py +104 -0
- numerai-cli-1.1.0/numerai/examples/signals-python3/requirements.txt +5 -0
- numerai-cli-1.1.0/numerai/examples/tournament-python3/Dockerfile +35 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/examples/tournament-python3/predict.py +17 -7
- numerai-cli-1.1.0/numerai/examples/tournament-python3/requirements.txt +5 -0
- numerai-cli-1.1.0/numerai/examples/tournament-python3/train.py +10 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/-inputs.tf +6 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/-main.tf +1 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/-inputs.tf +6 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/cluster.tf +50 -5
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/PKG-INFO +2 -1
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/SOURCES.txt +3 -1
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/setup.py +1 -1
- numerai-cli-1.0.3/numerai/examples/signals-python3/data.py +0 -164
- numerai-cli-1.0.3/numerai/examples/signals-python3/predict.py +0 -160
- numerai-cli-1.0.3/numerai/examples/signals-python3/requirements.txt +0 -8
- numerai-cli-1.0.3/numerai/examples/signals-python3/train.py +0 -6
- numerai-cli-1.0.3/numerai/examples/tournament-python3/requirements.txt +0 -5
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/MANIFEST.in +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/__init__.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/__init__.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/destroy_all.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/doctor.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/misc.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/node/deploy.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/node/destroy.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/setup.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/uninstall.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/upgrade.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/util/__init__.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/util/debug.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/util/files.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/cli/util/keys.py +0 -0
- {numerai-cli-1.0.3/numerai/examples/tournament-python3 → numerai-cli-1.1.0/numerai/examples/signals-python3}/train.py +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/examples/tournament-rlang/Dockerfile +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/examples/tournament-rlang/install_packages.R +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/examples/tournament-rlang/main.R +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/-outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/-locals.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/-outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/main.zip +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/network.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/aws/aws/webhook.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure/cluster.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure/input.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure/locals.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure/outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure/webhook.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure_cron_trigger.zip +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/azure_trigger.zip +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/input.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/main.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/azure/registry.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/-inputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/-main.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/-outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/cloud-function.zip +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/-inputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/-locals.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/-outputs.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/cluster.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/webhook.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/gcp/workflow-source.yaml +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/registry.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai/terraform/gcp/services.tf +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/dependency_links.txt +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/entry_points.txt +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/requires.txt +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/numerai_cli.egg-info/top_level.txt +0 -0
- {numerai-cli-1.0.3 → numerai-cli-1.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: numerai-cli
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: A library for deploying Numer.ai Prediction Nodes.
|
|
5
5
|
Home-page: https://github.com/numerai/numerai-cli
|
|
6
6
|
Author: Numer.ai
|
|
@@ -11,6 +11,7 @@ Description: # numerai-cli
|
|
|
11
11
|
[](https://pypi.org/project/numerai-cli/)
|
|
12
12
|
|
|
13
13
|
Welcome to the Numerai CLI for the [Numerai Tournament](https://docs.numer.ai/tournament/learn).
|
|
14
|
+
Numerai CLI (aka Compute Heavy) is a tool to help you set up cloud automation for your Numerai predictions.
|
|
14
15
|
This README is designed to have EVERYTHING you need to setup and maintain a Numerai Compute Heavy node.
|
|
15
16
|
|
|
16
17
|
This CLI runs on your local computer to configure a Numerai Prediction Node in the cloud.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://pypi.org/project/numerai-cli/)
|
|
4
4
|
|
|
5
5
|
Welcome to the Numerai CLI for the [Numerai Tournament](https://docs.numer.ai/tournament/learn).
|
|
6
|
+
Numerai CLI (aka Compute Heavy) is a tool to help you set up cloud automation for your Numerai predictions.
|
|
6
7
|
This README is designed to have EVERYTHING you need to setup and maintain a Numerai Compute Heavy node.
|
|
7
8
|
|
|
8
9
|
This CLI runs on your local computer to configure a Numerai Prediction Node in the cloud.
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""Constants for Numerai CLI"""
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
import json
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
|
-
TOURNAMENT_NUMERAI = 8
|
|
7
|
-
TOURNAMENT_SIGNALS = 11
|
|
8
7
|
PACKAGE_PATH = os.path.dirname(__file__)
|
|
9
8
|
CONFIG_PATH = os.path.join(str(Path.home()), ".numerai")
|
|
10
9
|
KEYS_PATH = os.path.join(CONFIG_PATH, ".keys")
|
|
@@ -65,11 +64,6 @@ DEFAULT_SETTINGS = {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
CONSTANTS_STR = f"""Default values (not your configured node values):
|
|
68
|
-
|
|
69
|
-
---Tournament Numbers---
|
|
70
|
-
TOURNAMENT_NUMERAI: {TOURNAMENT_NUMERAI}
|
|
71
|
-
TOURNAMENT_SIGNALS: {TOURNAMENT_SIGNALS}
|
|
72
|
-
|
|
73
67
|
---Paths---
|
|
74
68
|
PACKAGE_PATH: {PACKAGE_PATH}
|
|
75
69
|
CONFIG_PATH: {CONFIG_PATH}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Init for node"""
|
|
2
|
+
|
|
2
3
|
import json
|
|
3
4
|
import logging
|
|
4
5
|
import click
|
|
@@ -15,22 +16,23 @@ from numerai.cli.util.keys import get_numerai_keys
|
|
|
15
16
|
# Setting azure's logging level "ERROR" to avoid spamming the terminal
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
def tournaments_dict():
|
|
20
|
+
napi = base_api.Api()
|
|
21
|
+
tournaments = napi.raw_query('query { tournaments { name tournament } }')
|
|
22
|
+
return {t["tournament"]: t["name"] for t in tournaments["data"]["tournaments"]}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_models(tournament):
|
|
25
26
|
napi = base_api.Api(*get_numerai_keys())
|
|
26
27
|
models = napi.get_models(tournament)
|
|
27
|
-
|
|
28
|
+
tournaments = napi.raw_query('query { tournaments { name tournament } }')
|
|
29
|
+
name_prefix = tournaments_dict()[tournament]
|
|
28
30
|
model_dict = {}
|
|
29
31
|
for model_name, model_id in models.items():
|
|
30
32
|
model_dict[model_name] = {
|
|
31
33
|
"id": model_id,
|
|
32
34
|
"name": f"{name_prefix}-{model_name}",
|
|
33
|
-
"
|
|
35
|
+
"tournament": tournament,
|
|
34
36
|
}
|
|
35
37
|
return model_dict
|
|
36
38
|
|
|
@@ -43,16 +45,18 @@ def get_models(signals):
|
|
|
43
45
|
type=str,
|
|
44
46
|
prompt=True,
|
|
45
47
|
help="The name of one of your models to configure the Prediction Node for."
|
|
46
|
-
"It defaults to the first model returned from your account.",
|
|
48
|
+
" It defaults to the first model returned from your account.",
|
|
47
49
|
)
|
|
48
50
|
@click.option(
|
|
49
|
-
"--
|
|
50
|
-
"-
|
|
51
|
-
|
|
52
|
-
help="Target a
|
|
51
|
+
"--tournament",
|
|
52
|
+
"-t",
|
|
53
|
+
default=8,
|
|
54
|
+
help="Target a specific tournament number."
|
|
55
|
+
" Defaults to Numerai Tournament/Classic."
|
|
56
|
+
f" Available tournaments: {json.dumps(tournaments_dict(), indent=2)}",
|
|
53
57
|
)
|
|
54
58
|
@click.pass_context
|
|
55
|
-
def node(ctx, verbose, model_name,
|
|
59
|
+
def node(ctx, verbose, model_name, tournament):
|
|
56
60
|
"""
|
|
57
61
|
Commands to manage and test Prediction Nodes.
|
|
58
62
|
"""
|
|
@@ -68,7 +72,7 @@ def node(ctx, verbose, model_name, signals):
|
|
|
68
72
|
else:
|
|
69
73
|
logger.setLevel(logging.ERROR)
|
|
70
74
|
|
|
71
|
-
models = get_models(
|
|
75
|
+
models = get_models(tournament)
|
|
72
76
|
|
|
73
77
|
try:
|
|
74
78
|
ctx.ensure_object(dict)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Config command for Numerai CLI"""
|
|
2
|
+
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
4
5
|
import click
|
|
@@ -94,6 +95,12 @@ from numerai.cli.util.keys import get_provider_keys, get_numerai_keys, load_or_i
|
|
|
94
95
|
help="Forces your webhook to register with Numerai. "
|
|
95
96
|
"Use in conjunction with options that prevent webhook auto-registering.",
|
|
96
97
|
)
|
|
98
|
+
@click.option(
|
|
99
|
+
"--volume",
|
|
100
|
+
"-v",
|
|
101
|
+
type=int,
|
|
102
|
+
help="Specify additional block storage in GB. Currently only supported in AWS.",
|
|
103
|
+
)
|
|
97
104
|
@click.pass_context
|
|
98
105
|
def config(
|
|
99
106
|
ctx,
|
|
@@ -107,6 +114,7 @@ def config(
|
|
|
107
114
|
cron,
|
|
108
115
|
timeout_minutes,
|
|
109
116
|
register_webhook,
|
|
117
|
+
volume,
|
|
110
118
|
):
|
|
111
119
|
"""
|
|
112
120
|
Uses Terraform to create a full Numerai Compute cluster in your desired provider.
|
|
@@ -262,12 +270,12 @@ def config(
|
|
|
262
270
|
provider_registry_conf = create_gcp_registry(provider, verbose=verbose)
|
|
263
271
|
node_conf.update(provider_registry_conf)
|
|
264
272
|
registry_parts = node_conf["registry_id"].split("/")
|
|
265
|
-
node_conf[
|
|
266
|
-
"
|
|
267
|
-
|
|
268
|
-
node_conf[
|
|
269
|
-
"
|
|
270
|
-
|
|
273
|
+
node_conf["artifact_registry_login_url"] = (
|
|
274
|
+
f"https://{registry_parts[3]}-docker.pkg.dev/"
|
|
275
|
+
)
|
|
276
|
+
node_conf["docker_repo"] = (
|
|
277
|
+
f"{registry_parts[3]}-docker.pkg.dev/{registry_parts[1]}/numerai-container-registry/{node}:latest"
|
|
278
|
+
)
|
|
271
279
|
docker.login(node_conf, verbose)
|
|
272
280
|
try:
|
|
273
281
|
docker.manifest_inspect(node_conf["docker_repo"], verbose)
|
|
@@ -276,6 +284,9 @@ def config(
|
|
|
276
284
|
docker.tag("hello-world:linux", node_conf["docker_repo"], verbose)
|
|
277
285
|
docker.push(node_conf["docker_repo"], verbose)
|
|
278
286
|
nodes_config[node] = node_conf
|
|
287
|
+
elif provider == "aws":
|
|
288
|
+
if volume is not None:
|
|
289
|
+
node_conf["volume"] = volume
|
|
279
290
|
|
|
280
291
|
store_config(NODES_PATH, nodes_config)
|
|
281
292
|
|
|
@@ -61,7 +61,7 @@ def test(ctx, local, command, verbose):
|
|
|
61
61
|
ctx.ensure_object(dict)
|
|
62
62
|
model = ctx.obj["model"]
|
|
63
63
|
node = model["name"]
|
|
64
|
-
|
|
64
|
+
tournament = model["tournament"]
|
|
65
65
|
node_config = load_or_init_nodes(node)
|
|
66
66
|
provider = node_config["provider"]
|
|
67
67
|
|
|
@@ -143,7 +143,6 @@ def test(ctx, local, command, verbose):
|
|
|
143
143
|
variables={"modelId": node_config["model_id"]},
|
|
144
144
|
authorization=True,
|
|
145
145
|
)
|
|
146
|
-
tournament = TOURNAMENT_SIGNALS if is_signals else TOURNAMENT_NUMERAI
|
|
147
146
|
curr_round = api.get_current_round(tournament)
|
|
148
147
|
latest_subs = sorted(
|
|
149
148
|
filter(
|
|
@@ -338,7 +337,7 @@ def get_recent_task_status_aws(cluster_arn, ecs_client, node, trigger_id):
|
|
|
338
337
|
"red",
|
|
339
338
|
)
|
|
340
339
|
elif matched_task["lastStatus"] in stopped_codes:
|
|
341
|
-
return matched_task, True, "Job execution
|
|
340
|
+
return matched_task, True, "Job execution finished!\r", "green"
|
|
342
341
|
elif matched_task["lastStatus"] in pending_codes:
|
|
343
342
|
return matched_task, False, "Waiting for job to start...", "yellow"
|
|
344
343
|
elif matched_task["lastStatus"] in running_codes:
|
|
@@ -166,9 +166,16 @@ def build(node_config, node, verbose):
|
|
|
166
166
|
build_arg_str += f" --build-arg NODE={node}"
|
|
167
167
|
|
|
168
168
|
cmd = (
|
|
169
|
-
f'docker build --platform=linux/amd64 -t {node_config["docker_repo"]}'
|
|
169
|
+
f'docker build --platform=linux/amd64 --load -t {node_config["docker_repo"]}'
|
|
170
|
+
f"{build_arg_str} -f {path}/Dockerfile ."
|
|
170
171
|
)
|
|
171
|
-
execute(cmd, verbose)
|
|
172
|
+
stdout, stderr = execute(cmd, verbose)
|
|
173
|
+
if "unknown flag: --load" in (stdout.decode() + stderr.decode()):
|
|
174
|
+
click.secho(
|
|
175
|
+
"Docker version too old, please upgrade to at least 18.09",
|
|
176
|
+
fg="red",
|
|
177
|
+
)
|
|
178
|
+
exit(1)
|
|
172
179
|
|
|
173
180
|
|
|
174
181
|
def run(node_config, verbose, command=""):
|
|
@@ -181,7 +188,9 @@ def login(node_config, verbose):
|
|
|
181
188
|
username, password = login_aws()
|
|
182
189
|
login_url = node_config['docker_repo']
|
|
183
190
|
elif node_config["provider"] == PROVIDER_AZURE:
|
|
184
|
-
username, password = login_azure(
|
|
191
|
+
username, password = login_azure(
|
|
192
|
+
node_config["registry_rg_name"], node_config["registry_name"]
|
|
193
|
+
)
|
|
185
194
|
login_url = node_config['docker_repo']
|
|
186
195
|
elif node_config["provider"] == PROVIDER_GCP:
|
|
187
196
|
username, password = login_gcp()
|
|
@@ -214,17 +223,23 @@ def login_aws():
|
|
|
214
223
|
)
|
|
215
224
|
|
|
216
225
|
token = ecr_client.get_authorization_token() # TODO: use registryIds
|
|
217
|
-
username, password =
|
|
226
|
+
username, password = (
|
|
227
|
+
base64.b64decode(token["authorizationData"][0]["authorizationToken"])
|
|
228
|
+
.decode()
|
|
229
|
+
.split(":")
|
|
230
|
+
)
|
|
218
231
|
|
|
219
232
|
return username, password
|
|
220
233
|
|
|
221
234
|
|
|
222
235
|
def login_azure(resource_group_name, registry_name):
|
|
223
236
|
azure_subs_id, azure_client, azure_tenant, azure_secret = get_azure_keys()
|
|
224
|
-
credentials = ClientSecretCredential(
|
|
225
|
-
|
|
226
|
-
resource_group_name, registry_name
|
|
237
|
+
credentials = ClientSecretCredential(
|
|
238
|
+
client_id=azure_client, tenant_id=azure_tenant, client_secret=azure_secret
|
|
227
239
|
)
|
|
240
|
+
username_password = ContainerRegistryManagementClient(
|
|
241
|
+
credentials, azure_subs_id
|
|
242
|
+
).registries.list_credentials(resource_group_name, registry_name)
|
|
228
243
|
username = username_password.username
|
|
229
244
|
password = username_password.passwords[0].value
|
|
230
245
|
return username, password
|
|
@@ -288,24 +303,32 @@ def cleanup_aws(docker_repo):
|
|
|
288
303
|
|
|
289
304
|
docker_repo_name = docker_repo.split("/")[-1]
|
|
290
305
|
|
|
291
|
-
resp = ecr_client.list_images(
|
|
306
|
+
resp = ecr_client.list_images(
|
|
307
|
+
repositoryName=docker_repo_name, filter={"tagStatus": "UNTAGGED"}
|
|
308
|
+
)
|
|
292
309
|
|
|
293
310
|
imageIds = resp["imageIds"]
|
|
294
311
|
if len(imageIds) == 0:
|
|
295
312
|
return []
|
|
296
313
|
|
|
297
|
-
resp = ecr_client.batch_delete_image(
|
|
314
|
+
resp = ecr_client.batch_delete_image(
|
|
315
|
+
repositoryName=docker_repo_name, imageIds=imageIds
|
|
316
|
+
)
|
|
298
317
|
|
|
299
318
|
return resp["imageIds"]
|
|
300
319
|
|
|
301
320
|
|
|
302
321
|
def cleanup_azure(node_config):
|
|
303
322
|
_, azure_client, azure_tenant, azure_secret = get_azure_keys()
|
|
304
|
-
credentials = ClientSecretCredential(
|
|
323
|
+
credentials = ClientSecretCredential(
|
|
324
|
+
client_id=azure_client, tenant_id=azure_tenant, client_secret=azure_secret
|
|
325
|
+
)
|
|
305
326
|
acr_client = ContainerRegistryClient(node_config["acr_login_server"], credentials)
|
|
306
327
|
docker_repo = node_config["docker_repo"]
|
|
307
328
|
node_repo_name = [
|
|
308
|
-
repo_name
|
|
329
|
+
repo_name
|
|
330
|
+
for repo_name in acr_client.list_repository_names()
|
|
331
|
+
if repo_name == docker_repo.split("/")[-1]
|
|
309
332
|
][0]
|
|
310
333
|
|
|
311
334
|
# get all manifests, ordered by last update time
|
|
@@ -318,7 +341,9 @@ def cleanup_azure(node_config):
|
|
|
318
341
|
# Remove all but the latest manifest
|
|
319
342
|
removed_manifests = []
|
|
320
343
|
for manifest in manifest_list[1:]:
|
|
321
|
-
acr_client.update_manifest_properties(
|
|
344
|
+
acr_client.update_manifest_properties(
|
|
345
|
+
node_repo_name, manifest.digest, can_write=True, can_delete=True
|
|
346
|
+
)
|
|
322
347
|
removed_manifests.append(manifest.digest)
|
|
323
348
|
acr_client.delete_manifest(node_repo_name, manifest.digest)
|
|
324
349
|
return removed_manifests
|
|
@@ -4,9 +4,9 @@ FROM python:3.9
|
|
|
4
4
|
# These are docker arguments that `numerai node deploy/test` will always pass into docker.
|
|
5
5
|
# They are then set in your environment so that numerapi can access them when uploading submissions.
|
|
6
6
|
# You can also access them from your script like so:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
# import os
|
|
8
|
+
# public_id = os.environ["NUMERAI_PUBLIC_ID"]
|
|
9
|
+
# secret_key = os.environ["NUMERAI_SECRET_KEY"]
|
|
10
10
|
ARG NUMERAI_PUBLIC_ID
|
|
11
11
|
ENV NUMERAI_PUBLIC_ID=$NUMERAI_PUBLIC_ID
|
|
12
12
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
""" Sample tournament model in python 3 """
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import joblib
|
|
7
|
+
import numerapi
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import lightgbm as lgbm
|
|
10
|
+
|
|
11
|
+
logging.basicConfig(filename="log.txt", filemode="a")
|
|
12
|
+
|
|
13
|
+
TOURNAMENT = 12
|
|
14
|
+
DATA_VERSION = "crypto/v1.0"
|
|
15
|
+
TRAINED_MODEL_PREFIX = "./trained_model"
|
|
16
|
+
|
|
17
|
+
DEFAULT_MODEL_ID = None
|
|
18
|
+
DEFAULT_PUBLIC_ID = None
|
|
19
|
+
DEFAULT_SECRET_KEY = None
|
|
20
|
+
|
|
21
|
+
# Read model id and initialize API client with api keys
|
|
22
|
+
# these are set by the docker image that you deploy after training,
|
|
23
|
+
# but you can also set them manually above for local testing
|
|
24
|
+
MODEL_ID = os.getenv("MODEL_ID", DEFAULT_MODEL_ID)
|
|
25
|
+
napi = numerapi.NumerAPI(
|
|
26
|
+
public_id=os.getenv("NUMERAI_PUBLIC_ID", DEFAULT_PUBLIC_ID),
|
|
27
|
+
secret_key=os.getenv("NUMERAI_SECRET_KEY", DEFAULT_SECRET_KEY),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def train(napi, model_id, force_training=False):
|
|
32
|
+
model_name = TRAINED_MODEL_PREFIX
|
|
33
|
+
if model_id:
|
|
34
|
+
model_name += f"_{model_id}"
|
|
35
|
+
|
|
36
|
+
# load a model if we have a trained model already and we aren't forcing a training session
|
|
37
|
+
if os.path.exists(model_name) and not force_training:
|
|
38
|
+
logging.info("loading existing trained model")
|
|
39
|
+
model = joblib.load(model_name)
|
|
40
|
+
return model
|
|
41
|
+
|
|
42
|
+
logging.info("reading training data")
|
|
43
|
+
napi.download_dataset(f"{DATA_VERSION}/train_targets.parquet")
|
|
44
|
+
target = pd.read_parquet(f"{DATA_VERSION}/train_targets.parquet")
|
|
45
|
+
|
|
46
|
+
# TODO: implement get_features and train a model
|
|
47
|
+
# This will take a few minutes 🍵
|
|
48
|
+
# logging.info("training model")
|
|
49
|
+
# model = lgbm.LGBMRegressor(
|
|
50
|
+
# n_estimators=2000,
|
|
51
|
+
# learning_rate=0.01,
|
|
52
|
+
# max_depth=5,
|
|
53
|
+
# num_leaves=2**5-1,
|
|
54
|
+
# colsample_bytree=0.1
|
|
55
|
+
# )
|
|
56
|
+
# model.fit(
|
|
57
|
+
# train_data[feature_cols],
|
|
58
|
+
# train_data["target"]
|
|
59
|
+
# )
|
|
60
|
+
|
|
61
|
+
# logging.info("saving model")
|
|
62
|
+
# joblib.dump(model, model_name)
|
|
63
|
+
# return model
|
|
64
|
+
|
|
65
|
+
# just return the target for now
|
|
66
|
+
return target
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def predict(napi, model):
|
|
70
|
+
logging.info("reading prediction data")
|
|
71
|
+
napi.download_dataset(f"{DATA_VERSION}/live_universe.parquet")
|
|
72
|
+
live_universe = pd.read_parquet(f"{DATA_VERSION}/live_universe.parquet")
|
|
73
|
+
|
|
74
|
+
# TODO: implement get_features and predict the target
|
|
75
|
+
# logging.info("generating predictions")
|
|
76
|
+
# predictions = model.predict(get_features(live_universe))
|
|
77
|
+
# predictions = pd.DataFrame(
|
|
78
|
+
# predictions, columns=["prediction"], index=predict_data.index
|
|
79
|
+
# )
|
|
80
|
+
# return predictions
|
|
81
|
+
|
|
82
|
+
# just return the latest target for now
|
|
83
|
+
napi.download_dataset(f"{DATA_VERSION}/train_targets.parquet")
|
|
84
|
+
target = pd.read_parquet(f"{DATA_VERSION}/train_targets.parquet")
|
|
85
|
+
return (
|
|
86
|
+
target[target.date == target.date.max()]
|
|
87
|
+
.drop(columns=["date"])
|
|
88
|
+
.rename(columns={"target": "prediction"})
|
|
89
|
+
.set_index("symbol")
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def submit(predictions, predict_output_path="predictions.csv", model_id=None):
|
|
94
|
+
logging.info("writing predictions to file and submitting")
|
|
95
|
+
include_index = predictions.index.name is not None
|
|
96
|
+
predictions.to_csv(predict_output_path, index=include_index)
|
|
97
|
+
napi.upload_predictions(
|
|
98
|
+
predict_output_path, model_id=model_id, tournament=TOURNAMENT
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
trained_model = train(napi, MODEL_ID)
|
|
104
|
+
predictions = predict(napi, trained_model)
|
|
105
|
+
submit(predictions, model_id=MODEL_ID)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Provides us a working Python 3 environment.
|
|
2
|
-
FROM python:3.
|
|
2
|
+
FROM python:3.9
|
|
3
3
|
|
|
4
4
|
# These are docker arguments that `numerai node deploy/test` will always pass into docker.
|
|
5
5
|
# They are then set in your environment so that numerapi can access them when uploading submissions.
|
|
6
6
|
# You can also access them from your script like so:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
# import os
|
|
8
|
+
# public_id = os.environ["NUMERAI_PUBLIC_ID"]
|
|
9
|
+
# secret_key = os.environ["NUMERAI_SECRET_KEY"]
|
|
10
10
|
ARG NUMERAI_PUBLIC_ID
|
|
11
11
|
ENV NUMERAI_PUBLIC_ID=$NUMERAI_PUBLIC_ID
|
|
12
12
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
""" Sample tournament model in python 3 """
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import joblib
|
|
7
|
+
import numerapi
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import lightgbm as lgbm
|
|
10
|
+
|
|
11
|
+
logging.basicConfig(filename="log.txt", filemode="a")
|
|
12
|
+
|
|
13
|
+
TOURNAMENT = 11
|
|
14
|
+
DATA_VERSION = "signals/v1.0"
|
|
15
|
+
TARGET_COL = "target"
|
|
16
|
+
TRAINED_MODEL_PREFIX = "./trained_model"
|
|
17
|
+
|
|
18
|
+
DEFAULT_MODEL_ID = None
|
|
19
|
+
DEFAULT_PUBLIC_ID = None
|
|
20
|
+
DEFAULT_SECRET_KEY = None
|
|
21
|
+
|
|
22
|
+
# Read model id and initialize API client with api keys
|
|
23
|
+
# these are set by the docker image that you deploy after training,
|
|
24
|
+
# but you can also set them manually above for local testing
|
|
25
|
+
MODEL_ID = os.getenv("MODEL_ID", DEFAULT_MODEL_ID)
|
|
26
|
+
napi = numerapi.NumerAPI(
|
|
27
|
+
public_id=os.getenv("NUMERAI_PUBLIC_ID", DEFAULT_PUBLIC_ID),
|
|
28
|
+
secret_key=os.getenv("NUMERAI_SECRET_KEY", DEFAULT_SECRET_KEY),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def train(napi, model_id, force_training=False):
|
|
33
|
+
model_name = TRAINED_MODEL_PREFIX
|
|
34
|
+
if model_id:
|
|
35
|
+
model_name += f"_{model_id}"
|
|
36
|
+
|
|
37
|
+
# load a model if we have a trained model already and we aren't forcing a training session
|
|
38
|
+
if os.path.exists(model_name) and not force_training:
|
|
39
|
+
logging.info("loading existing trained model")
|
|
40
|
+
model = joblib.load(model_name)
|
|
41
|
+
return model
|
|
42
|
+
|
|
43
|
+
logging.info("reading training data")
|
|
44
|
+
napi.download_dataset(f"{DATA_VERSION}/train.parquet")
|
|
45
|
+
train_data = pd.read_parquet(f"{DATA_VERSION}/train.parquet")
|
|
46
|
+
feature_cols = [
|
|
47
|
+
col
|
|
48
|
+
for col in train_data.columns
|
|
49
|
+
if col.startswith('feature_')
|
|
50
|
+
and col not in ("feature_country", "feature_exchange_code")
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# This will take a few minutes 🍵
|
|
54
|
+
logging.info("training model")
|
|
55
|
+
model = lgbm.LGBMRegressor(
|
|
56
|
+
n_estimators=2000,
|
|
57
|
+
learning_rate=0.01,
|
|
58
|
+
max_depth=5,
|
|
59
|
+
num_leaves=2**5 - 1,
|
|
60
|
+
colsample_bytree=0.1,
|
|
61
|
+
)
|
|
62
|
+
model.fit(train_data[feature_cols], train_data["target"])
|
|
63
|
+
|
|
64
|
+
logging.info("saving model")
|
|
65
|
+
joblib.dump(model, model_name)
|
|
66
|
+
return model
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def predict(napi, model):
|
|
70
|
+
logging.info("reading prediction data")
|
|
71
|
+
napi.download_dataset(f"{DATA_VERSION}/live.parquet")
|
|
72
|
+
predict_data = pd.read_parquet(f"{DATA_VERSION}/live.parquet").set_index(
|
|
73
|
+
'numerai_ticker'
|
|
74
|
+
)
|
|
75
|
+
feature_cols = [
|
|
76
|
+
col
|
|
77
|
+
for col in predict_data.columns
|
|
78
|
+
if col.startswith('feature_')
|
|
79
|
+
and col not in ("feature_country", "feature_exchange_code")
|
|
80
|
+
]
|
|
81
|
+
print(predict_data)
|
|
82
|
+
|
|
83
|
+
logging.info("generating predictions")
|
|
84
|
+
predictions = model.predict(predict_data[feature_cols])
|
|
85
|
+
predictions = pd.DataFrame(
|
|
86
|
+
predictions, columns=["prediction"], index=predict_data.index
|
|
87
|
+
)
|
|
88
|
+
return predictions
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def submit(predictions, predict_output_path="predictions.csv", model_id=None):
|
|
92
|
+
logging.info("writing predictions to file and submitting")
|
|
93
|
+
include_index = predictions.index.name is not None
|
|
94
|
+
predictions.to_csv(predict_output_path, index=include_index)
|
|
95
|
+
print(predictions)
|
|
96
|
+
napi.upload_predictions(
|
|
97
|
+
predict_output_path, model_id=model_id, tournament=TOURNAMENT
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
trained_model = train(napi, MODEL_ID)
|
|
103
|
+
predictions = predict(napi, trained_model)
|
|
104
|
+
submit(predictions, model_id=MODEL_ID)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Provides us a working Python 3 environment.
|
|
2
|
+
FROM python:3.9
|
|
3
|
+
|
|
4
|
+
# These are docker arguments that `numerai node deploy/test` will always pass into docker.
|
|
5
|
+
# They are then set in your environment so that numerapi can access them when uploading submissions.
|
|
6
|
+
# You can also access them from your script like so:
|
|
7
|
+
# import os
|
|
8
|
+
# public_id = os.environ["NUMERAI_PUBLIC_ID"]
|
|
9
|
+
# secret_key = os.environ["NUMERAI_SECRET_KEY"]
|
|
10
|
+
ARG NUMERAI_PUBLIC_ID
|
|
11
|
+
ENV NUMERAI_PUBLIC_ID=$NUMERAI_PUBLIC_ID
|
|
12
|
+
|
|
13
|
+
ARG NUMERAI_SECRET_KEY
|
|
14
|
+
ENV NUMERAI_SECRET_KEY=$NUMERAI_SECRET_KEY
|
|
15
|
+
|
|
16
|
+
ARG MODEL_ID
|
|
17
|
+
ENV MODEL_ID=$MODEL_ID
|
|
18
|
+
|
|
19
|
+
ARG SRC_PATH
|
|
20
|
+
ENV SRC_PATH=$SRC_PATH
|
|
21
|
+
|
|
22
|
+
# We then add the requirements.txt file, and pip install every requirement from it.
|
|
23
|
+
# The `ADD [source] [destination]` command will take a file from the source directory on your computer
|
|
24
|
+
# and copy it over to the destination directory in the Docker container.
|
|
25
|
+
ADD $SRC_PATH/requirements.txt .
|
|
26
|
+
RUN pip install -r requirements.txt --no-cache-dir
|
|
27
|
+
|
|
28
|
+
# Now, add everything in the source code directory.
|
|
29
|
+
# (including your code, compiled files, serialized models, everything...)
|
|
30
|
+
ADD $SRC_PATH .
|
|
31
|
+
|
|
32
|
+
# This sets the default command to run your docker container.
|
|
33
|
+
# It runs by default in the cloud and when running `numerai node test`.
|
|
34
|
+
# This is overridden when using `numerai node test --command [COMMAND]`.
|
|
35
|
+
CMD [ "python", "./predict.py" ]
|
|
@@ -6,10 +6,11 @@ import logging
|
|
|
6
6
|
import joblib
|
|
7
7
|
import numerapi
|
|
8
8
|
import pandas as pd
|
|
9
|
-
|
|
9
|
+
import lightgbm as lgbm
|
|
10
10
|
|
|
11
11
|
logging.basicConfig(filename="log.txt", filemode="a")
|
|
12
12
|
|
|
13
|
+
TOURNAMENT = 8
|
|
13
14
|
DATA_VERSION = "v4.1"
|
|
14
15
|
ERA_COL = "era"
|
|
15
16
|
DATA_TYPE_COL = "data_type"
|
|
@@ -52,18 +53,24 @@ def train(napi, model_id, force_training=False):
|
|
|
52
53
|
model = joblib.load(model_name)
|
|
53
54
|
return model
|
|
54
55
|
|
|
55
|
-
model = LinearRegression()
|
|
56
|
-
|
|
57
56
|
logging.info("reading training data")
|
|
58
57
|
napi.download_dataset(f"{DATA_VERSION}/train.parquet")
|
|
59
58
|
train_data = pd.read_parquet(
|
|
60
59
|
f"{DATA_VERSION}/train.parquet", columns=get_features(napi)
|
|
61
60
|
)
|
|
62
61
|
|
|
62
|
+
# This will take a few minutes 🍵
|
|
63
63
|
logging.info("training model")
|
|
64
|
+
model = lgbm.LGBMRegressor(
|
|
65
|
+
n_estimators=2000,
|
|
66
|
+
learning_rate=0.01,
|
|
67
|
+
max_depth=5,
|
|
68
|
+
num_leaves=2**5 - 1,
|
|
69
|
+
colsample_bytree=0.1,
|
|
70
|
+
)
|
|
64
71
|
model.fit(
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
train_data.filter(like="feature_", axis="columns"),
|
|
73
|
+
train_data[TARGET_COL],
|
|
67
74
|
)
|
|
68
75
|
|
|
69
76
|
logging.info("saving model")
|
|
@@ -88,8 +95,11 @@ def predict(napi, model):
|
|
|
88
95
|
|
|
89
96
|
def submit(predictions, predict_output_path="predictions.csv", model_id=None):
|
|
90
97
|
logging.info("writing predictions to file and submitting")
|
|
91
|
-
predictions.
|
|
92
|
-
|
|
98
|
+
include_index = predictions.index.name is not None
|
|
99
|
+
predictions.to_csv(predict_output_path, index=include_index)
|
|
100
|
+
napi.upload_predictions(
|
|
101
|
+
predict_output_path, model_id=model_id, tournament=TOURNAMENT
|
|
102
|
+
)
|
|
93
103
|
|
|
94
104
|
|
|
95
105
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
""" An extra entry point specifically for training. Used when running locally """
|
|
2
|
+
|
|
3
|
+
import predict
|
|
4
|
+
|
|
5
|
+
train_data_path, predict_data_path, predict_output_path = predict.download_data()
|
|
6
|
+
|
|
7
|
+
model_id = predict.MODEL_ID
|
|
8
|
+
model_type = predict.MODEL
|
|
9
|
+
|
|
10
|
+
predict.train(train_data_path, model_id, model_type, force_training=True)
|