Designing a Basic Visual Plot

By Yeo Yong Kiat in Finanalytics

5 min read
Much of technical analysis is staring at charts, not at figures. Few traders can make trading decisions based solely on a table of historical price data - few humans can, in fact. When deciding on a visualisation tool, I recommend opting for something that allows you to interact with the data, minimally through hover-on tooltips. One such Python library which integrates with D3.js and JavaScript is Plotly. (Although I'll be explaining the mechanics of the Python Plotly library, the graphs on this page are generated via the JavaScript Plotly library.)

Installation

If you haven't installed Plotly, key in the following command in your terminal for a quick installation:

pip install plotly

Once installed, load up Plotly together with other relevant libraries in your Python code:

## Loading Libraries
import plotly.express as px
import pandas as pd
import yfinance as yf

Designing the End Product: A Basic Line Chart

To help you see what we're aiming for, I've coded a final product below for you to explore first. A line chart is the most basic of charts used for analysis - however, by plotting the daily high and low prices with time, one minimally obtains a sense of the price band variation of a stock. We'll look at how we can create an interactive line chart with a 1-month and 6-month look-back function for the TSLA stock price via Plotly.

Accessing Stock Price Data

Start off by accessing the stock price data you're interested in from yfinance:

## Accessing stock price data from yfinance
tsla_df=yf.download(
    tickers="TSLA", 
    period="ytd",     
    interval="1d",    
    progress=False,   
    auto_adjust=True  
)

## Change the "date" index into a "date" column
tsla_df = tsla_df.reset_index()

Create a Plotly Chart Object

Then proceed to create a Plotly chart object and load your data in. We will start by creating only one line that corresponds to the daily low price.

## Creating the Plotly chart object
# Step 1 - Creating the general chart object 
fig = px.line(          # specify type of chart
    tsla_df,            # specify dataframe
    x="Date",           # specify column for x axis
    y="Low",          # specify column for y axis
    title="Tesla Stock Price (Year to Date)"
)

# Step 2 - Creating the traces of the chart (i.e. line)
fig.update_traces(
    line_width=2,
    line_color="black"
)

# Step 3 - Setting the layout of the chart
fig.update_layout(
    xaxis_title="Date", 
    xaxis_gridcolor="lightgrey",
    yaxis_title="Price",
    yaxis_gridcolor="lightgrey",
    
    font_color="black",
    
    title_font_size=20,
    title_x=0.5,
    paper_bgcolor='white',
    plot_bgcolor='white'
)

# Step 4 - Generate chart in browser
fig.show()

## Alternatively, write to a HTML file
fig.write_html("tsla-ytd-closing-prices.html")

You should obtain an output like the one below. I recommend always starting with a simple black and white design, with just a single line, so that you can focus on any patterns you may want to highlight in your end product.

Adding Further Data to Plotly Object

This is so important, I've dedicated a single section for it. Once the basic Plotly object is created, you can add traces by using the ".add_trace()" method.

# Step 5 - Create another Plotly object and load in data
fig2 = px.line(          
      tsla_df,            
      x="Date",           
      y="High"
)

# Step 6 - Add the data from fig2 as another 
# trace in the original fig
fig.add_trace(fig2.data[0])

This should another line to your plot. And because we pre-empted the default colour blue, we had earlier defined the colour of the first line to be black.

Formatting the Chart

We will now need to format the chart. In simple terms, we will do the following:

  • Layout Changes
    • Change background colour to black
    • Change font colour to white
    • Bold the title and axis labels
    • Change the title
  • Trace Changes
    • Customise the colour of the lines
    • Show the legend for each line and set their names

From the above list of changes, notice how I've separated the necessary changes into "Layout Changes" and "Trace Changes". I will illustrate two ways to make formatting changes:

  • Trace changes will be accessed individually for each line using individual properties of the Plotly object.
  • Layout changes will be accessed in bulk via the "update_layout()" method
You'll see what I mean below, although both methods are interchangeable:

# Step 7 - Update trace changes
# Notice how we extract out the datasets individually, and then
# accessing their trace property individually as well
fig.data[0].line.color = "#17BECF"
fig.data[1].line.color = "#7F7F7F"

fig['data'][0]['showlegend']=True
fig['data'][1]['showlegend']=True

fig['data'][0]['name']='Daily Low'
fig['data'][1]['name']='Daily High'

# Step 8a - Actually you can also access layout properties individually
fig.layout.legend.font.color = 'white'

# Step 8b - Update layout through the "update_layout" method
fig.update_layout(
  xaxis_title="<b>Date</b>", 
  xaxis_gridcolor="#272822",
  xaxis_title_font_color="white",

  yaxis_title="<b>Price</b>t",
  yaxis_gridcolor="#272822",
  yaxis_title_font_color="white",

  title="<b>Tesla Stock Price (YTD, 15 Jun 2022)</b>",
  title_font_color="white",
  title_x=0.5,

  paper_bgcolor="#272822",
  plot_bgcolor="#272822"
)

Nicely done! This should have given you the following chart:

Adding Chart Functions

Plotly allows us to code for additional chart functions through the use of buttons. Let's try to code a "1-month" and "6-month" lookback, and another button to reset the chart to show all data. We do so using the "update_layout()" method again, and we call in what we call a "rangeselector" (which only works with "date"-type axes):

# Step 9 - Code in a range selector
fig.update_layout(
  xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="YTD",
                     step="year",
                     stepmode="todate"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),

        ## shows a useful slider at bottom of chart
        rangeslider=dict(
            visible=True
        )
  )
)

