SECTION 1: LEARNING OBJECTIVES

By the end of this lesson, you will be able to:

  • Understand the principles of effective data visualisation – clarity, accuracy, efficiency, and aesthetics.

  • Apply the grammar of graphics to create layered, customised visualisations.

  • Select the appropriate chart type for different financial data and narratives.

  • Create interactive dashboards using Plotly Dash or Streamlit.

  • Use storytelling techniques to communicate insights effectively to stakeholders.

  • Integrate visualisations with financial models for dynamic reporting.

  • Implement time-series visualisations – candlestick charts, heatmaps, and animated plots.

  • Visualise risk metrics – VaR, ES, volatility surfaces, and correlation matrices.

  • Design executive dashboards with KPIs, trends, and alerts.

  • Apply best practices for colour, annotation, and layout to enhance comprehension.


SECTION 2: PRINCIPLES OF EFFECTIVE DATA VISUALISATION

2.1 The Visualisation Hierarchy
 
 
Level Goal Example
Level 1: Exploratory Understand the data. Histograms, scatter plots, box plots.
Level 2: Explanatory Communicate insights. Bar charts, line charts, bullet charts.
Level 3: Decision Drive action. Dashboards with KPIs, alerts, and drill-down.
2.2 Key Principles
 
 
Principle Description Financial Example
Clarity Remove clutter; focus on the message. Clean line chart of a stock price.
Accuracy Ensure scales, labels, and axes are truthful. No truncated axes; proper baseline.
Efficiency Use pre-attentive attributes (colour, size, position). Colour-coded profit/loss.
Consistency Use consistent colours, fonts, and scales. Uniform palette across dashboards.
Context Provide annotations and comparisons. Add benchmarks or targets.
Emotion Use storytelling to connect with the audience. Narrative around a financial crisis.
2.3 Choosing the Right Chart
 
 
Data Type Chart Type Financial Use
Time Series Line chart, area chart Stock prices, yields, GDP.
Comparison Bar chart, column chart Revenue by segment, expense breakdown.
Distribution Histogram, box plot Returns distribution, VaR.
Relationship Scatter plot, bubble chart Correlation, risk-return trade-off.
Composition Pie chart, stacked bar, waterfall Portfolio allocation, profit composition.
Geospatial Choropleth map Regional revenue, branch locations.
Correlation Heatmap Correlation matrix, volatility surface.
Candlestick OHLC chart Stock price movement.

SECTION 3: THE GRAMMAR OF GRAPHICS

Grammar of Graphics is a framework for describing visualisations in terms of layers:

  • Data: The dataset.

  • Aesthetics: Mapping variables to visual properties (x, y, colour, size, shape).

  • Geometric objects (geoms): Points, lines, bars, areas.

  • Facets: Subplots by category.

  • Coordinates: Cartesian, polar, etc.

  • Scales: Axes, colour scales.

  • Themes: Background, grid, font, legend.

Implementation in Python (Plotly/Matplotlib/Seaborn):

python
import plotly.express as px

# Grammar of Graphics with Plotly
fig = px.scatter(
    data_frame=df,
    x='Return',
    y='Risk',
    color='Sector',
    size='Market_Cap',
    hover_data=['Company'],
    title='Risk-Return Trade-off'
)
fig.show()

SECTION 4: INTERACTIVE DASHBOARDS WITH PLOTLY DASH

Dash is a Python framework for building interactive web applications.

Key Components:

  • Layout: HTML components, graphs, sliders, dropdowns.

  • Callbacks: Python functions that update the UI based on user interactions.

Example Structure:

python
import dash
from dash import dcc, html, Input, Output
import plotly.express as px

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('Portfolio Dashboard'),
    dcc.Dropdown(id='sector-dropdown', options=[...], value='All'),
    dcc.Graph(id='portfolio-chart')
])

@app.callback(
    Output('portfolio-chart', 'figure'),
    Input('sector-dropdown', 'value')
)
def update_chart(selected_sector):
    filtered_df = df if selected_sector == 'All' else df[df['Sector'] == selected_sector]
    fig = px.scatter(filtered_df, x='Return', y='Risk', color='ESG_Score')
    return fig

if __name__ == '__main__':
    app.run_server(debug=True)

SECTION 5: STORYTELLING WITH DATA

Effective data storytelling combines data, visuals, and narrative.

Key Elements:

  1. Context: Why is this important? What is the background?

  2. Insight: What did you discover? What are the key takeaways?

  3. Action: What should the audience do? What decisions should be made?

The Pyramid of Storytelling:

