How to Reconcile Your Bot's P&L Against the Exchange Dashboard
Your bot reports a $2,400 profit. The exchange dashboard shows $1,850. You've just spent three hours cross-checking trades one by one. This happens to every serious bot trader—and it's not always the bot lying to you or the dashboard being wrong. Both can be correct and still show different numbers. The difference lives in what each system counts, when it counts it, and what it ignores.
This guide walks you through a concrete, row-by-row method to find every dollar of that gap and understand exactly where it came from. You'll pull raw API data, match it against your bot's internal journal, and identify the real sources of discrepancy so you can trust your P&L again.
Why Exchange Dashboards and Bot P&L Diverge
The exchange dashboard and your bot are watching the same account—but they're answering different questions. Your bot tracks positions and realized P&L as it executes. The exchange tracks settled fills, realized P&L, and unrealized positions at the moment you check the dashboard. Here's where the gaps live:
- Fee handling. The exchange deducts trading fees from your collateral and reports them in your account statement. Your bot may calculate estimated fees, apply them at different times, or use a fixed assumption that differs from actual exchange fees. If your bot assumed 0.02% and the actual fee was 0.025%, that gap compounds across hundreds of trades.
- Funding payments. On perpetual futures exchanges like Hyperliquid, funding payments are collected or paid every 8 hours based on open positions and the funding rate at settlement time. Your bot may calculate an estimated funding cost and deduct it from P&L, but the actual payment happens async at exchange time. The bot's estimate may lag behind or predict incorrectly.
- Partial fills and order timing. When your bot places an order, it may fill in batches. Your bot records the trade at execution time; the exchange records each fill separately. If your bot crosses a spread multiple times for a single logical trade, the two systems may timestamp or aggregate the fills differently.
- Realized vs. unrealized P&L. Your bot may calculate position P&L in real time as prices move. The exchange dashboard only knows realized P&L (closed positions) and unrealized P&L (current mark-to-market on open positions). If you check the dashboard mid-trade or after the bot closed a position, the unrealized portion doesn't exist on the exchange—it only appears in your bot's internal state.
- Timing and timezone. Your bot's clock and the exchange's clock drift by milliseconds. More importantly, your bot's journal timestamp and the exchange API timestamp for the same fill can be seconds apart. If you naively match fills by exact timestamp, you'll miss matches. Exchanges also bucket stats by UTC day, not your local time.
- Collateral movements and transfers. Any deposits, withdrawals, or collateral transfers in or out of your subaccount are part of your net P&L equation but may not be captured in your bot's P&L calculation. The dashboard includes them; the bot may not.
The Reconciliation Method: Step-by-Step
Use this method to extract the ground truth from the exchange API and match it against your bot's journal line-by-line. You'll identify every source of discrepancy and prove where the gap lives.
Step 1: Extract Your Bot's Internal P&L Report
Dump your bot's complete trade journal and any internal P&L calculation it maintains. This should include:
- Every trade: entry timestamp, symbol, side (buy/sell), quantity, price, exchange fee paid, fees in native currency (if applicable)
- Every position close: entry price, exit price, quantity, profit/loss, realized fees, any funding payments the bot recorded
- A final summary: total realized P&L, estimated funding costs, estimated fees, bot-reported net P&L
Export this to CSV with precise numeric values—no rounding, no currency conversion shortcuts. Include the bot's internal timestamp for every trade.
Step 2: Pull Raw Fill Data from the Exchange API
Call the exchange's fills or order history endpoint. For Hyperliquid:
- Endpoint:
GET /infowith actionuserFills(requires signed request with your API key) - Parameters: user address, start time (Unix milliseconds), end time
- Response: array of fill objects with: timestamp, symbol, side, price, quantity, fee, fee currency, order ID
Download the complete fill history for your reporting period. Store this in CSV format with columns: timestamp_ms, symbol, side, price_usd, size, fee_usd, fee_token, order_id. Ensure you have the exact numeric values without rounding. If the exchange API returns fee in a native token, convert it to USD at the spot price at the moment of fill, or keep both columns and reconcile separately.
Step 3: Pull Funding Payments and Other Collateral Movements
Funding payments are separate from trades. Call the funding history endpoint (if available) or check the account statement in the dashboard. For Hyperliquid, check:
- Funding payment history: timestamp, symbol, funding rate, position size, amount paid/received (in USD or token)
- Collateral changes: deposits, withdrawals, transfers in the period
- Liquidations or auto-deleverage events: any forced position exits
Export this as a separate CSV: timestamp_ms, event_type (funding/deposit/withdrawal/etc), symbol, amount_usd. This will be your second reconciliation pass.
Step 4: Aggregate Exchange Data by Position Lifecycle
Group fills by position: entry fills, all subsequent fills on that position, and exit fills. For each position:
- Sum entry quantity and volume-weighted entry price
- Sum exit quantity and volume-weighted exit price
- Calculate realized P&L: (exit_price - entry_price) × quantity
- Sum all fees paid (in USD or native currency)
- Sum funding payments (if the position was open across funding intervals)
- Net realized P&L = gross P&L - fees - funding
This is your exchange's ground truth. Every dollar that moved is here, timestamped and fee-accounted.
Step 5: Match Bot Positions Against Exchange Positions
For each closed position your bot reported, find the corresponding set of fills in the exchange data. Match on:
- Symbol (exact match)
- Entry timestamp (within 5 seconds—account for clock drift)
- Entry quantity (exact or within 0.01 lot units, depending on precision)
- Entry price (within 0.01% of bot's recorded price)
When you find a match, compare:
- Bot's recorded fees vs. exchange's actual fees for that fill
- Bot's exit timestamp vs. exchange's exit fill timestamp
- Bot's exit price vs. exchange's actual fill price (if partial fills occurred, check if the bot averaged correctly)
- Bot's P&L calculation vs. exchange's calculated P&L
Create a reconciliation table: Bot Position ID | Symbol | Bot Entry Price | Exchange Entry Price | Match? | Bot Fees | Exchange Fees | Fee Difference | Bot P&L | Exchange P&L | P&L Difference.
Step 6: Isolate the Gaps
Sum all differences in the reconciliation table. You should now have clear categories:
- Fee discrepancies (bot estimated wrong, or charged a different tier)
- Funding payment discrepancies (bot predicted funding cost, actual was different)
- Partial fill aggregation errors (bot didn't correctly average fills)
- Positions the bot recorded but the exchange never filled (order rejection or timeout)
- Unrealized P&L the bot counted but the exchange didn't (position still open)
- Collateral movements the bot didn't account for
Common Causes of Mismatches: A Checklist
When you find gaps, work through this list:
- Fee tier change. Did your account volume cross a tier threshold? Some exchanges apply retroactive fee adjustments or change your fee rate mid-period. Check your account tier history on the exchange.
- Rebates or maker fees. If your exchange offers rebates for limit orders or pays maker fees, your bot may not have accounted for them. The dashboard will show the net fee; your bot may only know the gross fee.
- Funding rate timing. Funding payments are applied at specific times (usually every 8 hours UTC). If your bot calculated funding at a different rate or time, the discrepancy is pure prediction error. Compare your bot's funding timestamp to the actual exchange funding settlement timestamp.
- Rounding and precision. Your bot may round intermediate calculations to 2 decimal places; the exchange tracks exact microsatoshi or exact basis points. Across 100+ trades, rounding compounds. Recalculate without rounding.
- Bot crash or restart. If your bot restarted during the period, it may have lost state and missed recording a fill or funding payment. Check the bot's logs for crashes or restarts aligned with gaps in the journal.
- Stale position state. If the bot was stopped while a position was open, it won't record the unrealized P&L at the stop time. The dashboard does. This gap is expected and not an error.
- Slippage on market orders. Market orders fill at multiple prices across the book. If your bot estimated slippage as X and actual was Y, that difference is baked into the P&L. Compare the bot's assumed fill price to the actual volume-weighted average fill price.
- Liquidation cascades. If a position was force-liquidated or auto-deleveraged, the exchange may have executed it at a much worse price than your bot's estimated exit. Check for liquidation flags in the fills API response.
- Token price fluctuation. If fees are paid in a non-USD token and you converted them to USD using a stale price, the bot's USD fee estimate will diverge from the dashboard's real USD value. Use the exact price at fill time, not the current price.
- Open positions at period end. The dashboard's unrealized P&L on open positions is mark-to-market at the moment you check. Your bot may have calculated it at a different time or different price. This divergence is natural and resolves when the position closes.
Conclusion
Bot P&L reconciliation is grunt work, but it's worth the precision. By pulling API data and matching row-by-row, you move from suspicion ("something's wrong") to certainty ("fees are 0.015% higher than assumed"). That certainty lets you adjust your bot's assumptions, recalibrate your profit targets, and trust the next month's numbers.
The discrepancy between your bot and the exchange dashboard is never a mystery if you have the API data. Every trade, every fee, every funding payment is timestamped and signed. Your job is to match them correctly and account for the real sources of divergence. Do that, and you own your numbers.