1

on a dataframe that contains the price of bitcoin, I want to measure the strength of a trend by displaying the angle of the slope of a moving average (calculated over 20 periods) on each row.

A moving average allows you to analyze a time series, removing transient fluctuations in order to highlight longer term trends. To calculate a simple 20-period moving average for trading purposes, we take the last 20 closing prices, add them together and divide the result by 20.

I started by trying to use the linregress function of scipy but I get the exception "len() of unsized object" that I could not solve:

from scipy.stats import linregress
x = df.iloc[-1, 8] # -1:last row, 8: sma20
y = df['sma20']
df['slope_deg'] = df.apply(linregress(x, y))

I then used the atan function of the math module but the result returned is always nan, whatever the row is:

import math
df['sma20'] =  df['Close'].rolling(20).mean()
slope=((df['sma20'][0]-df['sma20'][20])/20)
df['slope_deg'] = math.atan(slope) * 180 / math.pi

... or 45 :

import math
df['sma20'] =  df['Close'].rolling(20).mean()
df['slope_deg'] = math.atan(1) * 180 / math.pi
df

Here is an example of code with the date as an index, the price used to calculate the moving average, and the moving average (over 5 periods for the example):

df= pd.DataFrame({'date':np.tile( pd.date_range('1/1/2011', 
periods=25, freq='D'), 4 ),
'price':(np.random.randn(100).cumsum() + 10),
'sma5':df['price'].rolling(5).mean() 
})

df.head(10)

Can someone help me to create a column that returns the slope of a moving average?

