Initial release v1.1.0
- Complete MVP for tracking Fidelity brokerage account performance - Transaction import from CSV with deduplication - Automatic FIFO position tracking with options support - Real-time P&L calculations with market data caching - Dashboard with timeframe filtering (30/90/180 days, 1 year, YTD, all time) - Docker-based deployment with PostgreSQL backend - React/TypeScript frontend with TailwindCSS - FastAPI backend with SQLAlchemy ORM Features: - Multi-account support - Import via CSV upload or filesystem - Open and closed position tracking - Balance history charting - Performance analytics and metrics - Top trades analysis - Responsive UI design Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
199
docs/TIMEFRAME_FILTERING.md
Normal file
199
docs/TIMEFRAME_FILTERING.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Timeframe Filtering Feature
|
||||
|
||||
## Overview
|
||||
The timeframe filtering feature allows users to view dashboard metrics and charts for specific date ranges, providing better insights into performance over different time periods.
|
||||
|
||||
## User Interface
|
||||
|
||||
### Location
|
||||
- Dashboard page (DashboardV2 component)
|
||||
- Dropdown filter positioned at the top of the dashboard, above metrics cards
|
||||
|
||||
### Available Options
|
||||
1. **All Time** - Shows all historical data
|
||||
2. **Last 30 Days** - Shows data from the past 30 days
|
||||
3. **Last 90 Days** - Shows data from the past 90 days
|
||||
4. **Last 180 Days** - Shows data from the past 180 days (default for chart)
|
||||
5. **Last 1 Year** - Shows data from the past 365 days
|
||||
6. **Year to Date** - Shows data from January 1st of current year to today
|
||||
|
||||
## What Gets Filtered
|
||||
|
||||
### Metrics Cards (Top of Dashboard)
|
||||
When a timeframe is selected, the following metrics are filtered by position open date:
|
||||
- Total Positions count
|
||||
- Open Positions count
|
||||
- Closed Positions count
|
||||
- Total Realized P&L
|
||||
- Total Unrealized P&L
|
||||
- Win Rate percentage
|
||||
- Average Win amount
|
||||
- Average Loss amount
|
||||
- Current Balance (always shows latest)
|
||||
|
||||
### Balance History Chart
|
||||
The chart adjusts to show the requested number of days:
|
||||
- All Time: ~10 years (3650 days)
|
||||
- Last 30 Days: 30 days
|
||||
- Last 90 Days: 90 days
|
||||
- Last 180 Days: 180 days
|
||||
- Last 1 Year: 365 days
|
||||
- Year to Date: Dynamic calculation from Jan 1 to today
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Frontend
|
||||
|
||||
#### Component: `DashboardV2.tsx`
|
||||
```typescript
|
||||
// State management
|
||||
const [timeframe, setTimeframe] = useState<TimeframeOption>('all');
|
||||
|
||||
// Convert timeframe to days for balance history
|
||||
const getDaysFromTimeframe = (tf: TimeframeOption): number => {
|
||||
switch (tf) {
|
||||
case 'last30days': return 30;
|
||||
case 'last90days': return 90;
|
||||
// ... etc
|
||||
}
|
||||
};
|
||||
|
||||
// Get date range for filtering
|
||||
const { startDate, endDate } = getTimeframeDates(timeframe);
|
||||
```
|
||||
|
||||
#### API Calls
|
||||
1. **Overview Stats**:
|
||||
- Endpoint: `GET /analytics/overview/{account_id}`
|
||||
- Parameters: `start_date`, `end_date`
|
||||
- Query key includes timeframe for proper caching
|
||||
|
||||
2. **Balance History**:
|
||||
- Endpoint: `GET /analytics/balance-history/{account_id}`
|
||||
- Parameters: `days` (calculated from timeframe)
|
||||
- Query key includes timeframe for proper caching
|
||||
|
||||
### Backend
|
||||
|
||||
#### Endpoint: `analytics_v2.py`
|
||||
```python
|
||||
@router.get("/overview/{account_id}")
|
||||
def get_overview(
|
||||
account_id: int,
|
||||
refresh_prices: bool = False,
|
||||
max_api_calls: int = 5,
|
||||
start_date: Optional[date] = None, # NEW
|
||||
end_date: Optional[date] = None, # NEW
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
# Passes dates to calculator
|
||||
stats = calculator.calculate_account_stats(
|
||||
account_id,
|
||||
update_prices=True,
|
||||
max_api_calls=max_api_calls,
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
```
|
||||
|
||||
#### Service: `performance_calculator_v2.py`
|
||||
```python
|
||||
def calculate_account_stats(
|
||||
self,
|
||||
account_id: int,
|
||||
update_prices: bool = True,
|
||||
max_api_calls: int = 10,
|
||||
start_date = None, # NEW
|
||||
end_date = None # NEW
|
||||
) -> Dict:
|
||||
# Filter positions by open date
|
||||
query = self.db.query(Position).filter(Position.account_id == account_id)
|
||||
|
||||
if start_date:
|
||||
query = query.filter(Position.open_date >= start_date)
|
||||
if end_date:
|
||||
query = query.filter(Position.open_date <= end_date)
|
||||
|
||||
positions = query.all()
|
||||
# ... rest of calculation logic
|
||||
```
|
||||
|
||||
## Filter Logic
|
||||
|
||||
### Position Filtering
|
||||
Positions are filtered based on their `open_date`:
|
||||
- Only positions opened on or after `start_date` are included
|
||||
- Only positions opened on or before `end_date` are included
|
||||
- Open positions are always included if they match the date criteria
|
||||
|
||||
### Balance History
|
||||
The balance history shows account balance at end of each day:
|
||||
- Calculated from transactions within the specified days
|
||||
- Does not filter by open date, shows actual historical balances
|
||||
|
||||
## Caching Strategy
|
||||
|
||||
React Query cache keys include timeframe parameters to ensure:
|
||||
1. Different timeframes don't conflict in cache
|
||||
2. Changing timeframes triggers new API calls
|
||||
3. Cache invalidation works correctly
|
||||
|
||||
Cache keys:
|
||||
- Overview: `['analytics', 'overview', accountId, startDate, endDate]`
|
||||
- Balance: `['analytics', 'balance-history', accountId, timeframe]`
|
||||
|
||||
## User Experience
|
||||
|
||||
### Performance
|
||||
- Balance history queries are fast (no market data needed)
|
||||
- Overview queries use cached prices by default (fast)
|
||||
- Users can still trigger price refresh within filtered timeframe
|
||||
|
||||
### Visual Feedback
|
||||
- Filter immediately updates both metrics and chart
|
||||
- Loading states handled by React Query
|
||||
- Stale data shown while fetching (stale-while-revalidate pattern)
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] All timeframe options work correctly
|
||||
- [ ] Metrics update when timeframe changes
|
||||
- [ ] Balance history chart adjusts to show correct date range
|
||||
- [ ] "All Time" shows complete data
|
||||
- [ ] Year to Date calculation is accurate
|
||||
- [ ] Filter persists during price refresh
|
||||
- [ ] Cache invalidation works properly
|
||||
- [ ] UI shows loading states appropriately
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
1. Add custom date range picker
|
||||
2. Compare multiple timeframes side-by-side
|
||||
3. Save preferred timeframe in user settings
|
||||
4. Add timeframe filter to Transactions table
|
||||
5. Add timeframe presets for tax year, quarters
|
||||
6. Export filtered data to CSV
|
||||
|
||||
## Related Components
|
||||
|
||||
- `TimeframeFilter.tsx` - Reusable dropdown component
|
||||
- `getTimeframeDates()` - Helper function to convert timeframe to dates
|
||||
- `TransactionTable.tsx` - Already uses timeframe filtering
|
||||
|
||||
## API Reference
|
||||
|
||||
### GET /analytics/overview/{account_id}
|
||||
```
|
||||
Query Parameters:
|
||||
- refresh_prices: boolean (default: false)
|
||||
- max_api_calls: integer (default: 5)
|
||||
- start_date: date (optional, format: YYYY-MM-DD)
|
||||
- end_date: date (optional, format: YYYY-MM-DD)
|
||||
```
|
||||
|
||||
### GET /analytics/balance-history/{account_id}
|
||||
```
|
||||
Query Parameters:
|
||||
- days: integer (default: 30, max: 3650)
|
||||
```
|
||||
Reference in New Issue
Block a user