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
|
|
@ -46,20 +46,26 @@ def create_app(config: ApiGatewayConfig | None = None) -> FastAPI:
|
|||
|
||||
# Start portfolio sync background task
|
||||
from services.api_gateway.tasks.portfolio_sync import portfolio_sync_loop
|
||||
from services.api_gateway.tasks.trade_reconcile import trade_reconcile_loop
|
||||
|
||||
sync_task = asyncio.create_task(
|
||||
portfolio_sync_loop(config, session_factory)
|
||||
)
|
||||
reconcile_task = asyncio.create_task(
|
||||
trade_reconcile_loop(config, session_factory)
|
||||
)
|
||||
|
||||
logger.info("API Gateway started")
|
||||
yield
|
||||
|
||||
# Cancel the sync task
|
||||
# Cancel the background tasks
|
||||
sync_task.cancel()
|
||||
try:
|
||||
await sync_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
reconcile_task.cancel()
|
||||
for task in (sync_task, reconcile_task):
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Cleanup
|
||||
await app.state.redis.aclose()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue