fix: overlay Polygon prices on positions instead of stale Alpaca prices
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize unknown status
ci/woodpecker/push/build-2 Pipeline failed
ci/woodpecker/push/build-1 Pipeline failed
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled

Alpaca paper trading returns inaccurate current_price values.
The positions endpoint now uses the latest Polygon close from
market_snapshots and recomputes unrealized P&L from that.
This commit is contained in:
Celes Renata
2026-04-30 22:32:21 +00:00
parent 414f476620
commit 9a60ce127b
+31 -2
View File
@@ -1179,7 +1179,12 @@ async def get_order(order_id: str):
async def list_positions( async def list_positions(
ticker: Optional[str] = None, ticker: Optional[str] = None,
): ):
"""List current positions.""" """List current positions with Polygon market prices overlaid.
The current_price from the broker (Alpaca paper) can be stale or
inaccurate. We overlay the latest close from market_snapshots
(Polygon daily bars) and recompute unrealized P&L from that.
"""
if ticker: if ticker:
rows = await pool.fetch( rows = await pool.fetch(
"""SELECT p.id, p.broker_account_id, p.ticker, p.quantity, """SELECT p.id, p.broker_account_id, p.ticker, p.quantity,
@@ -1195,7 +1200,31 @@ async def list_positions(
p.unrealized_pnl, p.realized_pnl, p.updated_at p.unrealized_pnl, p.realized_pnl, p.updated_at
FROM positions p ORDER BY p.ticker""", FROM positions p ORDER BY p.ticker""",
) )
return [_row_to_dict(r) for r in rows]
# Build a price map from the latest Polygon bars
tickers = list({r["ticker"] for r in rows})
price_map: dict[str, float] = {}
if tickers:
price_rows = await pool.fetch(
"""SELECT DISTINCT ON (ticker) ticker, (data->>'c')::float AS close
FROM market_snapshots
WHERE ticker = ANY($1) AND snapshot_type = 'bar'
ORDER BY ticker, captured_at DESC""",
tickers,
)
price_map = {r["ticker"]: r["close"] for r in price_rows if r["close"]}
results = []
for r in rows:
d = _row_to_dict(r)
polygon_price = price_map.get(d["ticker"])
if polygon_price:
d["current_price"] = polygon_price
qty = d.get("quantity", 0) or 0
entry = d.get("avg_entry_price", 0) or 0
d["unrealized_pnl"] = round(qty * (polygon_price - entry), 2)
results.append(d)
return results
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------