Coverage for src/finbot/execution/broker.py: 100%
34 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
1import math
2from datetime import datetime, UTC
4from finbot.execution.fill import Fill
5from finbot.execution.order import Order, OrderSide
6from finbot.portfolio.portfolio import Portfolio
9class SimulatedBroker:
10 """A simulated financy broker. TBD: Replace with real brokers"""
12 def __init__(self, portfolio: Portfolio, fee_rate: float = 0.0, slippage_rate: float = 0.0):
13 if not math.isfinite(fee_rate):
14 raise ValueError("Fee rate must be a valid number.")
15 if not math.isfinite(slippage_rate):
16 raise ValueError("Slippage rate must be a valid number.")
18 if fee_rate < 0:
19 raise ValueError("Fee rate cannot be negative.")
20 if not 0 <= slippage_rate <= 1.0:
21 raise ValueError("Slippage rate must be between 0.0 and 1.0.")
23 self.portfolio = portfolio
24 self.fee_rate = fee_rate
25 self.slippage_rate = slippage_rate
27 def execute(
28 self,
29 order: Order,
30 market_price: float,
31 timestamp: datetime | None = None,
32 ) -> Fill:
33 if not math.isfinite(market_price):
34 raise ValueError("Market price must be a valid number.")
35 if market_price <= 0:
36 raise ValueError("Market price must be greater than zero.")
38 if timestamp is None: timestamp = datetime.now(UTC)
40 if order.side == OrderSide.BUY:
41 price = market_price * (1 + self.slippage_rate)
42 elif order.side == OrderSide.SELL:
43 price = market_price * (1 - self.slippage_rate)
44 else:
45 raise ValueError(f"Unsupported order side: {order.side}")
47 slippage_cost = abs(price - market_price) * order.quantity
48 fee = price * order.quantity * self.fee_rate
50 fill = Fill(
51 symbol=order.symbol,
52 side=order.side,
53 quantity=order.quantity,
54 price=price,
55 timestamp=timestamp,
56 fee=fee,
57 slippage_cost=slippage_cost,
58 )
60 self.portfolio.apply_fill(fill)
62 return fill