Trading Dashboard with Yfinance & Python.
By: Scott Andersen
Tags: Python, Dashboard, Stock Analysis, API, Finance

Beginner level coding with advanced techniques.

Table of Contents:

Pull Data with Yfinance Api

Set the Short and Long windows (SMA)

Generate trading signals

Plot Entry/Exit points

Backtest

Analyze Portfolio metrics

Serve Dashboard

Introduction

To begin, let’s first understand the goal of this article, which is to provide the average retail investor with a quick and easy way to pull live data, use that data to highlight key indicators and create a nice clean readable table before investing in a particular company(s).

This process will help you take emotion out of the equation and give you enough information to make informed decisions.

Substitute any stock ticker you would like at the bottom of the code block:

# Import libraries and dependencies

import numpy as np

import pandas as pd

import hvplot.pandas

from pathlib import Path

import yfinance as yf #Cloudflare

net = yf.Ticker(“net”)

net # Set the timeframe you are interested in viewing. net_historical = net.history(start="2018-01-2", end="2020-12-11", interval="1d") # Create a new DataFrame called signals, keeping only the 'Date' & 'Close' columns. signals_df = net_historical.drop(columns=['Open', 'High', 'Low', 'Volume','Dividends', 'Stock Splits'])



Moving Averages:

Next, we want to create columns for the short and long windows, also known as the simple moving averages. In this case, we will be using the 50-day and the 100-day averages.

In the code below we will need to set the trading signals as 0 or 1. This will tell python at which points we should Buy or Sell a position.

Keep in mind when the SMA50 crosses above the SMA100 or resistance level, this is a bullish breakout signal.

# Set the short window and long windows

short_window = 50

long_window = 100 # Generate the short and long moving averages (50 and 100 days, respectively)

signals_df['SMA50'] = signals_df['Close'].rolling(window=short_window).mean()

signals_df['SMA100'] = signals_df['Close'].rolling(window=long_window).mean()

signals_df['Signal'] = 0.0 # Generate the trading signal 0 or 1,

# where 0 is when the SMA50 is under the SMA100, and

# where 1 is when the SMA50 is higher (or crosses over) the SMA100

signals_df['Signal'][short_window:] = np.where(

signals_df['SMA50'][short_window:] > signals_df['SMA100'][short_window:], 1.0, 0.0

) # Calculate the points in time at which a position should be taken, 1 or -1

signals_df['Entry/Exit'] = signals_df['Signal'].diff() # Print the DataFrame

signals_df.tail(10)

The third step towards building our dashboard is creating a chart with green and red signal markers for Entry / Exit indicators.

Plotting the Moving Averages with HvPlot:

# Visualize exit position relative to close price exit = signals_df[signals_df['Entry/Exit'] == -1.0]['Close'].hvplot.scatter(

color='red',

legend=False,

ylabel='Price in $',

width=1000,

height=400

) # Visualize entry position relative to close price entry = signals_df[signals_df['Entry/Exit'] == 1.0]['Close'].hvplot.scatter(

color='green',

legend=False,

ylabel='Price in $',

width=1000,

height=400

) # Visualize close price for the investment security_close = signals_df[['Close']].hvplot(

line_color='lightgray',

ylabel='Price in $',

width=1000,

height=400

) # Visualize moving averages moving_avgs = signals_df[['SMA50', 'SMA100']].hvplot(

ylabel='Price in $',

width=1000,

height=400

) # Overlay plots

entry_exit_plot = security_close * moving_avgs * entry * exit

entry_exit_plot.opts(xaxis=None)

Next, we will set an initial investment stake of capital and set the number of shares. For this example, let’s say we want to buy 500 shares of Cloudflare.

# Set initial capital

initial_capital = float(100000) # Set the share size

share_size = 500 # Take a 500 share position where the dual moving average crossover is 1 (SMA50 is greater than SMA100)

signals_df['Position'] = share_size * signals_df['Signal'] # Find the points in time where a 500 share position is bought or sold

signals_df['Entry/Exit Position'] = signals_df['Position'].diff() # Multiply share price by entry/exit positions and get the cumulatively sum

signals_df['Portfolio Holdings'] = signals_df['Close'] * signals_df['Entry/Exit Position'].cumsum() # Subtract the initial capital by the portfolio holdings to get the amount of liquid cash in the portfolio

signals_df['Portfolio Cash'] = initial_capital - (signals_df['Close'] * signals_df['Entry/Exit Position']).cumsum() # Get the total portfolio value by adding the cash amount by the portfolio holdings (or investments)

signals_df['Portfolio Total'] = signals_df['Portfolio Cash'] + signals_df['Portfolio Holdings'] # Calculate the portfolio daily returns

signals_df['Portfolio Daily Returns'] = signals_df['Portfolio Total'].pct_change() # Calculate the cumulative returns

signals_df['Portfolio Cumulative Returns'] = (1 + signals_df['Portfolio Daily Returns']).cumprod() - 1 # Print the DataFrame

signals_df.tail(10)

Visualize the Exit positions relative to our portfolio:

# Visualize exit position relative to total portfolio value

exit = signals_df[signals_df['Entry/Exit'] == -1.0]['Portfolio Total'].hvplot.scatter(

color='red',

legend=False,

ylabel='Total Portfolio Value',

width=1000,

height=400

) # Visualize entry position relative to total portfolio value

