Chances are, you're going to be looking at more than one stock or financial asset - surely you believe in diversification, even for trading? In this post, we're going to work off our previous post on basic line charts, and explore a nifty way to toggle the price trends of several stocks through a simple dropdown menu. This will prove very useful when presenting a set of charts for different stocks.
Let's take a look at how a drop-down could make it more convenient for us to analyse the prices of various stocks. As usual, I show the end product first so you get a better sense of what we're working towards. In the chart below, you have the option to choose between "All Stocks", "TSLA" or "NIO", and the price data curves update accordingly. For continuity, we work off the same chart with the slider function at the bottom, from our previous post.
Let's initiate the Python header with the important libraries and call for the stock prices of TSLA and NIO, two very popular electric vehicle companies. However, this time round, we will be using the "plotly.graph_objects" module. The key difference between both modules is that Plotly Express is a high-level wrapper for Plotly, meaning that you can do a lot of things with it with a much simpler syntax. For more complicated functions, we'll have to rely on the main Plotly modules, like "plotly.graph_objects".
## Loading Libraries
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import yfinance as yf
## Generating ticker objects
TSLA = yf.Ticker("TSLA")
NIO = yf.Ticker("NIO")
## Accessing stock price data
TSLA_df = TSLA.history(period="ytd")
NIO_df = NIO.history(period="ytd")
## Extracting only the daily high prices
## into a new dataframe
stocks_high = pd.DataFrame({
'TSLA': TSLA_df['High'],
'NIO': NIO_df['High']
})
## Extracting only the daily low prices
## into a new dataframe
stocks_low = pd.DataFrame({
'TSLA': TSLA_df['Low'],
'NIO': NIO_df['Low']
})
Check that your "stocks_high" and "stocks_low" dataframes give the following output:
Output for stocks_high.head(): TSLA NIO Date 2022-01-03 1201.069946 33.799999 2022-01-05 1170.339966 31.940001 2022-01-06 1088.000000 30.540001 2022-01-07 1080.930054 30.420000 Output for stocks_low.head(): TSLA NIO Date 2022-01-03 1136.040039 31.879999 2022-01-04 1123.050049 31.110001 2022-01-05 1081.010010 29.780001 2022-01-06 1020.500000 28.280001 2022-01-07 1010.000000 28.780001
With the "plotly.graph_objects" module, we can now create an empty chart Object via the "Figure()" method. After which we can then load in our data of high and low stock prices for each of the companies.
## Generating an empty Plotly chart object
fig = go.Figure()
## Adding high prices data into object
## Note: Dataset 1 = TSLA High, Dataset 2 = NIO High
for column in stocks_high.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_high.index,
y = stocks_high[column],
name = "Daily High" # legend name
)
)
## Adding low prices data into object
## Note: Dataset 3 = TSLA Low, Dataset 4 = NIO Low
for column in stocks_low.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_low.index,
y = stocks_low[column],
name = "Daily Low" # legend name
)
)
Next comes the fun bit - creating your dropdown! A dropdown belongs to the "updatemenus" portion of the "update_layout()" method. I've provided most of the explanation in comments below, but I want to focus on the "args" key.value pair in the dictionary. Remember how you loaded in TSLA/NIO High datasets followed by TSLA/NIO Low datasets? This means that the order of your datasets are [TSLA High, NIO High, TSLA Low, NIO Low]. The boolean list you see in the "args" key.value pair below corresponds to which dataset you want the Plotly chart to display when the corresponding label is clicked.
## Adding a dropdown list
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
## You want three buttons labelled "All Stocks", "TSLA" and "NIO"
## so create three dictionaries with three separate "label" key-value pairs.
## Also add in an "update" value under the "method" key, so that
## Plotly knows that you want to update the data in your charts.
buttons=list(
[dict(label = 'All Stocks',
method = 'update',
args = [{'visible': [True, True, True, True]},
{'title': '<b>YTD Price Data for Various Stocks</b>',
'showlegend':True}]),
dict(label = 'TSLA',
method = 'update',
args = [{'visible': [True, False, True, False]},
{'title': '<b>YTD TSLA Stock Price Data</b>',
'showlegend':True}]),
dict(label = 'NIO',
method = 'update',
args = [{'visible': [False, True, False, True]},
{'title': '<b>YTD NIO Stock Price Data</b>',
'showlegend':True}])
]),
## Positioning your dropdown along x-axis
## Play around with these
x=0.01,
xanchor="left",
## Positioning your dropdown along y-axis
## Play around with these
y=1.2,
yanchor="top"
)]
)
fig.update_layout(
title="YTD Price Data for Various Stocks",
title_x=0.5
)
## Display the chart
fig.show()
If you've been following thus far, you should have gotten the following Plotly output. Play around with it first, we'll tweak it further later.
Let's see how else we can improve the chart. Other than the standard formatting, it'd be useful to consider the following:
With this, we can go ahead with the formatting - here I like to tap on Plotly Express:
## Changing the daily highs and lows to
## grey dotted lines, and removing their legend
fig.update_traces(
line_width=2,
line_color="lightgrey",
line_dash="dash",
showlegend=False
)
## Formatting the chart area
fig.update_layout(
xaxis_title="<b>Date</b>",
yaxis_title="<b>Price</b>",
title="<b>YTD Price Data for Various Stocks</b>",
title_x=0.5,
title_font_size=30,
paper_bgcolor="white",
plot_bgcolor="white"
)
## Calculating the daily average prices
## into a new dataframe for loading
stocks_ave = pd.DataFrame({
'TSLA': (TSLA_df["Low"] + TSLA_df["High"]) / 2,
'NIO': (NIO_df["Low"] + NIO_df["High"]) / 2
})
## Adding ave prices data into object
for column in stocks_ave.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_ave.index,
y = stocks_ave[column],
name = column + " Daily Ave"
)
)
## Adding a date 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")
])
),
rangeslider=dict(
visible=True
)
)
)
fig.show()
You should end up with the chart you saw at the very beginning of this article:
One thing to consider is the possibly large number of stocks you might wish to monitor and study. It's worth considering how to implement the above procedure in the form of a loop over all the stocks. We'll come back to this in a future article. Again, the full Python implementation is below for your convenience:
## Loading Libraries
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import yfinance as yf
## Generating ticker objects
TSLA = yf.Ticker("TSLA")
NIO = yf.Ticker("NIO")
## Accessing stock price data
TSLA_df = TSLA.history(period="ytd")
NIO_df = NIO.history(period="ytd")
## Extracting only the daily high prices
## into a new dataframe
stocks_high = pd.DataFrame({
'TSLA': TSLA_df['High'],
'NIO': NIO_df['High']
})
## Extracting only the daily low prices
## into a new dataframe
stocks_low = pd.DataFrame({
'TSLA': TSLA_df['Low'],
'NIO': NIO_df['Low']
})
)
## Generating an empty Plotly chart object
fig = go.Figure()
## Adding high prices data into object
## Note: Dataset 1 = TSLA High, Dataset 2 = NIO High
for column in stocks_high.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_high.index,
y = stocks_high[column],
name = "Daily High" # legend name
)
)
## Adding low prices data into object
## Note: Dataset 3 = TSLA Low, Dataset 4 = NIO Low
for column in stocks_low.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_low.index,
y = stocks_low[column],
name = "Daily Low" # legend name
)
)
## Adding a dropdown list
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
## You want three buttons labelled "All Stocks", "TSLA" and "NIO"
## so create three dictionaries with three separate "label" key-value pairs.
## Also add in an "update" value under the "method" key, so that
## Plotly knows that you want to update the data in your charts.
buttons=list(
[dict(label = 'All Stocks',
method = 'update',
args = [{'visible': [True, True, True, True]},
{'title': 'YTD Price Data for Various Stocks',
'showlegend':True}]),
dict(label = 'TSLA',
method = 'update',
args = [{'visible': [True, False, True, False]},
{'title': 'YTD TSLA Stock Price Data',
'showlegend':True}]),
dict(label = 'NIO',
method = 'update',
args = [{'visible': [False, True, False, True]},
{'title': 'YTD NIO Stock Price Data',
'showlegend':True}])
]),
## Positioning your dropdown along x-axis
## Play around with these
x=0.01,
xanchor="left",
## Positioning your dropdown along y-axis
## Play around with these
y=1.2,
yanchor="top"
)]
)
fig.update_layout(
title="YTD Price Data for Various Stocks",
title_x=0.5
)
## Changing the daily highs and lows to
## grey dotted lines, and removing their legend
fig.update_traces(
line_width=2,
line_color="lightgrey",
line_dash="dash",
showlegend=False
)
## Formatting the chart area
fig.update_layout(
xaxis_title="Date",
yaxis_title="Price",
title="YTD Price Data for Various Stocks",
title_x=0.5,
title_font_size=30,
paper_bgcolor="white",
plot_bgcolor="white"
)
## Calculating the daily average prices
## into a new dataframe for loading
stocks_ave = pd.DataFrame({
'TSLA': (TSLA_df["Low"] + TSLA_df["High"]) / 2,
'NIO': (NIO_df["Low"] + NIO_df["High"]) / 2
})
## Adding ave prices data into object
for column in stocks_ave.columns.to_list():
fig.add_trace(
go.Scatter(
x = stocks_ave.index,
y = stocks_ave[column],
name = column + " Daily Ave"
)
)
## Adding a date 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")
])
),
rangeslider=dict(
visible=True
)
)
)
fig.show()
All fine and good, but how do we produce candlestick charts, those used by the pros?
Find more Finalytics stories on my blog. Have a suggestion? Contact me at [email protected].