Add READ_ONLY_API environment variable and enhance trading error handling

This commit is contained in:
coddard
2026-05-19 02:41:00 +03:00
parent 32537ef6e2
commit 22ac0da394
5 changed files with 114 additions and 2 deletions
+44 -1
View File
@@ -4,7 +4,7 @@ import os
from datetime import datetime, timezone
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from ib_async import LimitOrder, MarketOrder, Stock
@@ -34,6 +34,18 @@ def log_trade(symbol, action, quantity, price, cursor, conn, lock):
logger.info(f"Logged trade: {action} {quantity} {symbol} @ ${price:.2f}")
def _latest_trade_message(trade) -> str:
for entry in reversed(getattr(trade, "log", []) or []):
message = getattr(entry, "message", "")
if message:
return message
return ""
def _normalized_trade_status(trade) -> str:
return (getattr(getattr(trade, "orderStatus", None), "status", "") or "").strip()
@router.get("/tradelog", response_class=HTMLResponse)
async def tradelog(request: Request):
cursor = dependencies.get_db_cursor()
@@ -145,9 +157,40 @@ async def webhook(request: Request):
primary_trade = trades_list[0]
for _ in range(60):
await asyncio.sleep(0.5)
status = _normalized_trade_status(primary_trade)
if status in {"ValidationError", "Cancelled", "ApiCancelled", "Inactive"}:
break
if primary_trade.isDone():
break
status = _normalized_trade_status(primary_trade)
if status == "ValidationError":
for t in trades_list:
try:
ib.cancelOrder(t.order)
except Exception:
pass
detail = _latest_trade_message(primary_trade) or "Order was rejected by IBKR"
raise HTTPException(status_code=409, detail=detail)
if status in {"Cancelled", "ApiCancelled", "Inactive"} and not primary_trade.fills:
detail = _latest_trade_message(primary_trade) or f"Order became {status}"
raise HTTPException(status_code=409, detail=detail)
if status in {"PendingSubmit", "PreSubmitted", "Submitted"} and not primary_trade.fills:
return JSONResponse(
status_code=202,
content={
"status": "accepted",
"symbol": symbol,
"action": action,
"quantity": quantity,
"order_type": order_type,
"order_status": status,
"message": _latest_trade_message(primary_trade),
},
)
if not primary_trade.isDone() or not primary_trade.fills:
for t in trades_list:
try: