Source code for tradeexecutor.cli.commands.webapi

"""Web API command

Example::

    poetry run trade-executor webapi --strategy-file=strategies/enzyme-polygon-matic-usdc.py
"""

import datetime
import faulthandler
import logging
import os
import time
from decimal import Decimal
from pathlib import Path
from queue import Queue
from typing import Optional

import typer
import waitress

from . import shared_options
from .app import app
from ..bootstrap import prepare_executor_id, prepare_cache, create_metadata, create_state_store
from ..log import setup_logging, setup_discord_logging, setup_logstash_logging, setup_file_logging
from ..version_info import VersionInfo
from ...state.state import State
from ...strategy.execution_model import AssetManagementMode
from ...strategy.run_state import RunState
from ...strategy.strategy_module import read_strategy_module, StrategyModuleInformation
from ...webhook.server import create_pyramid_app


logger = logging.getLogger(__name__)


[docs]@app.command() def webapi( # Strategy assets id: str = shared_options.id, name: Optional[str] = shared_options.name, short_description: Optional[str] = typer.Option(None, envvar="SHORT_DESCRIPTION", help="Short description for metadata"), long_description: Optional[str] = typer.Option(None, envvar="LONG_DESCRIPTION", help="Long description for metadata"), badges: Optional[str] = typer.Option(None, envvar="BADGES", help="Comma separated list of badges to be displayed on the strategy tile"), icon_url: Optional[str] = typer.Option(None, envvar="ICON_URL", help="Strategy icon for web rendering and Discord avatar"), strategy_file: Path = shared_options.strategy_file, # Webhook server options http_port: int = typer.Option(3456, envvar="HTTP_PORT", help="Which HTTP port to listen. The default is 3456, the default port of Pyramid web server."), http_host: str = typer.Option("0.0.0.0", envvar="HTTP_HOST", help="The IP address to bind for the web server. By default listen to all IP addresses available in the run-time environment."), http_username: str = typer.Option(None, envvar="HTTP_USERNAME", help="Username for HTTP Basic Auth protection of webhooks"), http_password: str = typer.Option(None, envvar="HTTP_PASSWORD", help="Password for HTTP Basic Auth protection of webhooks"), # Logging file_log_level: Optional[str] = typer.Option("info", envvar="FILE_LOG_LEVEL", help="Log file log level. The default log file is logs/id.log."), # Logging log_level: str = shared_options.log_level, # Various file configurations state_file: Optional[Path] = shared_options.state_file, cache_path: Optional[Path] = shared_options.cache_path, ): """Launch Trade Executor instance.""" global logger started_at = datetime.datetime.utcnow() # Guess id from the strategy file id = prepare_executor_id(id, strategy_file) # We always need a name-*- if not name: if strategy_file: name = os.path.basename(strategy_file) else: name = "Unnamed backtest" if not log_level: log_level = logging.INFO # Make sure unit tests run logs do not get polluted # Don't touch any log levels, but # make sure we have logger.trading() available when # log_level is "disabled" logger = setup_logging(log_level, in_memory_buffer=True) setup_file_logging( f"logs/{id}.log", file_log_level, http_logging=True, ) if not state_file: state_file = f"state/{id}.json" cache_path = prepare_cache(id, cache_path, False) mod = read_strategy_module(strategy_file) if state_file: store = create_state_store(Path(state_file)) fees = dict( management_fee=mod.management_fee, trading_strategy_protocol_fee=mod.trading_strategy_protocol_fee, strategy_developer_fee=mod.strategy_developer_fee, ) metadata = create_metadata( name, short_description, long_description, icon_url, AssetManagementMode.dummy, chain_id=mod.get_default_chain_id(), vault=None, fees=fees, ) # Start the queue that relays info from the web server to the strategy executor command_queue = Queue() run_state = RunState() run_state.version = VersionInfo.read_docker_version() run_state.executor_id = id # Set up read-only state sync if not store.is_pristine(): run_state.read_only_state_copy = store.load() app = create_pyramid_app( http_username, http_password, command_queue, store, metadata, run_state, ) waitress.serve(app, host=http_host, port=http_port)