text
        Message
       /      \
   Insights  Insights
     /    \    /    \
    Data  Data  Data  Data

Example: Telling the Story of a Market Downturn

  • Data: Stock prices, volatility, trading volume, news sentiment.

  • Visuals: Line chart of the index, shaded recession periods, annotations of key events.

  • Narrative: “The 2022 bear market was driven by inflation, rising rates, and geopolitical tensions. However, defensive sectors outperformed, and opportunities emerged in energy and commodities.”


SECTION 6: IMPLEMENTATION IN PYTHON – FINANCIAL DASHBOARD

python
# ===================================================================
# BONUS LESSON 8: ADVANCED DATA VISUALISATION AND STORYTELLING
# ===================================================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import warnings
warnings.filterwarnings('ignore')

# Set style
sns.set_style("whitegrid")
np.random.seed(42)

print("="*70)
print("ADVANCED DATA VISUALISATION AND STORYTELLING FOR FINANCE")
print("="*70)

# ----------------------------------------------------------------
# PART A: GENERATE FINANCIAL DATA
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART A: Generating Financial Data for Visualisation")
print("-"*60)

# Stock price simulation (random walk)
n_days = 252
dates = pd.date_range(start='2023-01-01', periods=n_days, freq='B')
price = 100 * np.exp(np.cumsum(np.random.normal(0.0005, 0.015, n_days)))
volume = np.random.gamma(2, 1000, n_days).clip(100, 10000)

# Create OHLC data
df_stock = pd.DataFrame({
    'Date': dates,
    'Open': price * (1 + np.random.normal(0, 0.005, n_days)),
    'High': price * (1 + np.random.normal(0.01, 0.01, n_days)),
    'Low': price * (1 + np.random.normal(-0.01, 0.01, n_days)),
    'Close': price,
    'Volume': volume
})

# Ensure High is max of Open/Close and Low is min
df_stock['High'] = df_stock[['Open', 'High', 'Close']].max(axis=1)
df_stock['Low'] = df_stock[['Open', 'Low', 'Close']].min(axis=1)

print("Generated stock data:")
print(df_stock.head())

# Generate portfolio data
n_assets = 5
asset_returns = np.random.multivariate_normal(
    [0.0005] * n_assets,
    np.random.uniform(0.01, 0.03, (n_assets, n_assets)) * 0.5 + np.diag(np.random.uniform(0.015, 0.03, n_assets)),
    n_days
)
asset_names = ['Tech', 'Finance', 'Healthcare', 'Energy', 'Consumer']
df_returns = pd.DataFrame(asset_returns, columns=asset_names)
df_prices = 100 * (1 + df_returns.cumsum())

# ----------------------------------------------------------------
# PART B: CANDLESTICK CHART (PLOTLY)
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART B: Candlestick Chart")
print("-"*60)

# Create candlestick chart
fig = go.Figure(data=[go.Candlestick(
    x=df_stock['Date'],
    open=df_stock['Open'],
    high=df_stock['High'],
    low=df_stock['Low'],
    close=df_stock['Close'],
    name='Stock'
)])

# Add moving average
df_stock['MA_20'] = df_stock['Close'].rolling(20).mean()
fig.add_trace(go.Scatter(
    x=df_stock['Date'],
    y=df_stock['MA_20'],
    line=dict(color='orange', width=2),
    name='MA 20'
))

# Add volume as subplot (secondary y-axis)
fig.add_trace(go.Bar(
    x=df_stock['Date'],
    y=df_stock['Volume'],
    name='Volume',
    yaxis='y2',
    marker_color='lightblue',
    opacity=0.3
))

# Update layout
fig.update_layout(
    title='Stock Price with Candlesticks and Moving Average',
    yaxis_title='Price ($)',
    yaxis2=dict(title='Volume', overlaying='y', side='right'),
    xaxis_title='Date',
    template='plotly_white',
    height=600
)

# Show (in notebook, would use fig.show())
# For this script, save as HTML
fig.write_html('candlestick_chart.html')
print("Candlestick chart saved as 'candlestick_chart.html'")

# ----------------------------------------------------------------
# PART C: PORTFOLIO RISK-REWARD SCATTER PLOT
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART C: Portfolio Risk-Reward Scatter Plot")
print("-"*60)

# Calculate returns and risk for each asset
asset_returns_mean = df_returns.mean() * 252  # annualised
asset_returns_std = df_returns.std() * np.sqrt(252)

# Create a DataFrame for visualisation
df_assets = pd.DataFrame({
    'Asset': asset_names,
    'Return': asset_returns_mean,
    'Risk': asset_returns_std,
    'Sharpe': asset_returns_mean / asset_returns_std
})

