fix(performance): exclude paper trades by default (real-money tile must be real)
/performance summed paper + live P&L into one number. DashboardClient's 30d Performance tile consumes this directly and presents it as real performance — so a user who tried paper mode then went live saw simulated gains inflating the headline number on the homepage. Now: exclude hl_order_id=='paper' by default. Added optional ?include_paper query param for callers that explicitly want simulated stats. Analytics page already filters client-side by is_paper (separate fix), so both surfaces now agree: real-money numbers never include simulated fills. 72 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-2
@@ -23,6 +23,12 @@ async def get_performance(
|
||||
wallet: str = Query(..., description="Wallet address (lower-cased internally)"),
|
||||
ts: int = Query(..., description="Signed timestamp (ms)"),
|
||||
sig: str = Query(..., description="EIP-191 signature"),
|
||||
include_paper: bool = Query(
|
||||
False,
|
||||
description="Include paper (simulated) trades. Default false — this "
|
||||
"endpoint reports REAL-money performance, so paper fills "
|
||||
"(hl_order_id='paper') are excluded unless explicitly asked.",
|
||||
),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
wallet = wallet.lower().strip()
|
||||
@@ -43,13 +49,20 @@ async def get_performance(
|
||||
# realization order. A trade opened before the window but closed inside it
|
||||
# correctly counts; one opened inside but still open does not (closed_at IS
|
||||
# NOT NULL already excludes it).
|
||||
result = await db.execute(
|
||||
#
|
||||
# MONEY-SAFETY: by default exclude paper trades (hl_order_id == "paper").
|
||||
# Mixing simulated and real P&L into one "performance" number is misleading
|
||||
# — the dashboard tile that consumes this shows it as real performance.
|
||||
stmt = (
|
||||
select(BotTrade)
|
||||
.where(BotTrade.wallet_address == wallet)
|
||||
.where(BotTrade.closed_at.is_not(None))
|
||||
.where(BotTrade.closed_at >= since)
|
||||
.order_by(BotTrade.closed_at.asc())
|
||||
)
|
||||
if not include_paper:
|
||||
stmt = stmt.where(BotTrade.hl_order_id != "paper")
|
||||
stmt = stmt.order_by(BotTrade.closed_at.asc())
|
||||
result = await db.execute(stmt)
|
||||
trades = result.scalars().all()
|
||||
|
||||
total_trades = len(trades)
|
||||
|
||||
Reference in New Issue
Block a user