And this should give you some nice range selection buttons to play with:

Try playing around with the Plotly documentation a bit by yourself, and you'll realise that there are a ton more functions you can code into your chart. We'll explore more of these in posts to come.

Here's the entire Plotly chart coded in Python, for your convenience:

## Loading Libraries
import plotly.express as px
import pandas as pd
import yfinance as yf

## Accessing stock price data from yfinance
tsla_df=yf.download(
    tickers="TSLA", 
    period="ytd",     
    interval="1d",    
    progress=False,   
    auto_adjust=True  
)

## Change the "date" index into a "date" column
tsla_df = tsla_df.reset_index()

## Creating the Plotly chart object
# Step 1 - Creating the general chart object 
fig = px.line(          # specify type of chart
    tsla_df,            # specify dataframe
    x="Date",           # specify column for x axis
    y="Low",          # specify column for y axis
    title="Tesla Stock Price (Year to Date)"
)

# Step 2 - Creating the traces of the chart (i.e. line)
fig.update_traces(
    line_width=2,
    line_color="black"
)

# Step 3 - Setting the layout of the chart
fig.update_layout(
    xaxis_title="Date", 
    xaxis_gridcolor="lightgrey",
    yaxis_title="Price",
    yaxis_gridcolor="lightgrey",
    
    font_color="black",
    
    title_font_size=20,
    title_x=0.5,
    paper_bgcolor='white',
    plot_bgcolor='white'
)

# Step 4 - Generate chart in browser
fig.show()

# Step 5 - Create another Plotly object and load in data
fig2 = px.line(          
      tsla_df,            
      x="Date",           
      y="High"
)

# Step 6 - Add the data from fig2 as another trace in 
# the original fig
fig.add_trace(fig2.data[0])

# Step 7 - Update trace changes
# Notice how we extract out the datasets individually, and then
# accessing their trace property individually as well
fig.data[0].line.color = "#17BECF"
fig.data[1].line.color = "#7F7F7F"

fig['data'][0]['showlegend']=True
fig['data'][1]['showlegend']=True

fig['data'][0]['name']='Daily Low'
fig['data'][1]['name']='Daily High'

# Step 8a - Actually you can also access layout properties individually
fig.layout.legend.font.color = 'white'

# Step 8b - Update layout through the "update_layout" method
fig.update_layout(
  xaxis_title="Date", 
  xaxis_gridcolor="#272822",
  xaxis_title_font_color="white",

  yaxis_title="Pricet",
  yaxis_gridcolor="#272822",
  yaxis_title_font_color="white",

  title="Tesla Stock Price (YTD, 15 Jun 2022)",
  title_font_color="white",
  title_x=0.5,

  paper_bgcolor="#272822",
  plot_bgcolor="#272822"
)

# Step 9 - Code in a range selector
fig.update_layout(
  xaxis=dict(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="YTD",
                     step="year",
                     stepmode="todate"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),

        ## shows a useful slider at bottom of chart
        rangeslider=dict(
            visible=True
        )
  )
)

# Step 10 - write to a HTML file
fig.write_html("tsla-ytd-closing-prices.html")
Mhm... how do we compare different stock prices?

Find more Finalytics stories on my blog. Have a suggestion? Contact me at [email protected].