# Plotly scatter
fig = px.scatter(
    df_assets,
    x='Risk',
    y='Return',
    text='Asset',
    size='Sharpe',
    color='Sharpe',
    color_continuous_scale='RdYlGn',
    title='Risk-Return Trade-off by Asset Class',
    labels={'Risk': 'Annualised Volatility', 'Return': 'Annualised Return'},
    hover_data={'Sharpe': ':.3f'}
)
fig.update_traces(textposition='top center')
fig.write_html('risk_reward_scatter.html')
print("Risk-reward scatter plot saved as 'risk_reward_scatter.html'")

# ----------------------------------------------------------------
# PART D: CORRELATION HEATMAP
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART D: Correlation Heatmap")
print("-"*60)

corr = df_returns.corr()

fig = go.Figure(data=go.Heatmap(
    z=corr.values,
    x=corr.columns,
    y=corr.columns,
    colorscale='RdBu_r',
    zmin=-1,
    zmax=1,
    text=corr.values.round(2),
    texttemplate='%{text}',
    textfont={"size": 12}
))

fig.update_layout(
    title='Asset Return Correlation Matrix',
    xaxis_title='Asset',
    yaxis_title='Asset',
    height=500,
    width=600
)
fig.write_html('correlation_heatmap.html')
print("Correlation heatmap saved as 'correlation_heatmap.html'")

# ----------------------------------------------------------------
# PART E: TIME-SERIES WITH ANNOTATIONS (STORYTELLING)
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART E: Time-Series with Annotations")
print("-"*60)

# Simulate a market index with events
index = 1000 * np.exp(np.cumsum(np.random.normal(0.0002, 0.01, n_days)))

# Add some events
event_dates = ['2023-03-15', '2023-06-30', '2023-09-15', '2023-12-20']
event_labels = ['Fed Rate Hike', 'Earnings Season', 'Inflation Report', 'Year-End Rally']

# Create Plotly figure
fig = go.Figure()
fig.add_trace(go.Scatter(
    x=df_stock['Date'],
    y=index,
    mode='lines',
    name='Market Index',
    line=dict(color='navy', width=2)
))

# Add shaded recession (example)
fig.add_vrect(
    x0="2023-06-15", x1="2023-08-15",
    fillcolor="lightgray", opacity=0.3,
    layer="below", line_width=0,
    annotation_text="Market Correction",
    annotation_position="top left"
)

# Add annotations for events
for date, label in zip(event_dates, event_labels):
    # Find the closest date in the index
    idx = df_stock['Date'].searchsorted(pd.to_datetime(date))
    if idx < len(df_stock):
        y_val = index[idx]
        fig.add_annotation(
            x=date,
            y=y_val,
            text=label,
            showarrow=True,
            arrowhead=2,
            ax=0,
            ay=-40,
            font=dict(size=10)
        )

fig.update_layout(
    title='Market Index with Key Events Annotated',
    xaxis_title='Date',
    yaxis_title='Index Level',
    template='plotly_white',
    height=500
)
fig.write_html('time_series_annotated.html')
print("Annotated time-series saved as 'time_series_annotated.html'")

# ----------------------------------------------------------------
# PART F: INTERACTIVE DASHBOARD (CONCEPTUAL)
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART F: Interactive Dashboard Concept")
print("-"*60)

print("""
Interactive Dashboard Components:

1. KPIs:
   - Total Portfolio Value: $1.2B
   - YTD Return: 8.5%
   - Volatility: 12.3%
   - Sharpe Ratio: 1.2

2. Time-Series Chart:
   - Portfolio performance vs benchmark (S&P 500)
   - Filter by date range (slider)

3. Sector Allocation:
   - Donut chart showing sector weights
   - Drill-down to individual holdings

4. Risk Metrics:
   - VaR (95%, 99%) - daily, 10-day
   - Expected Shortfall
   - Stress test results

5. ESG Dashboard:
   - Overall ESG score
   - Carbon footprint
   - Controversies count

6. Scenario Analysis:
   - What-if sliders for interest rates, GDP growth, oil prices
   - Impact on portfolio value

7. Alerts:
   - Recent news sentiment
   - Risk threshold breaches
   - Upcoming corporate events

Technology Stack:
  - Frontend: Dash (Plotly) or Streamlit
  - Backend: Python data processing (Pandas, NumPy)
  - Data: Real-time APIs (Bloomberg, Reuters) or batch ETL
  - Hosting: Cloud (AWS, Azure) or on-premise
""")

