Source code for tradeexecutor.cli.prepare_docker_env

"""Prepare a Docker .env file for a strategy.

To run this script standalone::

.. code-block:: shell

    source ~/secrets.env
    prepare-docker-env < env/pancake-eth-usd-sma.env > ~/pancake-eth-usd-sma-final.env

"""

import os
import sys
from typing import TextIO

from dotenv import dotenv_values
import typer

from tradeexecutor.cli.env import get_available_env_vars


#: List of minimal options needed to launch a live trading strategy
REQUIRED_LIVE_TRADING_ENV = [

    # Secret options needed
    "PRIVATE_KEY",
    "TRADING_STRATEGY_API_KEY",
    # "DISCORD_WEBHOOK_URL",
    ("JSON_RPC_BINANCE", "JSON_RPC_POLYGON", "JSON_RPC_ETHEREUM", "JSON_RPC_AVALANCHE"),

    # Public options required
    "NAME",
    "HTTP_ENABLED",
    "STRATEGY_FILE",
    "EXECUTION_TYPE"
]


[docs]def check_required_live_trading_vars(env, required_list): """Check that we have required""" # Show what we got in the case we need to debug env_output = ", ".join(env.keys()) for s in required_list: if type(s) == tuple: for s2 in s: if s2 in env.keys(): break else: assert False, f"You need to pass one of environment variable {s} for this script in .env file or as environment variable. Also remember to source the secrets file before running. Got: {env_output}" else: assert s in env, f"You need to pass environment variable {s} for this script in .env file or as environment variable. Also remember to source the secrets file before running. Got: {env_output}" # Docker cannot read newlines from .env # https://github.com/moby/moby/issues/12997 for key, value in env.items(): assert "\n" not in value, f"Detected new line in {key}"
[docs]def merge_secrets(public: dict, secret: dict) -> dict: return secret | public
[docs]def filter_config(config: dict) -> dict: """ Because we are reading full host OS environment, we get a lot of env vars we do not want to pass. """ vars = get_available_env_vars() names = [v.name for v in vars] return {k: v for k, v in config.items() if k in names}
[docs]def write_config(out: TextIO, config): print("# This is automatically generated by prepare-docker-env command - do not hand edit", file=out) print("#", file=out) for k, v in config.items(): print(f'{k}={v}', file=out) print("", file=out)
[docs]def app(): """Creates .env file for a trade-executor Docker container. - Reads public .env configuration variables from a given file - Reads secret environment variables from passed environment variables - Validates we have required variables to run a live strategy - Writes the output to a single .env file that can be read by Docker - Operates on environment variables, stdin and stdout streams Usage: # Load your non-committed secrets from the server root source ~/my-secrets.env # Combine the env configuration file with the secrets prepare-docker-env env/my-config.env ~/docker-final.env Never publish or commit the final generated environment file, as it contains security critical variables. Leaking these allow stealing of your assets. """ strategy_config = dotenv_values(stream=sys.stdin) secret_config = {k: v for k, v in os.environ.items()} merged_config = merge_secrets(secret_config, strategy_config) filtered_config = filter_config(merged_config) check_required_live_trading_vars(filtered_config, REQUIRED_LIVE_TRADING_ENV) write_config(sys.stdout, filtered_config) print(f"Environment variables prepared for Docker .env:", file=sys.stderr) for k in filtered_config.keys(): print(f" {k}", file=sys.stderr)
[docs]def main(): typer.run(app)