summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 18:36:18 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 18:36:18 +0900
commitb23aef3a9947d4d3d8e87b595ecf547159df7289 (patch)
treec5c99869d300c4a1ace118a2dc03c5dfa3218499
parent5cee0686e421b1f21484c23e413692616e9e2ffa (diff)
feat(shared): add conviction, stop_loss, take_profit to Signal model
-rw-r--r--services/backtester/src/backtester/engine.py7
-rw-r--r--shared/src/shared/models.py3
-rw-r--r--shared/tests/test_models.py29
3 files changed, 38 insertions, 1 deletions
diff --git a/services/backtester/src/backtester/engine.py b/services/backtester/src/backtester/engine.py
index 8854b17..b03715d 100644
--- a/services/backtester/src/backtester/engine.py
+++ b/services/backtester/src/backtester/engine.py
@@ -90,7 +90,12 @@ class BacktestEngine:
signal = self._strategy.on_candle(candle)
if signal is not None:
- simulator.execute(signal, timestamp=candle.open_time)
+ simulator.execute(
+ signal,
+ timestamp=candle.open_time,
+ stop_loss=signal.stop_loss,
+ take_profit=signal.take_profit,
+ )
# Calculate final balance including open positions valued at last candle close
final_balance = simulator.balance
diff --git a/shared/src/shared/models.py b/shared/src/shared/models.py
index 0e8ca44..70820b5 100644
--- a/shared/src/shared/models.py
+++ b/shared/src/shared/models.py
@@ -45,6 +45,9 @@ class Signal(BaseModel):
price: Decimal
quantity: Decimal
reason: str
+ conviction: float = 1.0 # 0.0 to 1.0, signal strength/confidence
+ stop_loss: Optional[Decimal] = None # Price to exit at loss
+ take_profit: Optional[Decimal] = None # Price to exit at profit
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
diff --git a/shared/tests/test_models.py b/shared/tests/test_models.py
index 25ab4c9..b23d71d 100644
--- a/shared/tests/test_models.py
+++ b/shared/tests/test_models.py
@@ -94,6 +94,35 @@ def test_order_creation():
assert order.created_at is not None
+def test_signal_conviction_default():
+ """Test Signal defaults for conviction, stop_loss, take_profit."""
+ from shared.models import Signal, OrderSide
+
+ signal = Signal(
+ strategy="rsi", symbol="BTCUSDT", side=OrderSide.BUY,
+ price=Decimal("50000"), quantity=Decimal("0.01"), reason="test",
+ )
+ assert signal.conviction == 1.0
+ assert signal.stop_loss is None
+ assert signal.take_profit is None
+
+
+def test_signal_with_stops():
+ """Test Signal with explicit conviction, stop_loss, take_profit."""
+ from shared.models import Signal, OrderSide
+
+ signal = Signal(
+ strategy="rsi", symbol="BTCUSDT", side=OrderSide.BUY,
+ price=Decimal("50000"), quantity=Decimal("0.01"), reason="test",
+ conviction=0.8,
+ stop_loss=Decimal("48000"),
+ take_profit=Decimal("55000"),
+ )
+ assert signal.conviction == 0.8
+ assert signal.stop_loss == Decimal("48000")
+ assert signal.take_profit == Decimal("55000")
+
+
def test_position_unrealized_pnl():
"""Test Position unrealized_pnl computed property."""
from shared.models import Position