From 4747400168279c6cfc1196d86ec77b5d7b513c61 Mon Sep 17 00:00:00 2001 From: TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:05:19 +0900 Subject: 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 --- shared/src/shared/db.py | 3 +++ shared/src/shared/events.py | 16 ++++++++++++++-- shared/src/shared/sa_models.py | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'shared/src') 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" -- cgit v1.2.3