From 33b14aaa2344b0fd95d1629627c3d135b24ae102 Mon Sep 17 00:00:00 2001 From: TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:56:35 +0900 Subject: feat: initial trading platform implementation Binance spot crypto trading platform with microservices architecture: - shared: Pydantic models, Redis Streams broker, asyncpg DB layer - data-collector: Binance WebSocket/REST market data collection - strategy-engine: Plugin-based strategy execution (RSI, Grid) - order-executor: Order execution with risk management - portfolio-manager: Position tracking and PnL calculation - backtester: Historical strategy testing with simulator - cli: Click-based CLI for all operations - Docker Compose orchestration with Redis and PostgreSQL - 24 test files covering all modules --- cli/pyproject.toml | 19 +++++++++++++++++ cli/src/trading_cli/__init__.py | 0 cli/src/trading_cli/commands/__init__.py | 0 cli/src/trading_cli/commands/backtest.py | 26 +++++++++++++++++++++++ cli/src/trading_cli/commands/data.py | 31 +++++++++++++++++++++++++++ cli/src/trading_cli/commands/portfolio.py | 20 ++++++++++++++++++ cli/src/trading_cli/commands/service.py | 29 +++++++++++++++++++++++++ cli/src/trading_cli/commands/strategy.py | 20 ++++++++++++++++++ cli/src/trading_cli/commands/trade.py | 35 +++++++++++++++++++++++++++++++ cli/src/trading_cli/main.py | 22 +++++++++++++++++++ cli/tests/__init__.py | 0 cli/tests/test_cli_data.py | 17 +++++++++++++++ cli/tests/test_cli_trade.py | 10 +++++++++ 13 files changed, 229 insertions(+) create mode 100644 cli/pyproject.toml create mode 100644 cli/src/trading_cli/__init__.py create mode 100644 cli/src/trading_cli/commands/__init__.py create mode 100644 cli/src/trading_cli/commands/backtest.py create mode 100644 cli/src/trading_cli/commands/data.py create mode 100644 cli/src/trading_cli/commands/portfolio.py create mode 100644 cli/src/trading_cli/commands/service.py create mode 100644 cli/src/trading_cli/commands/strategy.py create mode 100644 cli/src/trading_cli/commands/trade.py create mode 100644 cli/src/trading_cli/main.py create mode 100644 cli/tests/__init__.py create mode 100644 cli/tests/test_cli_data.py create mode 100644 cli/tests/test_cli_trade.py (limited to 'cli') diff --git a/cli/pyproject.toml b/cli/pyproject.toml new file mode 100644 index 0000000..e208021 --- /dev/null +++ b/cli/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "trading-cli" +version = "0.1.0" +description = "CLI interface for the trading platform" +requires-python = ">=3.12" +dependencies = ["click>=8.0", "rich>=13.0", "trading-shared"] + +[project.scripts] +trading = "trading_cli.main:cli" + +[project.optional-dependencies] +dev = ["pytest>=8.0", "pytest-asyncio>=0.23"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/trading_cli"] diff --git a/cli/src/trading_cli/__init__.py b/cli/src/trading_cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/src/trading_cli/commands/__init__.py b/cli/src/trading_cli/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/src/trading_cli/commands/backtest.py b/cli/src/trading_cli/commands/backtest.py new file mode 100644 index 0000000..40617b6 --- /dev/null +++ b/cli/src/trading_cli/commands/backtest.py @@ -0,0 +1,26 @@ +import click + + +@click.group() +def backtest(): + """Backtesting commands.""" + pass + + +@backtest.command() +@click.option("--strategy", required=True, help="Strategy name to backtest") +@click.option("--symbol", required=True, help="Trading symbol (e.g. BTCUSDT)") +@click.option("--from", "from_date", required=True, help="Start date (ISO format)") +@click.option("--to", "to_date", default=None, help="End date (ISO format, defaults to now)") +@click.option("--balance", default=10000.0, show_default=True, help="Initial balance in USDT") +def run(strategy, symbol, from_date, to_date, balance): + """Run a backtest for a strategy.""" + to_label = to_date or "now" + click.echo(f"Running backtest: strategy={strategy}, symbol={symbol}, {from_date} → {to_label}, balance={balance}...") + + +@backtest.command() +@click.option("--id", "backtest_id", required=True, help="Backtest run ID") +def report(backtest_id): + """Show a backtest report by ID.""" + click.echo(f"Showing backtest report for ID: {backtest_id}...") diff --git a/cli/src/trading_cli/commands/data.py b/cli/src/trading_cli/commands/data.py new file mode 100644 index 0000000..1fa5e30 --- /dev/null +++ b/cli/src/trading_cli/commands/data.py @@ -0,0 +1,31 @@ +import click + + +@click.group() +def data(): + """Data collection and management commands.""" + pass + + +@data.command() +@click.option("--symbol", required=True, help="Trading symbol (e.g. BTCUSDT)") +@click.option("--timeframe", default="1m", show_default=True, help="Candle timeframe") +def collect(symbol, timeframe): + """Start collecting live market data for a symbol.""" + click.echo(f"Starting data collection for {symbol} at {timeframe} timeframe...") + + +@data.command() +@click.option("--symbol", required=True, help="Trading symbol (e.g. BTCUSDT)") +@click.option("--timeframe", default="1m", show_default=True, help="Candle timeframe") +@click.option("--from", "since", default=None, help="Start date (ISO format)") +@click.option("--limit", default=1000, show_default=True, help="Number of candles to fetch") +def history(symbol, timeframe, since, limit): + """Download historical market data for a symbol.""" + click.echo(f"Downloading {limit} {timeframe} candles for {symbol}" + (f" since {since}" if since else "") + "...") + + +@data.command("list") +def list_(): + """List available data streams and symbols.""" + click.echo("Fetching available data streams and collected symbols...") diff --git a/cli/src/trading_cli/commands/portfolio.py b/cli/src/trading_cli/commands/portfolio.py new file mode 100644 index 0000000..9389bac --- /dev/null +++ b/cli/src/trading_cli/commands/portfolio.py @@ -0,0 +1,20 @@ +import click + + +@click.group() +def portfolio(): + """Portfolio management commands.""" + pass + + +@portfolio.command() +def show(): + """Show the current portfolio holdings and balances.""" + click.echo("Fetching current portfolio...") + + +@portfolio.command() +@click.option("--days", default=30, show_default=True, help="Number of days of history") +def history(days): + """Show PnL history for the portfolio.""" + click.echo(f"Fetching PnL history for the last {days} days...") diff --git a/cli/src/trading_cli/commands/service.py b/cli/src/trading_cli/commands/service.py new file mode 100644 index 0000000..d01eaae --- /dev/null +++ b/cli/src/trading_cli/commands/service.py @@ -0,0 +1,29 @@ +import subprocess +import click + + +@click.group() +def service(): + """Docker service management commands.""" + pass + + +@service.command() +def up(): + """Start all services with docker compose.""" + click.echo("Starting all services...") + subprocess.run(["docker", "compose", "up", "-d"], check=True) + + +@service.command() +def down(): + """Stop all services with docker compose.""" + click.echo("Stopping all services...") + subprocess.run(["docker", "compose", "down"], check=True) + + +@service.command() +@click.option("--name", required=True, help="Service name to follow logs for") +def logs(name): + """Follow logs for a specific service.""" + subprocess.run(["docker", "compose", "logs", "-f", name], check=True) diff --git a/cli/src/trading_cli/commands/strategy.py b/cli/src/trading_cli/commands/strategy.py new file mode 100644 index 0000000..68ffeee --- /dev/null +++ b/cli/src/trading_cli/commands/strategy.py @@ -0,0 +1,20 @@ +import click + + +@click.group() +def strategy(): + """Strategy management commands.""" + pass + + +@strategy.command("list") +def list_(): + """List all available trading strategies.""" + click.echo("Fetching available strategies...") + + +@strategy.command() +@click.option("--name", required=True, help="Strategy name") +def info(name): + """Show detailed information about a strategy.""" + click.echo(f"Fetching details for strategy: {name}...") diff --git a/cli/src/trading_cli/commands/trade.py b/cli/src/trading_cli/commands/trade.py new file mode 100644 index 0000000..f90e0ed --- /dev/null +++ b/cli/src/trading_cli/commands/trade.py @@ -0,0 +1,35 @@ +import click + + +@click.group() +def trade(): + """Trading bot management commands.""" + pass + + +@trade.command() +@click.option("--strategy", required=True, help="Strategy name to run") +@click.option("--symbol", required=True, help="Trading symbol (e.g. BTCUSDT)") +def start(strategy, symbol): + """Start a trading bot for a strategy and symbol.""" + click.echo(f"Starting trading bot: strategy={strategy}, symbol={symbol}...") + + +@trade.command() +@click.option("--strategy", required=True, help="Strategy name to stop") +def stop(strategy): + """Stop a running trading bot.""" + click.echo(f"Stopping trading bot for strategy: {strategy}...") + + +@trade.command() +def status(): + """Show status of all running trading bots.""" + click.echo("Fetching running bots status...") + + +@trade.command("stop-all") +def stop_all(): + """Stop all running trading bots.""" + click.confirm("Are you sure you want to stop all running bots?", abort=True) + click.echo("Stopping all running trading bots...") diff --git a/cli/src/trading_cli/main.py b/cli/src/trading_cli/main.py new file mode 100644 index 0000000..db3c282 --- /dev/null +++ b/cli/src/trading_cli/main.py @@ -0,0 +1,22 @@ +import click +from trading_cli.commands.data import data +from trading_cli.commands.trade import trade +from trading_cli.commands.backtest import backtest +from trading_cli.commands.portfolio import portfolio +from trading_cli.commands.strategy import strategy +from trading_cli.commands.service import service + + +@click.group() +@click.version_option(version="0.1.0") +def cli(): + """Trading Platform CLI — Binance spot crypto trading""" + pass + + +cli.add_command(data) +cli.add_command(trade) +cli.add_command(backtest) +cli.add_command(portfolio) +cli.add_command(strategy) +cli.add_command(service) diff --git a/cli/tests/__init__.py b/cli/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/tests/test_cli_data.py b/cli/tests/test_cli_data.py new file mode 100644 index 0000000..2cc2149 --- /dev/null +++ b/cli/tests/test_cli_data.py @@ -0,0 +1,17 @@ +from click.testing import CliRunner +from trading_cli.main import cli + + +def test_cli_help(): + runner = CliRunner() + result = runner.invoke(cli, ["--help"]) + assert result.exit_code == 0 + assert "Usage" in result.output + + +def test_cli_data_group(): + runner = CliRunner() + result = runner.invoke(cli, ["data", "--help"]) + assert result.exit_code == 0 + assert "collect" in result.output + assert "history" in result.output diff --git a/cli/tests/test_cli_trade.py b/cli/tests/test_cli_trade.py new file mode 100644 index 0000000..d3f3079 --- /dev/null +++ b/cli/tests/test_cli_trade.py @@ -0,0 +1,10 @@ +from click.testing import CliRunner +from trading_cli.main import cli + + +def test_cli_trade_group(): + runner = CliRunner() + result = runner.invoke(cli, ["trade", "--help"]) + assert result.exit_code == 0 + assert "start" in result.output + assert "stop" in result.output -- cgit v1.2.3