Visualization Module Reference
===============================
The visualization module provides comprehensive charting and dashboard capabilities for financial data analysis.
.. contents:: Table of Contents
:local:
:depth: 2
Overview
--------
The visualization system delivers:
* **Interactive Dashboards**: Real-time portfolio and asset dashboards
* **Chart Generation**: Programmatic chart creation with Plotly
* **Technical Analysis**: Price charts with technical indicators
* **Performance Visualization**: Portfolio performance tracking and analytics
* **Responsive Design**: Mobile-optimized charts and layouts
.. automodule:: personal_finance.visualization.charts
:members:
:undoc-members:
Dashboard Access
----------------
Web Interface
~~~~~~~~~~~~~
* **Main Dashboard**: ``/dashboard/`` - Portfolio overview with summary metrics
* **Portfolio Details**: ``/dashboard/portfolio/{id}/`` - Detailed portfolio analysis
* **Asset Analysis**: ``/dashboard/asset/{id}/`` - Individual asset charts
Dashboard Features
~~~~~~~~~~~~~~~~~~
**Portfolio Overview**
- Total portfolio value across all accounts
- Number of active portfolios and positions
- Top performing assets with color-coded returns
- Interactive portfolio selection
**Chart Types Available**
1. **Performance Charts**: Track portfolio value and returns over time
2. **Allocation Charts**: Visualize asset distribution with interactive pie charts
3. **Risk Metrics**: Monitor Sharpe ratio, volatility, max drawdown, and beta
4. **Technical Analysis**: Individual asset charts with indicators
Chart API Reference
-------------------
REST Endpoints
~~~~~~~~~~~~~~
.. http:get:: /dashboard/api/portfolio/{id}/performance/
Get portfolio performance chart data.
:query int days: Number of days of history (default: 365)
**Response:**
.. code-block:: json
{
"figure": "{\"data\": [...], \"layout\": {...}}",
"title": "Portfolio Performance - Last 365 Days",
"type": "performance"
}
.. http:get:: /dashboard/api/portfolio/{id}/allocation/
Get portfolio allocation chart data.
**Response:**
.. code-block:: json
{
"figure": "{\"data\": [...], \"layout\": {...}}",
"title": "Current Asset Allocation",
"type": "allocation"
}
.. http:get:: /dashboard/api/asset/{id}/price/
Get asset price chart with technical indicators.
:query int days: Number of days of price history
:query array indicators: Technical indicators to include (sma_20, rsi, macd)
**Response:**
.. code-block:: json
{
"figure": "{\"data\": [...], \"layout\": {...}}",
"title": "AAPL Price Chart with Indicators",
"type": "technical"
}
Chart Data Structure
~~~~~~~~~~~~~~~~~~~~
All chart endpoints return data in this format:
.. code-block:: python
{
'figure': str, # Plotly JSON string
'title': str, # Chart title
'type': str # Chart type: performance|allocation|risk|technical|empty
}
JavaScript Client Usage
-----------------------
Basic Chart Loading
~~~~~~~~~~~~~~~~~~~
.. code-block:: javascript
// Load portfolio performance chart
function loadPerformanceChart(portfolioId, containerId) {
fetch(`/dashboard/api/portfolio/${portfolioId}/performance/?days=365`)
.then(response => response.json())
.then(data => {
const plotData = JSON.parse(data.figure);
Plotly.newPlot(containerId, plotData.data, plotData.layout);
})
.catch(error => {
console.error('Failed to load chart:', error);
});
}
// Load asset allocation chart
function loadAllocationChart(portfolioId, containerId) {
fetch(`/dashboard/api/portfolio/${portfolioId}/allocation/`)
.then(response => response.json())
.then(data => {
const plotData = JSON.parse(data.figure);
Plotly.newPlot(containerId, plotData.data, plotData.layout);
});
}
Advanced Chart Integration
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: javascript
// Technical analysis chart with indicators
function loadTechnicalChart(assetId, containerId, indicators = ['sma_20', 'rsi']) {
const params = new URLSearchParams({
days: 252,
...indicators.reduce((acc, indicator) => {
acc[`indicators`] = indicator;
return acc;
}, {})
});
fetch(`/dashboard/api/asset/${assetId}/price/?${params}`)
.then(response => response.json())
.then(data => {
if (data.type === 'empty') {
showEmptyChartMessage(containerId, data.title);
} else {
const plotData = JSON.parse(data.figure);
Plotly.newPlot(containerId, plotData.data, plotData.layout);
}
});
}
function showEmptyChartMessage(containerId, message) {
document.getElementById(containerId).innerHTML =
`
${message}
`;
}
React Integration
~~~~~~~~~~~~~~~~~
.. code-block:: javascript
import { useState, useEffect } from 'react';
function useChartData(url) {
const [chartData, setChartData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setChartData(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, [url]);
return { chartData, loading, error };
}
function PortfolioChart({ portfolioId, timeframe = 365 }) {
const url = `/dashboard/api/portfolio/${portfolioId}/performance/?days=${timeframe}`;
const { chartData, loading, error } = useChartData(url);
useEffect(() => {
if (chartData && chartData.type !== 'empty') {
const plotData = JSON.parse(chartData.figure);
Plotly.newPlot('chart-container', plotData.data, plotData.layout);
}
}, [chartData]);
if (loading) return Loading chart...
;
if (error) return Error loading chart: {error.message}
;
if (chartData?.type === 'empty') return {chartData.title}
;
return ;
}
Programmatic Chart Generation
-----------------------------
Chart Services
~~~~~~~~~~~~~~~
.. autoclass:: personal_finance.visualization.charts.PortfolioCharts
:members:
:undoc-members:
.. autoclass:: personal_finance.visualization.charts.AssetCharts
:members:
:undoc-members:
Python Chart Creation
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from personal_finance.visualization.charts import PortfolioCharts, AssetCharts
from personal_finance.portfolios.models import Portfolio
from personal_finance.assets.models import Asset
from datetime import date, timedelta
# Initialize chart generators
portfolio_charts = PortfolioCharts()
asset_charts = AssetCharts()
# Generate portfolio performance chart
portfolio = Portfolio.objects.get(id=1)
end_date = date.today()
start_date = end_date - timedelta(days=365)
performance_chart = portfolio_charts.create_portfolio_performance_chart(
portfolio, start_date, end_date
)
# Generate asset allocation chart
allocation_chart = portfolio_charts.create_asset_allocation_chart(portfolio)
# Generate risk metrics chart
risk_chart = portfolio_charts.create_risk_metrics_chart(portfolio)
# Generate asset price chart with technical indicators
asset = Asset.objects.get(symbol='AAPL')
price_chart = asset_charts.create_price_chart_with_indicators(
asset, days=252, indicators=['sma_20', 'rsi']
)
Advanced Chart Customization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
# Custom chart styling
chart_generator = PortfolioCharts()
chart_generator.performance_colors = {
'positive': '#00C851', # Green for gains
'negative': '#ff4444', # Red for losses
'neutral': '#33b5e5' # Blue for neutral
}
# Generate chart with custom styling
custom_chart = chart_generator.create_portfolio_performance_chart(
portfolio, start_date, end_date
)
Chart Configuration
-------------------
Time Periods
~~~~~~~~~~~~
Available time periods for performance charts:
* **30 Days** - Short-term performance tracking
* **3 Months** - Quarterly analysis
* **6 Months** - Semi-annual review
* **1 Year** - Annual performance (default)
* **2 Years** - Long-term trends
Technical Indicators
~~~~~~~~~~~~~~~~~~~~
Supported indicators for asset price charts:
.. data:: TECHNICAL_INDICATORS
* **SMA 20** - Simple Moving Average (20 periods)
* **RSI** - Relative Strength Index (14 periods)
* **MACD** - Moving Average Convergence Divergence
Indicator Configuration:
.. code-block:: python
indicators_config = {
'sma_20': {
'name': 'SMA (20)',
'color': '#FFA726',
'width': 2
},
'rsi': {
'name': 'RSI (14)',
'color': '#42A5F5',
'secondary_y': True,
'range': [0, 100]
},
'macd': {
'name': 'MACD',
'color': '#66BB6A',
'secondary_y': True
}
}
Interactive Features
--------------------
Chart Interactions
~~~~~~~~~~~~~~~~~~
All charts support these interactive features:
* **Zoom**: Click and drag to zoom into specific time periods
* **Pan**: Hold and drag to pan across the chart
* **Hover**: Hover over data points for detailed information
* **Legend**: Click legend items to show/hide data series
* **Download**: Use toolbar to download charts as images
* **Responsive**: Automatically resize based on container dimensions
Mobile Optimization
~~~~~~~~~~~~~~~~~~~
Charts are optimized for mobile devices:
* Touch-friendly interactions
* Responsive layouts that adjust chart heights
* Simplified hover information for smaller screens
* Optimized loading for slower connections
Error Handling
--------------
Chart Error States
~~~~~~~~~~~~~~~~~~
The system handles various error conditions gracefully:
.. code-block:: python
# No data available
if not portfolio.positions.exists():
return create_empty_chart("No active positions in portfolio")
# Insufficient data
if len(price_data) < 30:
return create_empty_chart("Insufficient data for analysis")
# API errors
try:
chart_data = generate_chart(portfolio)
except Exception as e:
logger.error(f"Chart generation failed: {e}")
return create_empty_chart("Failed to generate chart")
Error Response Format
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: json
{
"error": "Failed to generate chart",
"message": "Insufficient data for analysis",
"type": "empty",
"title": "No Data Available"
}
Performance Optimization
------------------------
Loading Strategy
~~~~~~~~~~~~~~~~
Charts load asynchronously to prevent UI blocking:
.. code-block:: javascript
// Show loading state
function showChartLoading(containerId) {
document.getElementById(containerId).innerHTML =
'';
}
// Load chart with loading state
function loadChartWithSpinner(url, containerId) {
showChartLoading(containerId);
fetch(url)
.then(response => response.json())
.then(data => {
const plotData = JSON.parse(data.figure);
Plotly.newPlot(containerId, plotData.data, plotData.layout);
})
.catch(error => {
showChartError(containerId, error.message);
});
}
Data Optimization
~~~~~~~~~~~~~~~~~
* **Efficient Queries**: Portfolio snapshots queried with date ranges
* **Caching**: Chart data cached where appropriate
* **Pagination**: Large datasets paginated to prevent timeouts
* **Lazy Loading**: Charts loaded on-demand as user navigates
Memory Management
~~~~~~~~~~~~~~~~~
.. code-block:: python
# Efficient data processing
def generate_performance_data(portfolio, start_date, end_date):
# Use values_list to reduce memory usage
snapshots = portfolio.snapshots.filter(
date__gte=start_date,
date__lte=end_date
).values_list('date', 'total_value', 'daily_return')
# Process in batches for large datasets
batch_size = 1000
for batch in chunk_queryset(snapshots, batch_size):
yield process_snapshot_batch(batch)
Custom Chart Extensions
-----------------------
Extending Chart Classes
~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
# Custom portfolio chart implementation
class CustomPortfolioCharts(PortfolioCharts):
def create_sector_allocation_chart(self, portfolio):
\"\"\"Create sector-based allocation chart.\"\"\"
positions = portfolio.positions.select_related('asset__sector')
sector_allocation = {}
for position in positions:
sector = position.asset.sector or 'Unknown'
sector_allocation[sector] = sector_allocation.get(sector, 0) + float(position.current_value)
# Generate pie chart
figure = go.Figure(data=[go.Pie(
labels=list(sector_allocation.keys()),
values=list(sector_allocation.values()),
hole=.3
)])
figure.update_layout(title=\"Portfolio by Sector\")
return self._format_chart_response(figure, \"Sector Allocation\", \"allocation\")
def create_performance_comparison_chart(self, portfolios, start_date, end_date):
\"\"\"Compare performance across multiple portfolios.\"\"\"
figure = go.Figure()
for portfolio in portfolios:
snapshots = portfolio.snapshots.filter(
date__gte=start_date, date__lte=end_date
).order_by('date')
dates = [s.date for s in snapshots]
values = [float(s.total_value) for s in snapshots]
figure.add_trace(go.Scatter(
x=dates, y=values,
mode='lines',
name=portfolio.name
))
figure.update_layout(
title=\"Portfolio Performance Comparison\",
xaxis_title=\"Date\",
yaxis_title=\"Portfolio Value ($)\"
)
return self._format_chart_response(figure, \"Performance Comparison\", \"performance\")
Integration Examples
--------------------
Dashboard Integration
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
# views.py - Dashboard view with charts
from django.shortcuts import render
from personal_finance.visualization.charts import PortfolioCharts
def portfolio_dashboard(request, portfolio_id):
portfolio = get_object_or_404(Portfolio, id=portfolio_id, user=request.user)
chart_service = PortfolioCharts()
context = {
'portfolio': portfolio,
'chart_urls': {
'performance': f'/dashboard/api/portfolio/{portfolio_id}/performance/',
'allocation': f'/dashboard/api/portfolio/{portfolio_id}/allocation/',
'risk_metrics': f'/dashboard/api/portfolio/{portfolio_id}/risk/'
}
}
return render(request, 'dashboard/portfolio_detail.html', context)
Export Integration
~~~~~~~~~~~~~~~~~~
.. code-block:: python
# Export chart data for external analysis
def export_chart_data(portfolio, chart_type, format='json'):
chart_service = PortfolioCharts()
if chart_type == 'performance':
chart_data = chart_service.create_portfolio_performance_chart(portfolio)
elif chart_type == 'allocation':
chart_data = chart_service.create_asset_allocation_chart(portfolio)
else:
raise ValueError(f\"Unsupported chart type: {chart_type}\")
if format == 'json':
return chart_data['figure']
elif format == 'csv':
# Convert Plotly data to CSV
return convert_plotly_to_csv(chart_data['figure'])
else:
raise ValueError(f\"Unsupported format: {format}\")
Troubleshooting
---------------
Common Issues
~~~~~~~~~~~~~
1. **Chart Not Loading**
- Check browser console for JavaScript errors
- Verify API endpoint accessibility
- Ensure Plotly.js library is loaded
2. **No Data Available**
- Verify portfolio has positions and price history
- Check date ranges for data availability
- Review portfolio snapshot generation
3. **Performance Issues**
- Reduce time range for large datasets
- Limit number of technical indicators
- Check database query performance
Debug Tools
~~~~~~~~~~~
.. code-block:: python
# Enable detailed chart logging
import logging
logging.getLogger('personal_finance.visualization').setLevel(logging.DEBUG)
# Test chart generation directly
from personal_finance.visualization.charts import PortfolioCharts
charts = PortfolioCharts()
chart_data = charts.create_portfolio_performance_chart(portfolio)
print(f\"Chart type: {chart_data['type']}\")
print(f\"Chart title: {chart_data['title']}\")
print(f\"Data length: {len(chart_data['figure'])}\")
See Also
--------
* :doc:`../api/rest_endpoints` - Dashboard API endpoints
* :doc:`portfolios` - Portfolio management
* :doc:`analytics` - Analytics integration with charts
* :doc:`../development/testing` - Testing visualization components