
Altair is a versatile visualization library for Python, based on Vega and Vega-Lite. It allows us to build sophisticated visualizations by layering different chart elements. In this article, we’ll walk through creating a professional financial layered visual, starting with the basics and adding complexity gradually. For those who are new to this package and would like some assistance on setting up your environment and the required dependencies, please check out this postor the official documentation.
Setting Up Our Data
First, we need to prepare our data and set some common properties. This setup block creates a year’s worth of sample stock data and calculates a 20-day moving average that we’ll use later:
import altair as alt
import pandas as pd
import numpy as np
# Create sample data
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
prices = 100 + np.random.randn(len(dates)).cumsum()
volume = np.random.randint(1000, 5000, len(dates))
data = pd.DataFrame({
'date': dates,
'price': prices,
'volume': volume
})
# Calculate moving average for later use
data['MA20'] = data['price'].rolling(window=20).mean()
# Common chart properties
chart_props = {
'width': 700,
'height': 400
}
In this setup:
- We use pandas to create a date range for the entire year of 2023.
- We generate random price data using a cumulative sum of normal distributions, starting at 100.
- We create random volume data between 1000 and 5000.
- We calculate a 20-day moving average of the price.
- We define common chart properties for consistent sizing across our visualizations.
Starting Simple: The Price Line
Let’s begin with a basic line chart showing stock prices over time. Altair makes this straightforward:
basic_chart = alt.Chart(data).mark_line(
color='#1f77b4'
).encode(
x='date:T',
y=alt.Y('price:Q', title='Price ($)', scale=alt.Scale(zero=False))
).properties(**chart_props)
basic_chart
This creates a clean, simple visualization focused solely on price movement. Notice how we:
- Use mark_line() to specify we want a line chart
- Set the x-axis to handle dates (:T for temporal data)
- Configure the y-axis scale to focus on price movements (scale=alt.Scale(zero=False))
- Use common chart properties for consistent sizing
The resulting chart looks like this:

Line chart showing stock price movement from early 2023 to December, with price ranging from 82 to 108. The trend shows a peak around March followed by a general decline through the year | Image by Author
Adding Context: Volume Data
Next, we’ll add trading volume as background bars. This adds another dimension to our analysis:
volume_layer = alt.Chart(data).mark_bar(
color='#2ca02c',
opacity=0.2
).encode(
x='date:T',
y=alt.Y('volume:Q', title='Volume')
)
dual_axis_chart = alt.layer(
volume_layer,
basic_chart
).resolve_scale(
y='independent'
).properties(**chart_props)
dual_axis_chart
Key additions here:
- Create a separate layer for volume bars using mark_bar()
- Use resolve_scale to handle different y-axis scales
- Layer the price line on top of volume bars using alt.layer()
- Maintain independent axes for price and volume
The resulting chart now looks like this:

Combined chart showing stock price (blue line) and trading volume (green bars) from early 2023 to early 2024. The price line mirrors Image 1, while volume fluctuates daily with bars reaching up to 5,000 | Image by Author
Creating the Complete Analysis Visual
Finally, we’ll create our full analysis layered visual by:
- Adding a moving average overlay
- Improving axis formatting
- Fine-tuning the visual presentation
# Create base chart with common settings
def create_axis(title, format=None, is_price=False):
return alt.Axis(
grid=False,
offset=40 if not is_price else 0,
format=format,
labelFontSize=10,
titleFontSize=12
)
base = alt.Chart(data).encode(
x=alt.X('date:T', title='Date', axis=alt.Axis(labelAngle=-45, grid=False))
)
# Create all layers at once
analysis_chart = alt.layer(
# Volume bars
base.mark_bar(opacity=0.15, color='#2ca02c').encode(
y=alt.Y('volume:Q', title='Volume', axis=create_axis('Volume', ',.0f'))
),
# Price line
base.mark_line(color='#1f77b4', strokeWidth=1.5).encode(
y=alt.Y('price:Q', title='Price ($)',
scale=alt.Scale(zero=False),
axis=create_axis('Price ($)', '$.2f', True))
),
# Moving average
base.mark_line(color='#ff7f0e', strokeWidth=1.5, strokeDash=[4, 2]).encode(
y=alt.Y('MA20:Q', axis=None)
)
).resolve_scale(
y='independent'
).properties(
width=700, height=400,
padding={'left': 5, 'right': 50, 'top': 30, 'bottom': 40},
title={
'text': 'Stock Price Analysis',
'subtitle': ['20-day moving average overlay'],
'fontSize': 16
}
).configure_view(stroke=None).configure_axis(
grid=False,
domainColor='#cccccc',
tickColor='#cccccc'
)
analysis_chart
The final version includes several professional touches:
- Helper function create_axis() for consistent axis formatting
- Careful padding to prevent overlap
- Clear title and subtitle
- Thoughtful color choices and opacity levels
- Proper number formatting for both price and volume
- Addition of a 20-day moving average line
The resulting layered visual looks like this:

Stock price analysis chart combining price (blue line), volume (green bars), and a 20-day moving average (orange dotted line) from 2023 to early 2024. The moving average smooths out the price trend, showing the overall downward movement throughout the year | Image by Author
Each step builds on the previous one, showing how Altair’s layered approach lets us create complex visualizations incrementally. By breaking down the process this way, we can focus on one aspect at a time while working toward a polished final product.
This modular approach is one of Altair’s strengths – it allows us to start simple and progressively enhance our visualizations while maintaining clean, readable code. Some key Altair concepts demonstrated in this tutorial include:
- Chart layering with alt.layer()
- Independent axis scaling with resolve_scale()
- Customizing chart properties with properties() and configure_*()
- Using different mark types (line, bar) for various data representations
- Encoding data channels with encode()
- Customizing axes with the alt.Axis() object
By understanding these concepts, you’ll be well on your way to creating sophisticated, interactive visualizations with Altair.