entry = signals_df[signals_df['Entry/Exit'] == 1.0]['Portfolio Total'].hvplot.scatter(

color='green',

legend=False,

ylabel='Total Portfolio Value',

width=1000,

height=400

) # Visualize total portoflio value for the investment

total_portfolio_value = signals_df[['Portfolio Total']].hvplot(

line_color='lightgray',

ylabel='Total Portfolio Value',

width=1000,

height=400

) # Overlay plots

portfolio_entry_exit_plot = total_portfolio_value * entry * exit

portfolio_entry_exit_plot.opts(xaxis=None)

# Prepare DataFrame for metrics

metrics = [

'Annual Return',

'Cumulative Returns',

'Annual Volatility',

'Sharpe Ratio',

'Sortino Ratio'] columns = ['Backtest'] # Initialize the DataFrame with index set to evaluation metrics and column as `Backtest` (just like PyFolio)

portfolio_evaluation_df = pd.DataFrame(index=metrics, columns=columns)

Perform Backtest:

In this section we will look to highlight 🖐🏼 indicators.

1. Cumulative return — return on the investment in total.

on the investment in total. 2. Annual return — return on investment received that year.

on investment received that year. 3. Annual volatility — daily volatility times the square root of 252 trading days.

4. Sharpe ratio — measures the performance of an investment compared to a risk-free asset, after adjusting for its risk.

5. Sortino ratio — differentiates harmful volatility from total overall volatility by using the asset’s standard deviation of negative portfolio returns, downside deviation, instead of the total standard deviation of portfolio returns.

# Calculate cumulative return

portfolio_evaluation_df.loc['Cumulative Returns'] = signals_df['Portfolio Cumulative Returns'][-1] # Calculate annualized return

portfolio_evaluation_df.loc['Annual Return'] = (

signals_df['Portfolio Daily Returns'].mean() * 252

) # Calculate annual volatility portfolio_evaluation_df.loc['Annual Volatility'] = (

signals_df['Portfolio Daily Returns'].std() * np.sqrt(252)

) # Calculate Sharpe Ratio portfolio_evaluation_df.loc['Sharpe Ratio'] = (

signals_df['Portfolio Daily Returns'].mean() * 252) / (

signals_df['Portfolio Daily Returns'].std() * np.sqrt(252)

) # Calculate Downside Return sortino_ratio_df = signals_df[['Portfolio Daily Returns']].copy()

sortino_ratio_df.loc[:,'Downside Returns'] = 0 target = 0

mask = sortino_ratio_df['Portfolio Daily Returns'] < target

sortino_ratio_df.loc[mask, 'Downside Returns'] = sortino_ratio_df['Portfolio Daily Returns']**2

portfolio_evaluation_df # Calculate Sortino Ratio down_stdev = np.sqrt(sortino_ratio_df['Downside Returns'].mean()) * np.sqrt(252)

expected_return = sortino_ratio_df['Portfolio Daily Returns'].mean() * 252

sortino_ratio = expected_return/down_stdev portfolio_evaluation_df.loc['Sortino Ratio'] = sortino_ratio

portfolio_evaluation_df.head()

# Initialize trade evaluation DataFrame with columns. trade_evaluation_df = pd.DataFrame(

columns=[

'Stock',

'Entry Date',

'Exit Date',

'Shares',

'Entry Share Price',

'Exit Share Price',

'Entry Portfolio Holding',

'Exit Portfolio Holding',

'Profit/Loss']

)

Loop through DataFrame, if the ‘Entry / Exit’ trade is 1, set Entry trade metrics.

If `Entry/Exit` is -1, set exit trade metrics and calculate profit.

Append the record to the trade evaluation DataFrame.

# Initialize iterative variables

entry_date = ''

exit_date = ''

entry_portfolio_holding = 0

exit_portfolio_holding = 0

share_size = 0

entry_share_price = 0

exit_share_price = 0

for index, row in signals_df.iterrows():

if row['Entry/Exit'] == 1:

entry_date = index

entry_portfolio_holding = abs(row['Portfolio Holdings'])

share_size = row['Entry/Exit Position']

entry_share_price = row['Close'] elif row['Entry/Exit'] == -1:

exit_date = index

exit_portfolio_holding = abs(row['Close'] * row['Entry/Exit Position'])

exit_share_price = row['Close']

profit_loss = entry_portfolio_holding - exit_portfolio_holding

trade_evaluation_df = trade_evaluation_df.append(

{

'Stock': 'NET',

'Entry Date': entry_date,

'Exit Date': exit_date,

'Shares': share_size,

'Entry Share Price': entry_share_price,

'Exit Share Price': exit_share_price,

'Entry Portfolio Holding': entry_portfolio_holding,

'Exit Portfolio Holding': exit_portfolio_holding,

'Profit/Loss': profit_loss

},

ignore_index=True)

PLOT RESULTS:

price_df = signals_df[['Close', 'SMA50', 'SMA100']]

price_chart = price_df.hvplot.line()

price_chart.opts(title='Cloudflare', xaxis=None)

Final Step: Print Dashboard

portfolio_evaluation_df.reset_index(inplace=True)

portfolio_evaluation_table = portfolio_evaluation_df.hvplot.table()

portfolio_evaluation_table

Thanks for reading!

If you found this article useful, feel welcome to download my personal codes on GitHub. You can also email me directly at scottandersen23@gmail.com and find me on LinkedIn. Interested in learning more about data analytics, data science and machine learning applications? Follow me on Medium.