How to Create a Candlestick Chart in Plotly

Candlestick charts are the standard visualization for financial time series data. Each candlestick represents four critical price points within a time period: open, high, low, and close (OHLC). The...

Key Insights

  • Plotly’s graph_objects.Candlestick() provides a production-ready solution for financial charting with minimal code, handling OHLC data structure natively and rendering interactive visualizations out of the box
  • Proper data preparation is critical—timestamps must be datetime objects, and OHLC columns need numeric types; mismatched data types are the most common source of rendering errors
  • Combining candlestick charts with subplots for volume data and technical indicators like moving averages creates professional-grade financial dashboards that rival expensive commercial solutions

Introduction to Candlestick Charts

Candlestick charts are the standard visualization for financial time series data. Each candlestick represents four critical price points within a time period: open, high, low, and close (OHLC). The body shows the range between open and close prices, while wicks extend to the high and low. Green or white candles indicate price increases; red or black show decreases.

Plotly excels at candlestick visualization because it combines publication-quality graphics with built-in interactivity. Unlike static matplotlib charts, Plotly charts allow users to zoom, pan, and hover for details—essential features when analyzing thousands of data points. The library handles the complex rendering logic internally, letting you focus on data analysis rather than drawing mechanics.

Setup and Data Preparation

Install Plotly using pip. You’ll also want pandas for data manipulation:

pip install plotly pandas

Financial data requires specific structure. You need a datetime index or column plus four numeric columns for OHLC values. Here’s how to create properly formatted sample data:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

# Generate sample stock data
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', end='2024-03-31', freq='D')
n = len(dates)

# Simulate realistic price movements
open_price = 100
prices = [open_price]

for i in range(n-1):
    change = np.random.normal(0, 2)
    prices.append(prices[-1] + change)

df = pd.DataFrame({
    'Date': dates,
    'Open': prices,
    'High': [p + abs(np.random.normal(0, 1)) for p in prices],
    'Low': [p - abs(np.random.normal(0, 1)) for p in prices],
    'Close': [p + np.random.normal(0, 0.5) for p in prices]
})

# Ensure High is actually the highest and Low is the lowest
df['High'] = df[['Open', 'High', 'Close']].max(axis=1)
df['Low'] = df[['Open', 'Low', 'Close']].min(axis=1)

print(df.head())

This creates realistic-looking price data with proper relationships between OHLC values. The high must be the maximum and low the minimum—Plotly won’t validate this, but incorrect data produces misleading charts.

Creating a Basic Candlestick Chart

Plotly’s graph_objects module provides the Candlestick class. The basic implementation requires just five parameters:

import plotly.graph_objects as go

fig = go.Figure(data=[go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close']
)])

fig.update_layout(
    title='Stock Price Candlestick Chart',
    xaxis_title='Date',
    yaxis_title='Price ($)',
    height=600
)

fig.show()

This code produces a fully interactive chart with default styling. Plotly automatically colors increasing candles green and decreasing candles red. The x-axis handles datetime formatting, and hovering displays all OHLC values.

The fig.show() method opens the chart in your browser. For Jupyter notebooks, the chart renders inline. For production dashboards, use fig.write_html('chart.html') to save standalone HTML files.

Customizing Chart Appearance

Default styling works for quick analysis, but production charts need customization. Control colors, layout, and axes to match your application’s design:

fig = go.Figure(data=[go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close'],
    increasing_line_color='#2ECC71',
    decreasing_line_color='#E74C3C',
    increasing_fillcolor='#2ECC71',
    decreasing_fillcolor='#E74C3C'
)])

fig.update_layout(
    title={
        'text': 'Stock Price Analysis',
        'font': {'size': 24, 'color': '#2C3E50'}
    },
    xaxis={
        'title': 'Date',
        'gridcolor': '#ECF0F1',
        'showgrid': True
    },
    yaxis={
        'title': 'Price ($)',
        'gridcolor': '#ECF0F1',
        'showgrid': True
    },
    plot_bgcolor='white',
    paper_bgcolor='white',
    height=600,
    margin=dict(l=50, r=50, t=80, b=50)
)

# Remove rangeslider for cleaner appearance
fig.update_xaxes(rangeslider_visible=False)

fig.show()

The increasing_line_color and decreasing_line_color parameters control candle borders, while fillcolor variants set body colors. Setting both ensures consistency across the chart.

Layout customization through update_layout() provides fine-grained control. The plot_bgcolor sets the chart area color, while paper_bgcolor controls the surrounding space. Proper margins prevent label clipping on different screen sizes.

Adding Technical Indicators

Candlestick charts become powerful when combined with technical indicators. Moving averages are the most common overlay:

# Calculate 20-day simple moving average
df['SMA_20'] = df['Close'].rolling(window=20).mean()

# Create figure with candlestick and SMA
fig = go.Figure()

fig.add_trace(go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close'],
    name='Price'
))

fig.add_trace(go.Scatter(
    x=df['Date'],
    y=df['SMA_20'],
    mode='lines',
    name='20-Day SMA',
    line=dict(color='#3498DB', width=2)
))

fig.update_layout(
    title='Price with Moving Average',
    yaxis_title='Price ($)',
    height=600,
    showlegend=True
)

fig.update_xaxes(rangeslider_visible=False)
fig.show()

Adding volume data requires subplots. Use make_subplots to create vertically stacked charts:

from plotly.subplots import make_subplots

# Generate sample volume data
df['Volume'] = np.random.randint(1000000, 5000000, size=len(df))

# Create subplots with 2 rows
fig = make_subplots(
    rows=2, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.03,
    row_heights=[0.7, 0.3],
    subplot_titles=('Price', 'Volume')
)

# Add candlestick to first subplot
fig.add_trace(
    go.Candlestick(
        x=df['Date'],
        open=df['Open'],
        high=df['High'],
        low=df['Low'],
        close=df['Close'],
        name='Price'
    ),
    row=1, col=1
)

# Add volume bars to second subplot
colors = ['#2ECC71' if close >= open else '#E74C3C' 
          for close, open in zip(df['Close'], df['Open'])]

fig.add_trace(
    go.Bar(
        x=df['Date'],
        y=df['Volume'],
        marker_color=colors,
        name='Volume',
        showlegend=False
    ),
    row=2, col=1
)

fig.update_layout(
    height=800,
    showlegend=False,
    xaxis_rangeslider_visible=False
)

fig.update_yaxes(title_text='Price ($)', row=1, col=1)
fig.update_yaxes(title_text='Volume', row=2, col=1)

fig.show()

The row_heights parameter allocates 70% of space to price and 30% to volume—a standard ratio in financial charting. Coloring volume bars based on price direction (green for up days, red for down days) provides visual correlation.

Making Charts Interactive

Plotly’s interactivity distinguishes it from static charting libraries. Range sliders let users focus on specific time periods:

fig = go.Figure(data=[go.Candlestick(
    x=df['Date'],
    open=df['Open'],
    high=df['High'],
    low=df['Low'],
    close=df['Close']
)])

fig.update_layout(
    title='Interactive Stock Chart',
    xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=7, label='1W', step='day', stepmode='backward'),
                dict(count=1, label='1M', step='month', stepmode='backward'),
                dict(count=3, label='3M', step='month', stepmode='backward'),
                dict(step='all', label='All')
            ])
        ),
        rangeslider=dict(visible=True),
        type='date'
    ),
    height=600
)

fig.show()

The rangeselector adds buttons for quick time period selection. Users click “1M” to view the last month or “All” for the complete dataset. The rangeslider provides a miniature chart for navigation.

Customize hover information to display exactly what users need:

fig.update_traces(
    hovertemplate='<b>Date</b>: %{x|%Y-%m-%d}<br>' +
                  '<b>Open</b>: $%{open:.2f}<br>' +
                  '<b>High</b>: $%{high:.2f}<br>' +
                  '<b>Low</b>: $%{low:.2f}<br>' +
                  '<b>Close</b>: $%{close:.2f}<br>' +
                  '<extra></extra>'
)

The <extra></extra> tag removes the secondary box that Plotly adds by default. Format strings like %{open:.2f} control decimal precision.

Conclusion and Best Practices

Plotly makes professional candlestick charts accessible with minimal code. Start with basic OHLC data, add the Candlestick trace, and customize from there. The library handles complex rendering details while providing extensive customization options.

For production applications, consider these best practices:

Performance optimization: Plotly handles thousands of data points smoothly, but charts with 10,000+ candles may lag. Use scattergl mode for large datasets or implement server-side filtering to load data on demand.

Data validation: Always validate that high values are actually the highest and low values are the lowest for each period. Invalid data produces confusing charts that mislead users.

Responsive design: Set height explicitly and use percentage-based widths when embedding charts in web applications. Test on mobile devices—Plotly’s touch interactions work well but may need layout adjustments for small screens.

Color accessibility: The default green/red scheme works for most users but consider colorblind-friendly alternatives like blue/orange. Plotly’s customization makes this trivial to implement.

Candlestick charts are essential for financial analysis, trading platforms, and any application displaying price movements over time. Plotly’s combination of ease-of-use and powerful features makes it the best choice for both quick analysis and production dashboards. The interactive capabilities transform static data into explorable visualizations that reveal patterns and trends impossible to spot in spreadsheets.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.