diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 16:05:19 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 16:05:19 +0900 |
| commit | 4747400168279c6cfc1196d86ec77b5d7b513c61 (patch) | |
| tree | ff92fd87e292c39900a6fee187fbd206a22d618a | |
| parent | c0496919e91f110aeed7bc47b24ebc3b8348ee81 (diff) | |
fix: add TradeRow ORM model, SignalRow missing columns, guard Event.from_dict
- Add TradeRow ORM model matching existing trades migration table
- Add conviction, stop_loss, take_profit columns to SignalRow + migration 004
- Persist conviction/stop_loss/take_profit in insert_signal()
- Guard Event.from_dict against malformed data with ValueError instead of KeyError
| -rw-r--r-- | shared/alembic/versions/004_add_signal_detail_columns.py | 23 | ||||
| -rw-r--r-- | shared/src/shared/db.py | 3 | ||||
| -rw-r--r-- | shared/src/shared/events.py | 16 | ||||
| -rw-r--r-- | shared/src/shared/sa_models.py | 16 | ||||
| -rw-r--r-- | shared/tests/test_sa_models.py | 4 |
5 files changed, 60 insertions, 2 deletions
diff --git a/shared/alembic/versions/004_add_signal_detail_columns.py b/shared/alembic/versions/004_add_signal_detail_columns.py new file mode 100644 index 0000000..7a8a77b --- /dev/null +++ b/shared/alembic/versions/004_add_signal_detail_columns.py @@ -0,0 +1,23 @@ +"""Add conviction, stop_loss, take_profit columns to signals table. + +Revision ID: 004 +Revises: 003 +""" + +import sqlalchemy as sa +from alembic import op + +revision = "004" +down_revision = "003" + + +def upgrade(): + op.add_column("signals", sa.Column("conviction", sa.Float, nullable=False, server_default="1.0")) + op.add_column("signals", sa.Column("stop_loss", sa.Numeric, nullable=True)) + op.add_column("signals", sa.Column("take_profit", sa.Numeric, nullable=True)) + + +def downgrade(): + op.drop_column("signals", "take_profit") + op.drop_column("signals", "stop_loss") + op.drop_column("signals", "conviction") diff --git a/shared/src/shared/db.py b/shared/src/shared/db.py index a718951..8fee000 100644 --- a/shared/src/shared/db.py +++ b/shared/src/shared/db.py @@ -112,6 +112,9 @@ class Database: price=signal.price, quantity=signal.quantity, reason=signal.reason, + conviction=signal.conviction, + stop_loss=signal.stop_loss, + take_profit=signal.take_profit, created_at=signal.created_at, ) async with self._session_factory() as session: diff --git a/shared/src/shared/events.py b/shared/src/shared/events.py index 6f8def1..61b85bd 100644 --- a/shared/src/shared/events.py +++ b/shared/src/shared/events.py @@ -88,6 +88,18 @@ class Event: @staticmethod def from_dict(data: dict) -> Any: - event_type = EventType(data["type"]) + """Deserialize a raw dict into the appropriate event type. + + Raises ValueError for malformed or unrecognized event data. + """ + try: + event_type = EventType(data["type"]) + except (KeyError, ValueError) as exc: + raise ValueError(f"Invalid or missing event type in data: {data!r}") from exc cls = _EVENT_TYPE_MAP[event_type] - return cls.from_raw(data) + try: + return cls.from_raw(data) + except KeyError as exc: + raise ValueError( + f"Missing required field in {event_type} event data: {exc}" + ) from exc diff --git a/shared/src/shared/sa_models.py b/shared/src/shared/sa_models.py index dc87ef5..b70a6c4 100644 --- a/shared/src/shared/sa_models.py +++ b/shared/src/shared/sa_models.py @@ -35,6 +35,9 @@ class SignalRow(Base): price: Mapped[Decimal] = mapped_column(Numeric, nullable=False) quantity: Mapped[Decimal] = mapped_column(Numeric, nullable=False) reason: Mapped[str | None] = mapped_column(Text) + conviction: Mapped[float] = mapped_column(sa.Float, nullable=False, server_default="1.0") + stop_loss: Mapped[Decimal | None] = mapped_column(Numeric) + take_profit: Mapped[Decimal | None] = mapped_column(Numeric) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) @@ -53,6 +56,19 @@ class OrderRow(Base): filled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) +class TradeRow(Base): + __tablename__ = "trades" + + id: Mapped[str] = mapped_column(Text, primary_key=True) + order_id: Mapped[str | None] = mapped_column(Text, ForeignKey("orders.id")) + symbol: Mapped[str] = mapped_column(Text, nullable=False) + side: Mapped[str] = mapped_column(Text, nullable=False) + price: Mapped[Decimal] = mapped_column(Numeric, nullable=False) + quantity: Mapped[Decimal] = mapped_column(Numeric, nullable=False) + fee: Mapped[Decimal] = mapped_column(Numeric, nullable=False, server_default="0") + traded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) + + class PositionRow(Base): __tablename__ = "positions" diff --git a/shared/tests/test_sa_models.py b/shared/tests/test_sa_models.py index ae73833..c9311dd 100644 --- a/shared/tests/test_sa_models.py +++ b/shared/tests/test_sa_models.py @@ -11,6 +11,7 @@ def test_base_metadata_has_all_tables(): "candles", "signals", "orders", + "trades", "positions", "portfolio_snapshots", "news_items", @@ -71,6 +72,9 @@ class TestSignalRow: "price", "quantity", "reason", + "conviction", + "stop_loss", + "take_profit", "created_at", } assert expected == cols |
