# FIFO Same-Day Trade Fix
## Issue
When buy and sell trades occurred on the same date, the FIFO calculator was not consistently processing buys before sells. This led to:
- Incorrect realized P&L calculations
- Inconsistent position tracking
- Potential "sell without open position" warnings
## Root Cause
The original sorting logic only sorted by date:
```typescript
const sortedTrades = [...trades].sort((a, b) =>
new Date(a.trade_date).getTime() - new Date(b.trade_date).getTime()
);
```
When trades had the same timestamp, JavaScript's sort is **unstable** - it doesn't guarantee any particular order for equal values. This meant that on the same day, sells could be processed before buys, violating FIFO principles.
## Example Scenario That Failed
**Before Fix:**
```
Same Day (2024-01-15):
- SELL 10 @ 150 (processed first - ERROR!)
- BUY 10 @ 100 (processed second)
Result: Sell has no inventory to match → Incorrect P&L
```
**After Fix:**
```
Same Day (2024-01-15):
- BUY 10 @ 100 (processed first - CORRECT!)
- SELL 10 @ 150 (processed second)
Result: Correct FIFO matching → Realized P&L = +500
```
## Solution
Updated the sort logic to have a two-level sort:
1. **Primary sort**: By trade date (chronological)
2. **Secondary sort**: By trade type (BUYs before SELLs on same date)
```typescript
const sortedTrades = [...trades].sort((a, b) => {
const dateA = new Date(a.trade_date).getTime();
const dateB = new Date(b.trade_date).getTime();
// First, sort by date
if (dateA !== dateB) {
return dateA - dateB;
}
// If same date, BUYs come before SELLs
if (a.trade_type === 'buy' && b.trade_type === 'sell') {
return -1; // a (buy) comes first
}
if (a.trade_type === 'sell' && b.trade_type === 'buy') {
return 1; // b (buy) comes first
}
// If both are same type on same date, maintain original order
return 0;
});
```
## Impact
### What Changes:
- ✅ Consistent FIFO behavior on same-day trades
- ✅ Correct realized P&L calculations
- ✅ Proper position lifecycle tracking
- ✅ No more "sell without open position" warnings for valid trades
### What Stays the Same:
- ✅ All trades on different dates process exactly as before
- ✅ Overall FIFO matching logic unchanged
- ✅ No performance impact
- ✅ No changes to unrealized P&L calculations
## Real-World Examples
### Intraday Trading
```
Date: 2024-03-15
09:30 AM - BUY 100 @ 250
11:00 AM - SELL 50 @ 260
02:30 PM - BUY 50 @ 255
03:45 PM - SELL 100 @ 265
Before Fix: Could process sells before buys → Wrong P&L
After Fix: Guaranteed correct order → Accurate P&L
```
### Same-Day Import from Broker
When importing CSV files where all trades have date but no time:
```
All trades show: 2024-03-15
Before Fix: Random order → Inconsistent results on re-import
After Fix: Always BUYs first → Consistent results every time
```
## Testing
### Manual Test Case
1. Create trades on same date:
- BUY 10 @ 100
- SELL 5 @ 150
2. Expected Results:
- Net Quantity: 5
- Realized P&L: +250 (5 × (150-100))
- Active Position: 5 @ 100
3. Verify in Tradebook:
- Check realized P&L matches
- Check unrealized P&L is based on remaining 5 @ 100
- No console warnings
### Edge Cases Covered
- ✅ Multiple buys and sells on same day
- ✅ All buys before all sells (correct order)
- ✅ All sells before all buys in data (will be reordered)
- ✅ Interleaved buy-sell-buy-sell (will be reordered to buy-buy-sell-sell)
- ✅ Same quantity buy and sell on same day (position closes same day)
## Files Changed
- `equity/lib/fifo-calculator.ts` - Updated sort logic
## Rollback
If needed, revert to previous version:
```bash
git checkout HEAD~1 equity/lib/fifo-calculator.ts
```
## No Migration Needed
- ✅ No database changes
- ✅ No API changes
- ✅ Fix applies automatically on next page load
- ✅ All historical data will be recalculated correctly
## Verification
After deploying, verify with your data:
1. Look for stocks where you traded multiple times on same day
2. Check the realized P&L in tradebook
3. Verify it matches your broker statements
4. Check browser console - should see no FIFO warnings
Console log to look for:
```
[Tradebook XIRR] SYMBOL: X trades, netQty: Y, price: Z
```
Should NOT see:
```
Sell trade without open position: {...}
```
---
**Status**: ✅ Fixed
**Date**: December 30, 2025
**Impact**: Critical for same-day trading accuracy
**Breaking Changes**: None