diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 16:52:03 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 16:52:03 +0900 |
| commit | e971d19678a7ce94666e5887909823cdd2a6cab2 (patch) | |
| tree | 9f7baf1b72e52cbe003251dc68e8201169cf03ce | |
| parent | ac6d98b7790506128cb3f65dfdbb2d9d9ddce555 (diff) | |
fix: resolve critical deployment blockers
- Add Alembic initial migration (6 tables: candles, signals, orders,
trades, positions, portfolio_snapshots)
- Expose health ports (8080-8083) in docker-compose with healthchecks
- Add numpy dependency to strategy-engine pyproject.toml
| -rw-r--r-- | docker-compose.yml | 28 | ||||
| -rw-r--r-- | services/strategy-engine/pyproject.toml | 1 | ||||
| -rw-r--r-- | shared/alembic/versions/001_initial_schema.py | 96 |
3 files changed, 125 insertions, 0 deletions
diff --git a/docker-compose.yml b/docker-compose.yml index 95a5c63..473e2bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,11 +32,18 @@ services: context: . dockerfile: services/data-collector/Dockerfile env_file: .env + ports: + - "8080:8080" depends_on: redis: condition: service_healthy postgres: condition: service_healthy + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"] + interval: 10s + timeout: 5s + retries: 3 restart: unless-stopped strategy-engine: @@ -44,11 +51,18 @@ services: context: . dockerfile: services/strategy-engine/Dockerfile env_file: .env + ports: + - "8081:8081" depends_on: redis: condition: service_healthy postgres: condition: service_healthy + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8081/health')"] + interval: 10s + timeout: 5s + retries: 3 restart: unless-stopped order-executor: @@ -56,11 +70,18 @@ services: context: . dockerfile: services/order-executor/Dockerfile env_file: .env + ports: + - "8082:8082" depends_on: redis: condition: service_healthy postgres: condition: service_healthy + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8082/health')"] + interval: 10s + timeout: 5s + retries: 3 restart: unless-stopped portfolio-manager: @@ -68,11 +89,18 @@ services: context: . dockerfile: services/portfolio-manager/Dockerfile env_file: .env + ports: + - "8083:8083" depends_on: redis: condition: service_healthy postgres: condition: service_healthy + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8083/health')"] + interval: 10s + timeout: 5s + retries: 3 restart: unless-stopped prometheus: diff --git a/services/strategy-engine/pyproject.toml b/services/strategy-engine/pyproject.toml index a86b282..4f5b6be 100644 --- a/services/strategy-engine/pyproject.toml +++ b/services/strategy-engine/pyproject.toml @@ -5,6 +5,7 @@ description = "Plugin-based strategy execution engine" requires-python = ">=3.12" dependencies = [ "pandas>=2.0", + "numpy>=1.20", "trading-shared", ] diff --git a/shared/alembic/versions/001_initial_schema.py b/shared/alembic/versions/001_initial_schema.py new file mode 100644 index 0000000..2bdaafc --- /dev/null +++ b/shared/alembic/versions/001_initial_schema.py @@ -0,0 +1,96 @@ +"""Initial schema + +Revision ID: 001 +Revises: +Create Date: 2026-04-01 +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "001" +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "candles", + sa.Column("symbol", sa.Text, primary_key=True), + sa.Column("timeframe", sa.Text, primary_key=True), + sa.Column("open_time", sa.DateTime(timezone=True), primary_key=True), + sa.Column("open", sa.Numeric, nullable=False), + sa.Column("high", sa.Numeric, nullable=False), + sa.Column("low", sa.Numeric, nullable=False), + sa.Column("close", sa.Numeric, nullable=False), + sa.Column("volume", sa.Numeric, nullable=False), + ) + + op.create_table( + "signals", + sa.Column("id", sa.Text, primary_key=True), + sa.Column("strategy", sa.Text, nullable=False), + sa.Column("symbol", sa.Text, nullable=False), + sa.Column("side", sa.Text, nullable=False), + sa.Column("price", sa.Numeric, nullable=False), + sa.Column("quantity", sa.Numeric, nullable=False), + sa.Column("reason", sa.Text), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + ) + + op.create_table( + "orders", + sa.Column("id", sa.Text, primary_key=True), + sa.Column("signal_id", sa.Text, sa.ForeignKey("signals.id")), + sa.Column("symbol", sa.Text, nullable=False), + sa.Column("side", sa.Text, nullable=False), + sa.Column("type", sa.Text, nullable=False), + sa.Column("price", sa.Numeric, nullable=False), + sa.Column("quantity", sa.Numeric, nullable=False), + sa.Column("status", sa.Text, nullable=False, server_default="PENDING"), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("filled_at", sa.DateTime(timezone=True)), + ) + + op.create_table( + "trades", + sa.Column("id", sa.Text, primary_key=True), + sa.Column("order_id", sa.Text, sa.ForeignKey("orders.id")), + sa.Column("symbol", sa.Text, nullable=False), + sa.Column("side", sa.Text, nullable=False), + sa.Column("price", sa.Numeric, nullable=False), + sa.Column("quantity", sa.Numeric, nullable=False), + sa.Column("fee", sa.Numeric, nullable=False, server_default="0"), + sa.Column("traded_at", sa.DateTime(timezone=True), nullable=False), + ) + + op.create_table( + "positions", + sa.Column("symbol", sa.Text, primary_key=True), + sa.Column("quantity", sa.Numeric, nullable=False), + sa.Column("avg_entry_price", sa.Numeric, nullable=False), + sa.Column("current_price", sa.Numeric, nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), + ) + + op.create_table( + "portfolio_snapshots", + sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), + sa.Column("total_value", sa.Numeric, nullable=False), + sa.Column("realized_pnl", sa.Numeric, nullable=False), + sa.Column("unrealized_pnl", sa.Numeric, nullable=False), + sa.Column("snapshot_at", sa.DateTime(timezone=True), nullable=False), + ) + + +def downgrade() -> None: + op.drop_table("portfolio_snapshots") + op.drop_table("positions") + op.drop_table("trades") + op.drop_table("orders") + op.drop_table("signals") + op.drop_table("candles") |
