Financial Data Visualisation in Python
Build professional-looking charts for stock prices, portfolio returns, and market data with matplotlib.
Why Visualisation Matters in Finance
Numbers in a spreadsheet tell you what happened. Charts tell you the story. A well-designed chart can reveal trends, outliers, and correlations that are invisible in raw data. In finance, visualisation is not decoration - it is analysis.
Python's matplotlib is the foundation of data visualisation in the Python ecosystem. It is not the prettiest library out of the box, but with the right styling it produces publication-quality charts. For candlestick charts specifically, mplfinance is purpose-built.
pip install matplotlib pandas numpy mplfinanceLine Charts: Share Price Over Time
The line chart is your bread and butter for time series data. Here we create a dark-themed chart that would not look out of place on a Bloomberg terminal. The key is setting the figure and axes background colours, using muted grid lines, and picking an accent colour that pops.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# Generate sample stock data
dates = pd.date_range('2024-01-01', periods=252, freq='B')
np.random.seed(42)
price = 100 + np.cumsum(np.random.randn(252) * 1.5)
# Create the figure with a dark background
fig, ax = plt.subplots(figsize=(12, 6))
fig.patch.set_facecolor('#0c0c14')
ax.set_facecolor('#14141e')
# Plot the price line
ax.plot(dates, price, color='#ff8c00', linewidth=1.5, label='ACME Corp')
# Style the chart
ax.set_title('ACME Corp Share Price (2024)', color='white', fontsize=16, pad=15)
ax.set_xlabel('Date', color='#888888')
ax.set_ylabel('Price (GBP)', color='#888888')
ax.tick_params(colors='#888888')
ax.grid(True, alpha=0.2, color='#333333')
ax.legend(facecolor='#14141e', edgecolor='#333333', labelcolor='white')
# Clean up spines
for spine in ax.spines.values():
spine.set_color('#333333')
plt.tight_layout()
plt.savefig('stock_chart.png', dpi=150, bbox_inches='tight')
plt.show()Bar Charts: Monthly Returns
Bar charts are perfect for comparing discrete values like monthly returns or sector performance. Colour-coding positive returns green and negative returns red is a finance convention that makes charts instantly readable.
import matplotlib.pyplot as plt
import numpy as np
# Monthly portfolio returns
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
returns = [2.1, -0.8, 3.5, 1.2, -1.5, 4.2,
-0.3, 2.8, -2.1, 3.7, 1.9, 2.5]
# Colours: green for positive, red for negative
colours = ['#22c55e' if r >= 0 else '#ef4444' for r in returns]
fig, ax = plt.subplots(figsize=(12, 6))
fig.patch.set_facecolor('#0c0c14')
ax.set_facecolor('#14141e')
ax.bar(months, returns, color=colours, width=0.6, edgecolor='none')
# Add a zero line
ax.axhline(y=0, color='#555555', linewidth=0.8)
# Style
ax.set_title('Monthly Portfolio Returns (%)', color='white', fontsize=16, pad=15)
ax.set_ylabel('Return (%)', color='#888888')
ax.tick_params(colors='#888888')
ax.grid(True, axis='y', alpha=0.2, color='#333333')
for spine in ax.spines.values():
spine.set_color('#333333')
plt.tight_layout()
plt.savefig('monthly_returns.png', dpi=150, bbox_inches='tight')
plt.show()Candlestick Charts
Candlestick charts show open, high, low, and close (OHLC) prices in a single view. Each candle tells you whether the price went up (green) or down (red) during that period, and the wicks show the full range. The mplfinance library handles all the complexity for you.
import mplfinance as mpf
import pandas as pd
import numpy as np
# Generate sample OHLCV data
dates = pd.date_range('2024-01-01', periods=60, freq='B')
np.random.seed(42)
close = 100 + np.cumsum(np.random.randn(60) * 1.5)
open_price = close + np.random.randn(60) * 0.5
high = np.maximum(open_price, close) + abs(np.random.randn(60) * 1.0)
low = np.minimum(open_price, close) - abs(np.random.randn(60) * 1.0)
volume = np.random.randint(100000, 1000000, 60)
df = pd.DataFrame({
'Open': open_price, 'High': high,
'Low': low, 'Close': close,
'Volume': volume
}, index=dates)
# Define a dark theme
dark_style = mpf.make_mpf_style(
base_mpf_style='nightclouds',
marketcolors=mpf.make_marketcolors(
up='#22c55e', down='#ef4444',
edge='inherit', wick='inherit',
volume='#555555'
),
facecolor='#0c0c14',
figcolor='#0c0c14',
gridcolor='#1e1e30',
gridstyle='--'
)
mpf.plot(df, type='candle', style=dark_style,
volume=True, title='ACME Corp - 60 Day Candles',
savefig=dict(fname='candlestick.png', dpi=150))Multiple Y-Axes: Price and Volume
When you need to overlay two datasets with different scales - like price and volume - use twinx() to create a second y-axis on the right. This is one of the most common chart types in financial analysis.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# Sample data: price and volume
dates = pd.date_range('2024-01-01', periods=90, freq='B')
np.random.seed(42)
price = 100 + np.cumsum(np.random.randn(90) * 1.2)
volume = np.random.randint(50000, 500000, 90)
fig, ax1 = plt.subplots(figsize=(12, 6))
fig.patch.set_facecolor('#0c0c14')
ax1.set_facecolor('#14141e')
# Left axis: Price
ax1.plot(dates, price, color='#ff8c00', linewidth=1.5, label='Price')
ax1.set_ylabel('Price (GBP)', color='#ff8c00')
ax1.tick_params(axis='y', labelcolor='#ff8c00')
ax1.tick_params(axis='x', colors='#888888')
# Right axis: Volume
ax2 = ax1.twinx()
ax2.bar(dates, volume, color='#22c55e', alpha=0.3, width=1, label='Volume')
ax2.set_ylabel('Volume', color='#22c55e')
ax2.tick_params(axis='y', labelcolor='#22c55e')
# Style
ax1.set_title('Price vs Volume', color='white', fontsize=16, pad=15)
ax1.grid(True, alpha=0.2, color='#333333')
for spine in ax1.spines.values():
spine.set_color('#333333')
for spine in ax2.spines.values():
spine.set_color('#333333')
# Combined legend
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2,
facecolor='#14141e', edgecolor='#333333', labelcolor='white')
plt.tight_layout()
plt.savefig('price_volume.png', dpi=150, bbox_inches='tight')
plt.show()Key Takeaways
- - Set facecolor on both the figure and axes for consistent dark themes
- - Use colour conventions: green for positive, red for negative, orange for accents
- - mplfinance handles candlestick charts with minimal code
- - twinx() lets you overlay two different scales on one chart
- - Always call tight_layout() and set dpi=150+ when saving to PNG
- - For interactive dashboards, look into Plotly or Dash as your next step