# ----------------------------------------------------------------
# PART G: STORYTELLING WITH FINANCIAL DATA
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART G: Storytelling with Financial Data")
print("-"*60)

print("""
Storytelling Framework for Finance:

1. Context Setting:
   - "The market has experienced significant volatility over the past year."
   - "Central banks are shifting policy, impacting asset prices."

2. Data Discovery:
   - "We analysed 50,000+ transactions across 10,000 customers."
   - "Our analysis reveals a 15% increase in delinquencies in the last quarter."

3. Visual Evidence:
   - "As shown in this chart, the increase is concentrated in the 30-45 age group."
   - "The correlation between credit score and default is strong (r = -0.75)."

4. Insight Generation:
   - "This suggests that rising rates are disproportionately affecting younger borrowers."
   - "We can mitigate risk by adjusting underwriting criteria for this segment."

5. Recommendation & Action:
   - "We propose tightening LTV requirements for younger applicants."
   - "Implement a targeted communication campaign to offer refinancing options."

6. Call to Action:
   - "Adopt the proposed changes by Q3."
   - "Monitor the segment monthly and report to the Risk Committee."

Example Story:
   "In 2023, our consumer loan portfolio grew by 18%, but we observed a troubling trend: 
   delinquency rates among millennials rose by 22%. Analysis of macroeconomic factors shows 
   that rising rents and student debt are straining this cohort. Using predictive models, 
   we identified that credit card utilisation above 70% is a key early warning signal. 
   We recommend implementing a real-time alert system that triggers when customers 
   exceed this threshold, allowing proactive outreach. This could reduce delinquencies 
   by an estimated 15% and save $5M annually."
""")

# ----------------------------------------------------------------
# PART H: BEST PRACTICES SUMMARY
# ----------------------------------------------------------------

print("\n" + "-"*60)
print("PART H: Data Visualisation Best Practices")
print("-"*60)

best_practices = {
    "Colour": {
        "Guideline": "Use colour purposefully; avoid over-saturation.",
        "Do": "Use blue for stable trends, red for negative, green for positive.",
        "Don't": "Use rainbow colormaps; use colour-blind-friendly palettes."
    },
    "Labels": {
        "Guideline": "Label axes and data points clearly; use titles.",
        "Do": "Include units, percentages, and currency symbols.",
        "Don't": "Leave axes unlabelled or use cryptic abbreviations."
    },
    "Annotations": {
        "Guideline": "Add context with annotations and callouts.",
        "Do": "Highlight key events, outliers, and thresholds.",
        "Don't": "Clutter the chart with excessive text."
    },
    "Scales": {
        "Guideline": "Use appropriate scales; avoid truncation.",
        "Do": "Start y-axis at zero for bar charts; use logarithmic for exponential data.",
        "Don't": "Manipulate scales to exaggerate differences."
    },
    "Interactivity": {
        "Guideline": "Enable exploration with tooltips, zoom, and filtering.",
        "Do": "Use hover tooltips to show detailed values.",
        "Don't": "Overcomplicate with too many interactive elements."
    },
    "Consistency": {
        "Guideline": "Maintain consistent design across dashboards.",
        "Do": "Use a uniform colour palette, font, and layout.",
        "Don't": "Mix different styles in the same report."
    }
}

for category, details in best_practices.items():
    print(f"\n{category}:")
    print(f"  Guideline: {details['Guideline']}")
    print(f"  Do: {details['Do']}")
    print(f"  Don't: {details['Don't']}")

print("\n" + "="*70)
print("END OF BONUS LESSON 8")
print("="*70)

SECTION 7: SUMMARY FOR THE DATA PRACTITIONER

  • Effective visualisation is critical for communicating financial insights.

  • Principles: Clarity, accuracy, efficiency, consistency, context, and emotion.

  • Chart selection depends on data type and the story you want to tell.

  • The grammar of graphics provides a structured way to build visualisations.

  • Interactive dashboards enable exploration and decision-making.

  • Storytelling transforms data into a compelling narrative that drives action.

  • Best practices include thoughtful use of colour, labels, annotations, and consistency.


SECTION 8: RECOMMENDED NEXT STEPS

  1. Build an interactive dashboard for a real financial dataset using Plotly Dash or Streamlit.

  2. Practice creating different chart types for different financial use cases.

  3. Learn advanced Plotly features (subplots, animations, custom layouts).

  4. Study data storytelling techniques from experts (e.g., Cole Nussbaumer Knaflic).

  5. Incorporate visualisation into your daily workflow – always visualise before analysing.