fix(perps): Incorrect PnL and order size displayed in perp market page after SL execution#27906
Conversation
…e after SL execution
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
Report: TAT-2483 — Incorrect PnL and order size on Perp Market page after SL executionSummaryMulti-fill trades (e.g., stop-loss orders split across multiple price levels) displayed incorrect PnL and order size on the Perp Market screen and Perps Home screen. The deduplication key used to merge REST and WebSocket fills collapsed distinct fills into one, losing data. The Activity page was unaffected because it doesn't use Map-based dedup. Root CauseFile: The dedup key Reproduction CommitSHA: Metro log excerpt (BUG_MARKER detection): Changes
Test PlanAutomated
ManualFeature: Multi-fill trade aggregation
Scenario: SL execution with multiple fills shows correct PnL
Given user has an account with SL trades that executed as multiple fills
When user navigates to the ETH market page trades tab
Then PnL and size match the Activity page values
And PnL and size match the HyperLiquid UI aggregated viewEvidence
Ticket |
…25-1840 # Conflicts: # app/components/UI/Perps/hooks/usePerpsHomeData.test.ts # app/components/UI/Perps/hooks/usePerpsHomeData.ts
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Tags selected:
The changes are isolated to Perps-specific data processing hooks with no impact on core infrastructure, navigation, controllers, or shared components. Performance Test Selection: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
|
✅ E2E Fixture Validation — Schema is up to date |
|




Description
Multi-fill trades (e.g., stop-loss orders split across multiple price levels by HyperLiquid) showed incorrect PnL and order size on the Perp Market screen and Perps Home screen. The deduplication key
orderId-timestampused to merge REST and WebSocket fills collapsed distinct fills with the same orderId+timestamp into one entry, losing size/PnL data. Fixed by extending the dedup key toorderId-timestamp-size-price, which preserves all distinct fills while still deduplicating identical entries from both sources. The Activity page was already correct (no Map-based dedup).Changelog
CHANGELOG entry: Fixed incorrect PnL and order size for multi-fill trades on the Perp Market screen and Home screen recent activity
Related issues
Fixes: TAT-2483
Manual testing steps
Screenshots/Recordings
Before
After
Validation Recipe
Automated validation recipe (validate-recipe.sh)
{ "pr": "27906", "title": "Verify aggregated PnL and order size consistency across all trade history surfaces", "jira": "TAT-2483", "acceptance_criteria": [ "Perp Market screen displays correct aggregated PnL and order size for multi-fill trades", "Perps Home screen recent activity shows identical aggregated values", "Activity page shows the same aggregated values as the other two surfaces", "Multi-fill trades (same orderId + timestamp) are properly aggregated, not collapsed by dedup" ], "validate": { "static": ["yarn lint:tsc"], "runtime": { "pre_conditions": ["wallet.unlocked", "perps.feature_enabled"], "steps": [ { "id": "check_multi_fills_exist", "description": "Verify account has multi-fill trades (prerequisite for the bug)", "action": "eval_async", "expression": "Engine.context.PerpsController.getActiveProviderOrNull().getOrderFills({aggregateByTime: false}).then(function(fills) { var grouped = {}; fills.forEach(function(f) { var key = f.orderId + '_' + f.timestamp; if (!grouped[key]) grouped[key] = 0; grouped[key] = grouped[key] + 1; }); var multiCount = 0; Object.keys(grouped).forEach(function(k) { if (grouped[k] > 1) multiCount = multiCount + 1; }); return JSON.stringify({totalFills: fills.length, multiFillKeys: multiCount}); })", "assert": { "operator": "gt", "field": "multiFillKeys", "value": 0 } }, { "id": "verify_new_dedup_preserves_fills", "description": "Assert that the fixed dedup key (orderId+timestamp+size+price) preserves all fills - this fails with old key", "action": "eval_async", "expression": "Engine.context.PerpsController.getActiveProviderOrNull().getOrderFills({aggregateByTime: false}).then(function(fills) { var oldKeyMap = {}; var newKeyMap = {}; fills.forEach(function(f) { var oldKey = f.orderId + '_' + f.timestamp; var newKey = f.orderId + '_' + f.timestamp + '_' + f.size + '_' + f.price; oldKeyMap[oldKey] = f; newKeyMap[newKey] = f; }); var oldCount = Object.keys(oldKeyMap).length; var newCount = Object.keys(newKeyMap).length; return JSON.stringify({totalFills: fills.length, oldKeyUnique: oldCount, newKeyUnique: newCount, oldKeyLost: fills.length - oldCount, newKeyLost: fills.length - newCount}); })", "assert": { "operator": "eq", "field": "newKeyLost", "value": 0 } }, { "id": "nav_market_eth", "description": "Navigate to ETH market page (has multi-fill SL trades)", "action": "flow_ref", "ref": "market-discovery", "params": { "symbol": "ETH" } }, { "id": "wait_market_render", "description": "Wait for trades list to render with REST fills", "action": "wait", "ms": 5000 }, { "id": "screenshot_market", "description": "Capture market trades list for visual review", "action": "screenshot", "filename": "market-trades-eth" }, { "id": "nav_activity", "description": "Navigate to activity page trades tab", "action": "flow_ref", "ref": "activity-view", "params": { "tab": "trades" } }, { "id": "wait_activity_render", "description": "Wait for activity data to load", "action": "wait", "ms": 3000 }, { "id": "screenshot_activity", "description": "Capture activity trades for visual comparison", "action": "screenshot", "filename": "activity-trades" } ] } } }Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Changes fill deduplication logic used to compute displayed trade history, which can affect user-visible PnL/size calculations if the new composite key has edge cases (e.g., precision/format differences for
size/price). Scope is limited to Perps Home and Market fills merging plus added tests.Overview
Fixes incorrect PnL and order size for stop-loss (multi-fill) executions by changing REST+WebSocket fill deduplication to key on
orderId+timestamp+size+price(instead of justorderId+timestamp), so distinct fills at the same timestamp are no longer collapsed.Adds/updates unit tests for
usePerpsHomeDataandusePerpsMarketFillsto cover multi-fill preservation and to assert WebSocket data still wins for exact duplicates across sources.Written by Cursor Bugbot for commit 68d15e8. This will update automatically on new commits. Configure here.