Source code for numerapi.cli

"""Access the numerai API via command line"""

import datetime
import decimal
import json

import click

import numerapi

DEFAULT_TOURNAMENT = 8


def _get_api(tournament: int):
    """
    Return the correct API implementation for a tournament.

    Classic (and any tournament other than Signals/Crypto) uses NumerAPI,
    Signals (11) uses SignalsAPI, and Crypto (12) uses CryptoAPI.
    """
    if tournament == 11:
        return numerapi.SignalsAPI()
    if tournament == 12:
        return numerapi.CryptoAPI()
    api = numerapi.NumerAPI()
    api.tournament_id = tournament
    return api


def _require_method(api, method_name: str, command_name: str):
    """Ensure the requested command is supported for the selected tournament."""
    if not hasattr(api, method_name):
        raise click.ClickException(
            f"The '{command_name}' command is not available for tournament "
            f"{api.tournament_id}."
        )
    return getattr(api, method_name)


[docs] def tournament_option(func): """Reusable Click option for selecting a tournament.""" return click.option( "--tournament", type=int, default=DEFAULT_TOURNAMENT, show_default=True, help="Tournament to target (8 classic, 11 signals, 12 crypto).", )(func)
[docs] class CommonJSONEncoder(json.JSONEncoder): """ Common JSON Encoder json.dumps(jsonString, cls=CommonJSONEncoder) """
[docs] def default(self, o): # Encode: Decimal if isinstance(o, decimal.Decimal): return str(o) # Encode: Date & Datetime if isinstance(o, (datetime.date, datetime.datetime)): return o.isoformat() return None
[docs] def prettify(stuff): """prettify json""" return json.dumps(stuff, cls=CommonJSONEncoder, indent=4)
@click.group() def cli(): """Wrapper around the Numerai API""" @cli.command() @click.option( "--round_num", type=int, help="round you are interested in. defaults to the current round", ) @tournament_option def list_datasets(round_num, tournament): """List of available data files""" api = _get_api(tournament) click.echo(prettify(api.list_datasets(round_num=round_num))) @cli.command() @click.option( "--round_num", type=int, help="round you are interested in. defaults to the current round", ) @click.option( "--filename", default="numerai_live_data.parquet", show_default=True, help="file to be downloaded", ) @click.option( "--dest_path", help="complete destination path, defaults to the name of the source file", ) @tournament_option def download_dataset( round_num, filename="numerai_live_data.parquet", dest_path=None, tournament=DEFAULT_TOURNAMENT, ): """Download specified file for the given round""" api = _get_api(tournament) click.echo( api.download_dataset( round_num=round_num, filename=filename, dest_path=dest_path ) ) @cli.command() @tournament_option def competitions(tournament=DEFAULT_TOURNAMENT): """Retrieves information about all competitions""" api = _get_api(tournament) method = _require_method(api, "get_competitions", "competitions") click.echo(prettify(method(tournament=tournament))) @cli.command() @tournament_option def current_round(tournament=DEFAULT_TOURNAMENT): """Get number of the current active round.""" api = _get_api(tournament) click.echo(api.get_current_round(tournament=tournament)) @cli.command() @click.option("--limit", default=20, help="Number of items to return, defaults to 20") @click.option("--offset", default=0, help="Number of items to skip, defaults to 0") @tournament_option def leaderboard(limit=20, offset=0, tournament=DEFAULT_TOURNAMENT): """Get the leaderboard.""" api = _get_api(tournament) method = _require_method(api, "get_leaderboard", "leaderboard") click.echo(prettify(method(limit=limit, offset=offset))) @cli.command() @click.option( "--round_num", type=int, default=None, help="filter by round number, defaults to None", ) @click.option( "--model_id", type=str, default=None, help="An account model UUID (required for accounts with multiple models", ) @tournament_option def submission_filenames(round_num, tournament, model_id): """Get filenames of your submissions""" api = _get_api(tournament) method = _require_method(api, "get_submission_filenames", "submission-filenames") click.echo( prettify(method(tournament=tournament, round_num=round_num, model_id=model_id)) ) @cli.command() @click.option("--hours", default=12, help="timeframe to consider, defaults to 12") @tournament_option def check_new_round(hours=12, tournament=DEFAULT_TOURNAMENT): """Check if a new round has started within the last `hours`.""" api = _get_api(tournament) click.echo(int(api.check_new_round(hours=hours))) @cli.command() @tournament_option def account(tournament=DEFAULT_TOURNAMENT): """Get all information about your account!""" api = _get_api(tournament) click.echo(prettify(api.get_account())) @cli.command() @tournament_option def models(tournament=DEFAULT_TOURNAMENT): """Get map of account models!""" api = _get_api(tournament) click.echo(prettify(api.get_models(tournament))) @cli.command() @click.argument("username") @tournament_option def profile(username, tournament=DEFAULT_TOURNAMENT): """Fetch the public profile of a user.""" api = _get_api(tournament) method = _require_method(api, "public_user_profile", "profile") click.echo(prettify(method(username))) @cli.command() @click.argument("username") @tournament_option def daily_model_performances(username, tournament=DEFAULT_TOURNAMENT): """Fetch daily performance of a model.""" api = _get_api(tournament) method = _require_method( api, "daily_model_performances", "daily-model-performances" ) click.echo(prettify(method(username))) @cli.command() @tournament_option def transactions(tournament=DEFAULT_TOURNAMENT): """List all your deposits and withdrawals.""" api = _get_api(tournament) click.echo(prettify(api.wallet_transactions())) @cli.command() @click.option( "--model_id", type=str, default=None, help="An account model UUID (required for accounts with multiple models", ) @click.argument("path", type=click.Path(exists=True)) @tournament_option def submit(path, model_id, tournament=DEFAULT_TOURNAMENT): """Upload predictions from file.""" api = _get_api(tournament) click.echo(api.upload_predictions(path, model_id=model_id)) @cli.command() @click.argument("username") @tournament_option def stake_get(username, tournament=DEFAULT_TOURNAMENT): """Get stake value of a user.""" api = _get_api(tournament) method = _require_method(api, "stake_get", "stake-get") click.echo(method(username)) @cli.command() @click.option( "--model_id", type=str, default=None, help="An account model UUID (required for accounts with multiple models", ) @tournament_option def stake_drain(model_id, tournament=DEFAULT_TOURNAMENT): """Completely remove your stake.""" api = _get_api(tournament) click.echo(api.stake_drain(model_id)) @cli.command() @click.argument("nmr") @click.option( "--model_id", type=str, default=None, help="An account model UUID (required for accounts with multiple models", ) @tournament_option def stake_decrease(nmr, model_id, tournament=DEFAULT_TOURNAMENT): """Decrease your stake by `value` NMR.""" api = _get_api(tournament) click.echo(api.stake_decrease(nmr, model_id)) @cli.command() @click.argument("nmr") @click.option( "--model_id", type=str, default=None, help="An account model UUID (required for accounts with multiple models", ) @tournament_option def stake_increase(nmr, model_id, tournament=DEFAULT_TOURNAMENT): """Increase your stake by `value` NMR.""" api = _get_api(tournament) click.echo(api.stake_increase(nmr, model_id)) @cli.command() def version(): """Installed numerapi version.""" print(numerapi.__version__) if __name__ == "__main__": cli()