To begin, let’s first understand the goal of this article, which is to provide the

Xsamir
7 min readDec 20, 2020

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).

https://247sports.com/college/utah/board/59490/contents/watch-usa-u20-vs-switzerland-u20-live-streaming-free-hockey-li-157492156/
https://247sports.com/college/utah/board/59490/contents/hockeystreamsusa-u20-vs-switzerland-u20-live-stream-reddit-157492157/
https://247sports.com/college/utah/board/59490/contents/live-usa-u20-vs-switzerland-u20-live-stream-hockey-2020-onl-157492158/
https://247sports.com/college/utah/board/59490/contents/redditstreams-usa-u20-vs-switzerland-u20-stream-free-157492161/

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:

https://247sports.com/high-school/mississippi/board/football-102607/contents/officiallivestream-usa-u20-vs-switzerland-u20-live-st-157488735/
https://247sports.com/high-school/mississippi/board/football-102607/contents/usa-u20-vs-switzerland-u20-hockey-free-live-stream-122020-157489040/
https://247sports.com/high-school/mississippi/board/football-102607/contents/liveusa-u20-vs-switzerland-u20-live-stream-reddit-free-157489043/
https://247sports.com/high-school/mississippi/board/football-102607/contents/total-sportek-usa-u20-vs-switzerland-u20-live-free-20-157489048/
https://247sports.com/high-school/mississippi/board/football-102607/contents/hockeystreamsusa-u20-vs-switzerland-u20-live-stream-reddit-157489055/
https://247sports.com/high-school/mississippi/board/football-102607/contents/liveusa-u20-vs-switzerland-u20-live-stream-reddit-free-157489087/

# 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 priceexit = 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 priceentry = 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 investmentsecurity_close = signals_df[['Close']].hvplot(
line_color='lightgray',
ylabel='Price in $',
width=1000,
height=400
)# Visualize moving averagesmoving_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.
  • 2. Annual return — return 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 volatilityportfolio_evaluation_df.loc['Annual Volatility'] = (
signals_df['Portfolio Daily Returns'].std() * np.sqrt(252)
)# Calculate Sharpe Ratioportfolio_evaluation_df.loc['Sharpe Ratio'] = (
signals_df['Portfolio Daily Returns'].mean() * 252) / (
signals_df['Portfolio Daily Returns'].std() * np.sqrt(252)
)# Calculate Downside Returnsortino_ratio_df = signals_df[['Portfolio Daily Returns']].copy()
sortino_ratio_df.loc[:,'Downside Returns'] = 0target = 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 Ratiodown_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_stdevportfolio_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.

--

--