Olivier
  • 69
  • 8
  • Can you provide a few rows of `df`? –  Feb 22 '22 at 17:47
  • Does this answer your question? [Calculate a rolling regression in Pandas and store the slope](https://stackoverflow.com/questions/58499114/calculate-a-rolling-regression-in-pandas-and-store-the-slope) – G. Anderson Feb 22 '22 at 17:47
  • @G.Anderson, unfortunately, the code does not work. Reading the comments I'm not the only one who can't get this code to work. – Olivier Feb 22 '22 at 19:21
  • @richardec, Yes, but I don't know how to send them to you. You have to imagine that the dataframe has only one column: df['Price'] This price changes with each row. By taking the average of the last 20 rows we get the 20 period moving average. Then you have to calculate the angle of the slope of this moving average. Between line 12 and 13 the angle will be x degrees, between line 13 and 14 it will be x degrees ... – Olivier Feb 22 '22 at 19:21
  • See [How to make good pandas examples](https://stackoverflow.com/questions/20109391/how-to-make-good-reproducible-pandas-examples) and [edit] your question to show a sample of the input dataframe – G. Anderson Feb 22 '22 at 19:23

1 Answers1

1

OK, I did the 20 day sma, I am not so sure about the slope part, since you didnt clearly specify what you need.

I am assuming slope values, in degrees, as follows:

arctan( (PriceToday - Price20daysAgo)/ 20 )

Here you have the code:

EDIT 1: simplified 'slope' code and adapted following #Oliver 's suggestion.

import pandas as pd
import yfinance as yf

btc          = yf.download('BTC-USD', period='1Y')
btc['sma20'] = btc.rolling(20).mean()['Adj Close']
btc['slope'] = np.degrees(np.arctan(btc['sma20'].diff()/20))
btc          = btc[['Adj Close','sma20','slope']].dropna()

Output:

btc

               Adj Close           sma20        slope
      Date          
2021-03-15  55907.199219    51764.509570     86.767651
2021-03-16  56804.902344    52119.488086     86.775283
2021-03-17  58870.894531    52708.340234     88.054732
2021-03-18  57858.921875    53284.298242     88.011217
2021-03-19  58346.652344    53892.208203     88.115671
... ... ... ...
2022-02-19  40122.156250    41560.807227     79.715989
2022-02-20  38431.378906    41558.219922     -7.371144
2022-02-21  37075.281250    41474.820312    -76.514600
2022-02-22  38286.027344    41541.472461     73.297321
2022-02-23  38748.464844    41621.165625     75.911862

As you can see, the slope value means little as it is. Thats because the variation in price from a 20 days spam is far greater than 20 units, the value representing the time window you chose to use.

Plotting prices and sma20 vs date.

btc[['Adj Close','sma20']].plot(figsize=(14,7));

enter image description here

P. Pinho
  • 394
  • 1
  • 6
  • Hi @P.Pinho, you are right. I realise that I was not specific enough. You have understood that the aim is to display the angle of a 20 period moving average. But what I want is not the angle over 20 periods, but the angle from one period to another, in order to see if the trend (highlighted by the moving average) is accelerating or slowing down. – Olivier Feb 22 '22 at 20:48
  • One thing I don't understand, if the slope is based on the average of the last 20 periods, the 'slope' column should be filled with nan until the 21st row, right? – Olivier Feb 22 '22 at 21:00
  • Yes, it is. I dropped all `NaN` values from the dataset using `dropna( )`. – P. Pinho Feb 22 '22 at 21:01
  • About the `slope`: can you describe how the calculation should look like? You dont need to code it, just describing it and, maybe, posting an example would be enough. – P. Pinho Feb 22 '22 at 21:04
  • For the example you have coded the sma20 very well. Then regarding the angle, look at the price of the BCT on your graph around 1 Oct 2021. You can see that from that point on the sma20 reverses, and starts to become bullish. What is interesting is to have the angle of this sma20 between the 1st and the 2nd of October, then between the 2nd and the 3rd of October, then between the 3rd and the 4th... The angle increases more and more during the first days of October, then stabilizes and starts to go down towards the end of October and finally the slope reverses towards November 10. – Olivier Feb 22 '22 at 22:14
  • OK, you need the angle for `1d` window, I did it before, it is fairly easy, so, the `Y axis` value would be this difference. The problem lies on the `X axis` value, because we need a scalar greater than 1 (1 day for this matter), or we will keep getting angles with little meaning due to the difference in dimensions. Price changes from one day to another can reach 1k, if you divide it just by 1, it wont provide much information in the end, you get it? – P. Pinho Feb 22 '22 at 22:23
  • Just to be clear: in order to calculate the angle I need a `X value` and a `Y value`. The first one is ok, I have doubts only about the second one. With those two values we can use the `np.arctan` formula to generate the angles. – P. Pinho Feb 22 '22 at 22:27
  • Normally the xvalue corresponds to the current and previous day's date, but I don't understand how the calculation of the date can be useful. And for yvalue it would be necessary to take into account the level of the sma20 of the day and that of the day before, no? – Olivier Feb 23 '22 at 01:51
  • @Olivier, I am not sure if we are on the same page here :). In order to get the `angle` you need at least 2 vectors. The dimension of our `first vector` is the price difference with a 1 day window, this dimension is represented on the `Y axis`, and the `opposite` side of our triangle. The `second vector` should be projected on the `X axis` and, considering we are using a 1 day window, it will always be 1 and thus, our `adjacent` side. – P. Pinho Feb 23 '22 at 11:08
  • Since the `adjacent` side will always be 1 (1 day window) and our `opposite` side, measured not in days, but in USD, will have far bigger values, the angle will almost always be too close to 90 degrees. I am not familiar with this slope metrics and could not find a proper calculation method, but, if you can provide some aditional information to bypass this issue, I will be glad to implement it. – P. Pinho Feb 23 '22 at 11:09
  • I think it's something like this: df["slope"] = (df['sma20']-df['sma20'].shift(periods=1))/20 – Olivier Feb 23 '22 at 15:54
  • OK, I updated the slope formula and made it easier to read. Also I took your last comment into consideration, using the `adjascent` side as a fixed value of 20. You can notice that, due to the larger values the `Y` axis can take, you will still have tons of slopes very close to 90 or -90 degrees. – P. Pinho Feb 23 '22 at 17:38
  • I did some research about this slope indicator. It is just the SMA price difference of today's value and the SMA value N days in the past, usualy 10. It makes more sense than calculating the angle of 2 variables with different dimensions. In this case, you can get slope just doing this: `btc['slope'] = btc['sma20'].diff(10)` – P. Pinho Feb 23 '22 at 17:55
  • Thank you for your dedication to solving the problem @P.Pinho . There is little quality content about trading on the internet. In the trading room the direction (bullish/bearish) of the moving averages is observed, and especially the angle of their slope. Here are two examples of codes (in Basic) that have understood the idea. Their codes do not display the angle of the slope directly, but normalized around 0, which is good information : https://www.prorealcode.com/prorealtime-indicators/moving-average-slope/ , https://www.prorealcode.com/prorealtime-indicators/tma-slope/ – Olivier Feb 23 '22 at 19:09