Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python and Pandas - Moving Average Crossover

There is a Pandas DataFrame object with some stock data. SMAs are moving averages calculated from previous 45/15 days.

Date      Price   SMA_45      SMA_15
20150127  102.75  113         106
20150128  103.05  100         106
20150129  105.10  112         105
20150130  105.35  111         105
20150202  107.15  111         105
20150203  111.95  110         105
20150204  111.90  110         106

I want to find all dates, when SMA_15 and SMA_45 intersect.

Can it be done efficiently using Pandas or Numpy? How?

EDIT:

What I mean by 'intersection':

The data row, when:

  • long SMA(45) value was bigger than short SMA(15) value for longer than short SMA period(15) and it became smaller.
  • long SMA(45) value was smaller than short SMA(15) value for longer than short SMA period(15) and it became bigger.
like image 280
chilliq Avatar asked Feb 05 '15 13:02

chilliq


People also ask

What is the best moving average crossover combination?

The best longer-term backtested moving average strategy with the expanded range was found to be the 70-day / 210-day SMA crossover signal.

How do you calculate a moving average crossover?

All you have to do is plop on a couple of moving averages on your chart, and wait for a crossover. If the moving averages cross over one another, it could signal that the trend is about to change soon, thereby giving you the chance to get a better entry.

What happens when two moving average cross?

In the statistics of time series, and in particular the stock market technical analysis, a moving-average crossover occurs when, on plotting two moving averages each based on different degrees of smoothing, the traces of these moving averages cross. It does not predict future direction but shows trends.


2 Answers

I'm taking a crossover to mean when the SMA lines -- as functions of time -- intersect, as depicted on this investopedia page.

enter image description here

Since the SMAs represent continuous functions, there is a crossing when, for a given row, (SMA_15 is less than SMA_45) and (the previous SMA_15 is greater than the previous SMA_45) -- or vice versa.

In code, that could be expressed as

previous_15 = df['SMA_15'].shift(1)
previous_45 = df['SMA_45'].shift(1)
crossing = (((df['SMA_15'] <= df['SMA_45']) & (previous_15 >= previous_45))
            | ((df['SMA_15'] >= df['SMA_45']) & (previous_15 <= previous_45)))

If we change your data to

Date      Price   SMA_45      SMA_15
20150127  102.75  113         106
20150128  103.05  100         106
20150129  105.10  112         105
20150130  105.35  111         105
20150202  107.15  111         105
20150203  111.95  110         105
20150204  111.90  110         106

so that there are crossings,

enter image description here

then

import pandas as pd

df = pd.read_table('data', sep='\s+')
previous_15 = df['SMA_15'].shift(1)
previous_45 = df['SMA_45'].shift(1)
crossing = (((df['SMA_15'] <= df['SMA_45']) & (previous_15 >= previous_45))
            | ((df['SMA_15'] >= df['SMA_45']) & (previous_15 <= previous_45)))
crossing_dates = df.loc[crossing, 'Date']
print(crossing_dates)

yields

1    20150128
2    20150129
Name: Date, dtype: int64
like image 61
unutbu Avatar answered Oct 12 '22 11:10

unutbu


The following methods gives the similar results, but takes less time than the previous methods:

df['position'] = df['SMA_15'] > df['SMA_45']
df['pre_position'] = df['position'].shift(1)
df.dropna(inplace=True) # dropping the NaN values
df['crossover'] = np.where(df['position'] == df['pre_position'], False, True)

Time taken for this approach: 2.7 ms ± 310 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Time taken for previous approach: 3.46 ms ± 307 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

like image 44
Jeril Avatar answered Oct 12 '22 11:10

Jeril