4

I have a question about the value of the slope in degrees which I have calculated below:

import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import datetime as dt
import numpy as np

df = yf.download('aapl', '2015-01-01', '2021-01-01')
df.rename(columns = {'Adj Close' : 'Adj_close'}, inplace= True)

x1 = pd.Timestamp('2019-01-02')
x2 = df.index[-1]
y1 = df[df.index == x1].Adj_close[0]
y2 = df[df.index == x2].Adj_close[0]

slope = (y2 - y1)/ (x2 - x1).days
angle = round(np.rad2deg(np.arctan2(y2 - y1, (x2 - x1).days)), 1)

fig, ax1 = plt.subplots(figsize= (15, 6))
ax1.grid(True, linestyle= ':')
ax1.set_zorder(1)
ax1.set_frame_on(False)
ax1.plot(df.index, df.Adj_close, c= 'k', lw= 0.8)
ax1.plot([x1, x2], [y1, y2], c= 'k')


ax1.set_xlim(df.index[0], df.index[-1])
plt.show()

It returns the value of the angle of the slope as 7.3 degrees. Which doesnt look true looking at the chart: enter image description here

It looks close to 45 degrees. What is wrong here?

Here is the line for which I need to calculate the angle: enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Raj Nair
  • 85
  • 1
  • 6

1 Answers1

9
  • The implementation in the OP is not the correct way to determine, or plot a linear model. As such, the question about determining the angle to plot the line is bypassed, and a more rigorous approach to plotting the regression line is shown.
  • A regression line can be added by converting the datetime dates to ordinal. The model can be calculated with sklearn, or added to the plot with seaborn.regplot, as show below.
  • Plot the full data with pandas.DataFrame.plot
  • Tested in python 3.8.11, pandas 1.3.2, matplotlib 3.4.3, seaborn 0.11.2, sklearn 0.24.2

Imports and Data

import yfinance as yf
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression

# download the data
df = yf.download('aapl', '2015-01-01', '2021-01-01')

# convert the datetime index to ordinal values, which can be used to plot a regression line
df.index = df.index.map(pd.Timestamp.toordinal)

# display(df.iloc[:5, [4]])
        Adj Close
Date             
735600  24.782110
735603  24.083958
735604  24.086227
735605  24.423975
735606  25.362394

# convert the regression line start date to ordinal
x1 = pd.to_datetime('2019-01-02').toordinal()

# data slice for the regression line
data=df.loc[x1:].reset_index()

Plot a Regression Line with seaborn

  • Using seaborn.regplot no calculations are required to add the regression line to the line plot of the data.
  • Convert the x-axis labels to datetime format
  • Play around with the xticks and labels if you need the endpoints adjusted.
# plot the Adj Close data
ax1 = df.plot(y='Adj Close', c='k', figsize=(15, 6), grid=True, legend=False,
              title='Adjusted Close with Regression Line from 2019-01-02')

# add a regression line
sns.regplot(data=data, x='Date', y='Adj Close', ax=ax1, color='magenta', scatter_kws={'s': 7}, label='Linear Model', scatter=False)

ax1.set_xlim(df.index[0], df.index[-1])

# convert the axis back to datetime
xticks = ax1.get_xticks()
labels = [pd.Timestamp.fromordinal(int(label)).date() for label in xticks]
ax1.set_xticks(xticks)
ax1.set_xticklabels(labels)

ax1.legend()

plt.show()

enter image description here

Calculate the Linear Model

# create the model
model = LinearRegression()

# extract x and y from dataframe data
x = data[['Date']]
y = data[['Adj Close']]

# fit the mode
model.fit(x, y)

# print the slope and intercept if desired
print('intercept:', model.intercept_)
print('slope:', model.coef_)

intercept: [-90078.45713565]
slope: [[0.1222514]]

# calculate y1, given x1
y1 = model.predict(np.array([[x1]]))

print(y1)
array([[28.27904095]])

# calculate y2, given the last date in data
x2 = data.Date.iloc[-1]
y2 = model.predict(np.array([[x2]]))

print(y2)
array([[117.40030862]])

# this can be added to `ax1` with
ax1 = df.plot(y='Adj Close', c='k', figsize=(15, 6), grid=True, legend=False,
              title='Adjusted Close with Regression Line from 2019-01-02')
ax1.plot([x1, x2], [y1[0][0], y2[0][0]], label='Linear Model', c='magenta')
ax1.legend()

enter image description here

Angle of the Slope

  • This is an artifact of the aspect of the axes, which is not equal for x and y. When the aspect is equal, see that the slope is 7.0 deg.
x = x2 - x1
y = y2[0][0] - y1[0][0]
slope = y / x

print(round(slope, 7) == round(model.coef_[0][0], 7))
[out]:
True

angle = round(np.rad2deg(np.arctan2(y, x)), 1)
print(angle)
[out]:
7.0

# given the existing plot
ax1 = df.plot(y='Adj Close', c='k', figsize=(15, 6), grid=True, legend=False,
              title='Adjusted Close with Regression Line from 2019-01-02')
ax1.plot([x1, x2], [y1[0][0], y2[0][0]], label='Linear Model', c='magenta')

# make the aspect equal
ax1.set_aspect('equal', adjustable='box')

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
  • 1
    Ok, got it. Its because of the figure size aspect ratio that it feels like it should have a larger angle. – Raj Nair Sep 14 '21 at 14:16