feat(kevin): reconcile Alpaca bracket auto-closes + order status
Bracket stop-loss/take-profit legs fill at Alpaca without passing through the executor, so those closes (and their P&L) were invisible locally. - broker: add get_order(nested) + list_orders to BaseBroker/AlpacaBroker (+ SimulatedBroker); BrokerOrder carries child legs - Trade gains broker_order_id (migration f6a7b8c9d0e1); executor stamps the entry order id - new api_gateway trade-reconcile loop: books a closing SELL + realized P&L when a bracket leg fills (idempotent on the leg order id), syncs PENDING->terminal status, logs drift; runs alongside portfolio_sync [ci skip] Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
52b3c76482
commit
82dc622544
13 changed files with 1049 additions and 8 deletions
|
|
@ -123,6 +123,31 @@ class TestTrade:
|
|||
assert t.status == TradeStatus.FILLED
|
||||
assert t.pnl == 250.50
|
||||
|
||||
def test_trade_broker_order_id_optional(self) -> None:
|
||||
"""broker_order_id links a Trade back to its Alpaca order for
|
||||
reconciliation; it is nullable (entry trades set it, manual rows may
|
||||
not)."""
|
||||
t = Trade(
|
||||
id=uuid.uuid4(),
|
||||
ticker="NVDA",
|
||||
side=TradeSide.BUY,
|
||||
qty=10.0,
|
||||
price=100.0,
|
||||
status=TradeStatus.FILLED,
|
||||
broker_order_id="alpaca-ord-abc",
|
||||
)
|
||||
assert t.broker_order_id == "alpaca-ord-abc"
|
||||
|
||||
t2 = Trade(
|
||||
id=uuid.uuid4(),
|
||||
ticker="NVDA",
|
||||
side=TradeSide.BUY,
|
||||
qty=10.0,
|
||||
price=100.0,
|
||||
status=TradeStatus.FILLED,
|
||||
)
|
||||
assert t2.broker_order_id is None
|
||||
|
||||
|
||||
class TestPosition:
|
||||
def test_create_position(self) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue