232 lines
7.3 KiB
Markdown
232 lines
7.3 KiB
Markdown
# Currency Conversion Feature
|
|
|
|
## Overview
|
|
|
|
The Portfolio Tracker now supports automatic currency conversion for transactions that are in different currencies than the portfolio's base currency. This feature allows users to have a unified view of their investments across multiple currencies.
|
|
|
|
## How It Works
|
|
|
|
### Automatic Detection
|
|
- When a transaction is added, the system automatically detects the stock's currency from Yahoo Finance
|
|
- If the transaction currency differs from the portfolio's base currency, conversion rates are fetched
|
|
- Converted values are displayed alongside original values for transparency
|
|
|
|
### Display Format
|
|
The transaction table now includes additional columns when multi-currency transactions are present:
|
|
|
|
| Original Column | New Column | Description |
|
|
|----------------|------------|-------------|
|
|
| Preis | Preis (BASE_CURRENCY) | Shows converted price in portfolio base currency |
|
|
| Gesamt | Gesamt (BASE_CURRENCY) | Shows converted total in portfolio base currency |
|
|
|
|
### Example Display
|
|
```
|
|
Original: 150.00 USD
|
|
Converted: 135.50 EUR (when EUR is portfolio base currency)
|
|
```
|
|
|
|
## Features
|
|
|
|
### 1. Real-time Currency Conversion
|
|
- Exchange rates are fetched from `exchangerate-api.io`
|
|
- Rates are cached for 1 hour to improve performance
|
|
- Supports all major world currencies
|
|
|
|
### 2. Smart Display Logic
|
|
- If transaction currency = portfolio currency: shows "-" in conversion columns
|
|
- If currencies differ: shows converted amount
|
|
- If conversion fails: shows "Conv. Error"
|
|
|
|
### 3. Portfolio Summary
|
|
- Total invested amount is automatically converted to portfolio base currency
|
|
- Summary shows "(converted)" indicator when multi-currency transactions exist
|
|
|
|
### 4. Currency Information Panel
|
|
A new information panel explains:
|
|
- That transactions in other currencies are automatically converted
|
|
- Exchange rates are updated hourly
|
|
- Conversion is for display purposes only
|
|
|
|
## Technical Implementation
|
|
|
|
### Currency Conversion Utility (`internal/util/currency.go`)
|
|
|
|
#### Key Functions:
|
|
```go
|
|
// Get exchange rate between two currencies
|
|
GetExchangeRate(fromCurrency, toCurrency string) (float64, error)
|
|
|
|
// Convert amount from one currency to another
|
|
ConvertCurrency(amount float64, fromCurrency, toCurrency string) (float64, error)
|
|
|
|
// Format currency with appropriate symbol
|
|
FormatCurrencyWithSymbol(amount float64, currency string) string
|
|
|
|
// Batch convert multiple amounts efficiently
|
|
BatchConvertCurrency(amounts []float64, fromCurrency, toCurrency string) ([]float64, error)
|
|
```
|
|
|
|
#### Caching System:
|
|
- **Cache Duration**: 1 hour per exchange rate
|
|
- **Cache Key Format**: `FROM_TO` (e.g., "USD_EUR")
|
|
- **Memory Usage**: Minimal - only stores rate and timestamp
|
|
- **Thread Safe**: Uses RWMutex for concurrent access
|
|
|
|
### Template Helper Functions (`internal/web/templates/helpers.go`)
|
|
|
|
#### Key Helper Functions:
|
|
```go
|
|
// Get converted price for display
|
|
getConvertedPrice(activity model.Activity, portfolioBaseCurrency string) string
|
|
|
|
// Get converted total for display
|
|
getConvertedTotal(activity model.Activity, portfolioBaseCurrency string) string
|
|
|
|
// Calculate total invested in base currency
|
|
calculateTotalInvestedInBaseCurrency(activities []model.Activity, baseCurrency string) float64
|
|
|
|
// Format total with conversion indicator
|
|
formatTotalInvestedWithConversion(activities []model.Activity, baseCurrency string) string
|
|
```
|
|
|
|
## Supported Currencies
|
|
|
|
The system supports all currencies provided by the exchange rate API, including:
|
|
|
|
| Currency | Code | Symbol |
|
|
|----------|------|--------|
|
|
| US Dollar | USD | $ |
|
|
| Euro | EUR | € |
|
|
| British Pound | GBP | £ |
|
|
| Japanese Yen | JPY | ¥ |
|
|
| Swiss Franc | CHF | CHF |
|
|
| Canadian Dollar | CAD | C$ |
|
|
| Australian Dollar | AUD | A$ |
|
|
| And many more... | | |
|
|
|
|
## Error Handling
|
|
|
|
### API Failures
|
|
- If exchange rate API is unavailable: shows "Conv. Error"
|
|
- Original transaction data is always preserved
|
|
- System continues to function normally
|
|
|
|
### Invalid Currencies
|
|
- Unknown currency codes are handled gracefully
|
|
- System falls back to displaying original amounts
|
|
- No data loss occurs
|
|
|
|
### Network Issues
|
|
- Cached rates are used when possible
|
|
- Graceful degradation to original currency display
|
|
- User is informed via "Conv. Error" message
|
|
|
|
## Performance Considerations
|
|
|
|
### Caching Strategy
|
|
- **Hit Rate**: High due to 1-hour cache duration
|
|
- **API Calls**: Minimized through intelligent caching
|
|
- **Memory Usage**: Low - only active currency pairs cached
|
|
- **Cleanup**: Automatic cache expiration
|
|
|
|
### Batch Operations
|
|
- Multiple conversions use single API call when possible
|
|
- Efficient for portfolios with many same-currency transactions
|
|
- Reduces API rate limit consumption
|
|
|
|
## Configuration
|
|
|
|
### API Limits
|
|
- **Free Tier**: 1,500 requests per month
|
|
- **Rate Limiting**: Built-in request throttling
|
|
- **Fallback**: Graceful degradation when limits exceeded
|
|
|
|
### Cache Management
|
|
```go
|
|
// Clear cache manually (for testing/debugging)
|
|
util.ClearCache()
|
|
|
|
// Get cache statistics
|
|
cacheInfo := util.GetCacheInfo()
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Adding Multi-Currency Transaction
|
|
1. User adds transaction for Apple (AAPL) in USD to EUR portfolio
|
|
2. System detects USD currency from Yahoo Finance
|
|
3. Fetches USD/EUR exchange rate
|
|
4. Displays both original (USD) and converted (EUR) amounts
|
|
5. Caches exchange rate for future use
|
|
|
|
### Portfolio Summary
|
|
```
|
|
Portfolio: "My Investments" (EUR)
|
|
Total Invested: 15,430.50 EUR (converted)
|
|
|
|
Recent Transactions:
|
|
- AAPL: 150.00 USD (~135.50 EUR)
|
|
- BMW.DE: 85.30 EUR (-)
|
|
- NESN.SW: 110.20 CHF (~102.15 EUR)
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
- All currency utilities have comprehensive test coverage
|
|
- Tests include edge cases and error conditions
|
|
- Performance benchmarks included
|
|
|
|
### Integration Tests
|
|
- Test with real transaction data
|
|
- Verify UI display logic
|
|
- Cache behavior validation
|
|
|
|
## Future Enhancements
|
|
|
|
### Planned Features
|
|
1. **Historical Rates**: Use transaction date for accurate historical conversion
|
|
2. **Multiple Rate Providers**: Fallback to alternative APIs
|
|
3. **Custom Rate Override**: Allow manual exchange rate input
|
|
4. **Currency Trends**: Show exchange rate history
|
|
5. **Base Currency Change**: Convert entire portfolio to new base currency
|
|
|
|
### API Improvements
|
|
1. **Rate Provider Selection**: Choose between multiple APIs
|
|
2. **Custom Update Intervals**: Configurable cache duration
|
|
3. **Offline Mode**: Store rates locally for offline use
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### "Conv. Error" Displayed
|
|
- **Cause**: Exchange rate API unavailable or rate limit exceeded
|
|
- **Solution**: Wait and refresh, or check internet connection
|
|
- **Impact**: Original data still visible and functional
|
|
|
|
#### Slow Loading
|
|
- **Cause**: First-time rate fetching or cache expiration
|
|
- **Solution**: Subsequent loads will be faster due to caching
|
|
- **Mitigation**: Consider pre-warming cache for common currencies
|
|
|
|
#### Incorrect Rates
|
|
- **Cause**: API data delay or temporary inconsistency
|
|
- **Solution**: Rates update automatically within 1 hour
|
|
- **Manual Fix**: Clear cache to force immediate refresh
|
|
|
|
### Debug Commands
|
|
```bash
|
|
# Clear currency cache
|
|
curl -X POST http://localhost:8080/api/admin/clear-currency-cache
|
|
|
|
# Get cache status
|
|
curl http://localhost:8080/api/admin/currency-cache-info
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
- **No API Keys Required**: Uses free public API
|
|
- **Rate Limiting**: Built-in protection against abuse
|
|
- **Data Privacy**: No sensitive data sent to external APIs
|
|
- **Fallback Security**: System functions without external dependencies |