From “Net PnL” to Truthful Metrics
Today was not about building something flashy.
It was about removing subtle distortions from the system.
And those are often the most important changes.
1. Fixed Parameter Binding Bug (Admin > Performance)
I encountered a classic but dangerous issue:
SQLSTATE[HY093]: Invalid parameter numberCode language: CSS (css)
The root cause wasn’t MySQL.
It was us.
The exchange filter (binance_real) was being inlined into raw SQL instead of properly bound as a parameter. In a UNION query, that caused:
- Mismatched bindings
- Different exchange values between branches
- Offset placeholder count mismatch
- Non-deterministic behavior
This reinforced a core Egis principle:
Never inline dynamic values. Always bind.
Especially in exchange-aware systems, where determinism matters.
2. Cleaned Up Average Metrics Logic
Today the Average Metrics page became more honest.
a) Only Count Active Trading Days
Previously, averages were diluted by days with zero trades.
Now:
- Only days with trades_count > 0 are used as denominator.
- Zero-trade days still exist in range, but don’t distort averages.
This made the numbers immediately more meaningful.
b) Renamed “Net PnL” → “Realised PnL”
This was subtle but important.
“Net PnL” was misleading on this page.
It was subtracting commission and drifting from how Admin > Positions defines truth.
Now:
- The metric is renamed to Realised PnL
- The calculation uses the same logic as Admin > Positions
- Commission is handled separately under Fees
- No more duplication of PnL logic
One source of truth.
No drift.
That alignment matters more than it looks.
c) Sorted Daily Breakdown by Date DESC
Simple but necessary.
The newest trading days now appear first.
Metrics should reflect recency — especially in a trading system.
d) Added Pagination to Daily Breakdown
This was structural.
- Summary cards still compute over full range.
- Daily rows are paginated.
- Sorting remains deterministic.
- Exchange filter persists.
- No LIMIT 0 edge cases.
Important distinction:
Pagination should never affect aggregation.
e) Fixed Duration Metrics Not Showing
Durations were silently failing.
Likely causes:
- Missing select alias
- Null duration for open trades
- Aggregation mismatch
- Frontend expecting a different field name
Fix involved:
- Ensuring duration is computed exactly like Admin > Positions.
- Explicitly selecting aggregated duration fields.
- Sending consistent duration_seconds.
- Formatting safely in UI.
- Preventing division-by-zero.
This reinforced another Egis principle:
If something shows blank, assume the pipeline is broken somewhere between DB and UI.
Trace the chain. Don’t guess.
What I Learned Today
1. Aggregation Drift Is Silent Technical Debt
When two pages compute “PnL” differently, it won’t crash.
It will lie quietly.
That’s worse than a crash.
Today was about unifying definitions.
2. UNION + Pagination + Bindings = Fragile If Sloppy
If bindings are not deterministic:
- MySQL errors surface unpredictably
- Queries become unsafe
- Exchange isolation breaks
Binding discipline is not optional in a trading system.
3. Metrics Are Philosophy
Changing from “Net PnL” to “Realised PnL” wasn’t cosmetic.
It clarified:
- What we are measuring.
- What we are not measuring.
- What belongs to fees.
- What belongs to performance.
Metrics shape perception.
Perception shapes decisions.
4. Egis Is Becoming Structurally Mature
Today wasn’t about new features.
It was about:
- Deterministic aggregation
- Exchange-aware filtering
- Shared logic reuse
- Active-day averaging
- Defensive pagination
- Evidence-first debugging
That’s architecture hardening.
Reflection
A trading system doesn’t fail because of missing buttons.
It fails because of:
- Slightly wrong PnL
- Slightly wrong fee aggregation
- Slightly wrong duration math
- Slightly inconsistent exchange scoping
Today I reduced that “slightly.”
And that matters.
— Son
