├── LICENSE ├── MA_crossover_strategy.ipynb ├── MA_crossover_strategy_script.py └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pratik Nabriya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MA_crossover_strategy_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[50]: 5 | 6 | 7 | # import necessary libraries 8 | 9 | get_ipython().run_line_magic('matplotlib', 'inline') 10 | import numpy as np 11 | import pandas as pd 12 | import matplotlib.pyplot as plt 13 | import datetime 14 | from tabulate import tabulate 15 | import warnings 16 | warnings.filterwarnings('ignore') 17 | import pandas_datareader.data as web 18 | 19 | def MovingAverageCrossStrategy(stock_symbol = 'ULTRACEMCO.NS', start_date = '2018-01-01', end_date = '2020-01-01', 20 | short_window = 20, long_window = 50, moving_avg = 'SMA', display_table = True): 21 | ''' 22 | The function takes the stock symbol, time-duration of analysis, 23 | look-back periods and the moving-average type(SMA or EMA) as input 24 | and returns the respective MA Crossover chart along with the buy/sell signals for the given period. 25 | ''' 26 | # stock_symbol - (str)stock ticker as on Yahoo finance. Eg: 'ULTRACEMCO.NS' 27 | # start_date - (str)start analysis from this date (format: 'YYYY-MM-DD') Eg: '2018-01-01' 28 | # end_date - (str)end analysis on this date (format: 'YYYY-MM-DD') Eg: '2020-01-01' 29 | # short_window - (int)lookback period for short-term moving average. Eg: 5, 10, 20 30 | # long_window - (int)lookback period for long-term moving average. Eg: 50, 100, 200 31 | # moving_avg - (str)the type of moving average to use ('SMA' or 'EMA') 32 | # display_table - (bool)whether to display the date and price table at buy/sell positions(True/False) 33 | 34 | # import the closing price data of the stock for the aforementioned period of time in Pandas dataframe 35 | start = datetime.datetime(*map(int, start_date.split('-'))) 36 | end = datetime.datetime(*map(int, end_date.split('-'))) 37 | stock_df = web.DataReader(stock_symbol, 'yahoo', start = start, end = end)['Close'] 38 | stock_df = pd.DataFrame(stock_df) # convert Series object to dataframe 39 | stock_df.columns = {'Close Price'} # assign new colun name 40 | stock_df.dropna(axis = 0, inplace = True) # remove any null rows 41 | 42 | # column names for long and short moving average columns 43 | short_window_col = str(short_window) + '_' + moving_avg 44 | long_window_col = str(long_window) + '_' + moving_avg 45 | 46 | if moving_avg == 'SMA': 47 | # Create a short simple moving average column 48 | stock_df[short_window_col] = stock_df['Close Price'].rolling(window = short_window, min_periods = 1).mean() 49 | 50 | # Create a long simple moving average column 51 | stock_df[long_window_col] = stock_df['Close Price'].rolling(window = long_window, min_periods = 1).mean() 52 | 53 | elif moving_avg == 'EMA': 54 | # Create short exponential moving average column 55 | stock_df[short_window_col] = stock_df['Close Price'].ewm(span = short_window, adjust = False).mean() 56 | 57 | # Create a long exponential moving average column 58 | stock_df[long_window_col] = stock_df['Close Price'].ewm(span = long_window, adjust = False).mean() 59 | 60 | # create a new column 'Signal' such that if faster moving average is greater than slower moving average 61 | # then set Signal as 1 else 0. 62 | stock_df['Signal'] = 0.0 63 | stock_df['Signal'] = np.where(stock_df[short_window_col] > stock_df[long_window_col], 1.0, 0.0) 64 | 65 | # create a new column 'Position' which is a day-to-day difference of the 'Signal' column. 66 | stock_df['Position'] = stock_df['Signal'].diff() 67 | 68 | # plot close price, short-term and long-term moving averages 69 | plt.figure(figsize = (20,10)) 70 | plt.tick_params(axis = 'both', labelsize = 14) 71 | stock_df['Close Price'].plot(color = 'k', lw = 1, label = 'Close Price') 72 | stock_df[short_window_col].plot(color = 'b', lw = 1, label = short_window_col) 73 | stock_df[long_window_col].plot(color = 'g', lw = 1, label = long_window_col) 74 | 75 | # plot 'buy' signals 76 | plt.plot(stock_df[stock_df['Position'] == 1].index, 77 | stock_df[short_window_col][stock_df['Position'] == 1], 78 | '^', markersize = 15, color = 'g', alpha = 0.7, label = 'buy') 79 | 80 | # plot 'sell' signals 81 | plt.plot(stock_df[stock_df['Position'] == -1].index, 82 | stock_df[short_window_col][stock_df['Position'] == -1], 83 | 'v', markersize = 15, color = 'r', alpha = 0.7, label = 'sell') 84 | plt.ylabel('Price in ₹', fontsize = 16 ) 85 | plt.xlabel('Date', fontsize = 16 ) 86 | plt.title(str(stock_symbol) + ' - ' + str(moving_avg) + ' Crossover', fontsize = 20) 87 | plt.legend() 88 | plt.grid() 89 | plt.show() 90 | 91 | if display_table == True: 92 | df_pos = stock_df[(stock_df['Position'] == 1) | (stock_df['Position'] == -1)] 93 | df_pos['Position'] = df_pos['Position'].apply(lambda x: 'Buy' if x == 1 else 'Sell') 94 | print(tabulate(df_pos, headers = 'keys', tablefmt = 'psql')) 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moving Average Crossover Trading Strategy with Python 2 | A Python script to generate buy/sell signals using Simple moving average(SMA) and Exponential moving average(EMA) Crossover Strategy. 3 | 4 | ## Authors 5 | 6 | Pratik Nabriya (pratik.nabriya@gmail.com) 7 | 8 | ## License 9 | 10 | MIT License 11 | --------------------------------------------------------------------------------