summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 16:52:03 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 16:52:03 +0900
commite971d19678a7ce94666e5887909823cdd2a6cab2 (patch)
tree9f7baf1b72e52cbe003251dc68e8201169cf03ce
parentac6d98b7790506128cb3f65dfdbb2d9d9ddce555 (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.yml28
-rw-r--r--services/strategy-engine/pyproject.toml1
-rw-r--r--shared/alembic/versions/001_initial_schema.py96
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")